Compare commits

..

777 Commits
v1.2.0 ... nsis

Author SHA1 Message Date
Florian Bruhin
179e583af5 NSIS: Remove installation dir if it exists
If we don't do this, when a newer version is installed on top of an older one,
old files which have been deleted persist. In the case of v1.3.3 -> v1.4.0,
this means qutebrowser won't start because of a leftover sip installation which
confuses PyQt 5.11.

This is still potentially dangerous as the user could use e.g. C:\ as
installation path, but we have that issue when uninstalling anyways.
See http://nsis.sourceforge.net/Uninstall_only_installed_files
2018-07-10 14:57:40 +02:00
Florian Bruhin
b9e3d3cab9 Add workaround for chrome-extension:// URLs
Fixes #4049
2018-07-09 12:29:35 +02:00
Florian Bruhin
274b66ec46 Strip trailing newlines from pastebin URL 2018-07-08 22:09:56 +02:00
Florian Bruhin
ae32b79d54 Add exam comments to contributing docs 2018-07-04 15:37:50 +02:00
Florian Bruhin
e80e695a56 Add a mkvenv-pypi-old environment
Fixes #4038
See #3662
2018-07-04 14:08:04 +02:00
Florian Bruhin
0a31e19eda Handle download errors when the reply is already gone
Fixes #1270
2018-07-03 17:16:02 +02:00
Florian Bruhin
0f037fb415 Release v1.4.0 2018-07-03 15:44:44 +02:00
Florian Bruhin
85cc1e4f84 Update changelog for v1.4.0 2018-07-03 15:44:25 +02:00
Florian Bruhin
42a3622906 Ignore a new Qt 5.11 lowlevel message 2018-07-03 15:38:29 +02:00
Florian Bruhin
a0f36c5cbf Skip JS test which is too flaky 2018-07-03 14:15:08 +02:00
Florian Bruhin
dfafab4cff Update changelog 2018-07-03 13:52:50 +02:00
Florian Bruhin
8cf22c85e0 Merge remote-tracking branch 'origin/pr/4019' 2018-07-03 13:52:24 +02:00
Florian Bruhin
4d1e56a8c6 Ignore "Lost UI shared context" error happening on AppVeyor 2018-07-03 13:39:34 +02:00
Florian Bruhin
8c11c516b4 tox: Always allow setting python via envvar 2018-07-03 13:32:53 +02:00
Florian Bruhin
857288b283 Try importing QtWebEngine from AppVeyor 2018-07-03 13:28:18 +02:00
Florian Bruhin
24e93fe023 Remove AppVeyor debugging 2018-07-03 13:27:31 +02:00
Florian Bruhin
8115e109db Update default env in tox.ini 2018-07-03 13:27:18 +02:00
Florian Bruhin
6abe8f2c97 Ignore QtNetwork warning on macOS 2018-07-03 13:22:05 +02:00
Florian Bruhin
7a9183d0b7 Remove 32-bit makensis call
I forgot to remove this in 0af8eec73a
2018-07-03 13:17:15 +02:00
Florian Bruhin
ba362de2c0 Turn on AppVeyor debugging 2018-07-03 13:15:07 +02:00
Florian Bruhin
d03b03f7cb Use Python 3.7 on macOS 2018-07-03 13:13:58 +02:00
Florian Bruhin
7e8c741937 Set compiled=False for BDD Qt comparisons
We often check for bugs and not APIs there.
2018-07-03 13:10:15 +02:00
Florian Bruhin
641f7eb3c5 Don't import test_file on Windows
See https://github.com/pytest-dev/pytest/issues/3650
2018-07-03 13:08:54 +02:00
Florian Bruhin
38791a2386 Only import gen_versioninfo on Windows 2018-07-03 11:38:48 +02:00
Florian Bruhin
b8fb5d4590 Make sure we're using Python 3.6 in release instructions 2018-07-03 11:37:26 +02:00
Florian Bruhin
f65f3db747 appveyor: Make sure we get the Python we want 2018-07-03 11:17:27 +02:00
Florian Bruhin
5add2cd930 Update changelog 2018-07-03 11:15:59 +02:00
Florian Bruhin
0af8eec73a build_release: Remove 32-bit support for Windows
QtWebEngine isn't available for 32-bit anymore:
https://blog.qt.io/blog/2018/05/22/qt-5-11-released/ (comments)
2018-07-03 11:08:56 +02:00
Florian Bruhin
49be92e047 Use 64-bit Python on AppVeyor
QtWebEngine isn't available in the 32-bit build.
2018-07-03 11:06:31 +02:00
Florian Bruhin
ec0bbe67f8 travis: Test with Python 3.7
See https://github.com/travis-ci/travis-ci/issues/9069
2018-07-03 11:00:33 +02:00
Florian Bruhin
e6e28c846f Use PyYAML 3.13b1
It seems to be finished except for a missing wheel we don't need, and it makes
things work on Python 3.7
2018-07-03 10:30:59 +02:00
Florian Bruhin
efc4eb9069 Use PyYAML from git for requirements-tests-git 2018-07-03 10:30:30 +02:00
Florian Bruhin
26d6cf8ef6 Stabilize URL escaping test 2018-07-03 10:29:28 +02:00
Florian Bruhin
05531ddcf0 brew: Update instead of install libyaml 2018-07-03 10:27:38 +02:00
Florian Bruhin
1c8917b10e Remove old brew_install line 2018-07-03 10:27:10 +02:00
pyup-bot
c33c019075 Update py from 1.5.3 to 1.5.4
(cherry picked from commit fa9698564af184e6e7cdddc96c9906442031197e)
2018-07-02 23:26:14 +02:00
pyup-bot
564cd3732b Update py from 1.5.3 to 1.5.4
(cherry picked from commit 2006ad70e88cf5fffec75aa465f968ebba722758)
2018-07-02 23:26:12 +02:00
pyup-bot
1d91a3ac66 Update hypothesis from 3.61.0 to 3.65.0
(cherry picked from commit 98ecc7e77b7dd21037224a4f3b51d0cb654610e6)
2018-07-02 23:26:09 +02:00
Florian Bruhin
f6f713bbfe Skip key forwarding tests on Qt 5.11.1
See #4036
2018-07-02 23:19:57 +02:00
Florian Bruhin
6ca11ed95b Adjust SSL test for Qt 5.11 2018-07-02 23:15:04 +02:00
Florian Bruhin
41303ecfcf Make sure temporary dir exists
This seems to be enforced with Qt 5.12
See #4025
2018-07-02 22:32:59 +02:00
Florian Bruhin
9a14574c9f Skip invalid links on any Qt 5.11 version
See #3661
2018-07-02 22:32:59 +02:00
Florian Bruhin
d861c097b1 Support new dead keys added in Qt 5.11 properly
See https://codereview.qt-project.org/#/c/207231/
2018-07-02 22:32:59 +02:00
Florian Bruhin
77fe2e1c85 Fix test_set_wrong_backend 2018-07-02 22:32:59 +02:00
Florian Bruhin
ad19833e34 Revert "Add workaround for PyQt 5.11 headerDataChanged bug"
PyQt 5.11.1 has already been released, with the bug fixed.

This reverts commit 291763a55643342a6f977ce2a12dcc6f4badbe8a.
2018-07-02 22:32:59 +02:00
Florian Bruhin
eca08f064b Add workaround for PyQt 5.11 headerDataChanged bug
https://www.riverbankcomputing.com/pipermail/pyqt/2018-June/040445.html
2018-07-02 22:32:59 +02:00
Florian Bruhin
c3455d9082 Add a wrapper around sip
Starting with PyQt 5.11, the sip module now is bundled with PyQt as PyQt.sip.
Having a qutebrowser.qt also helps with #3625, see #995
2018-07-02 22:32:59 +02:00
Florian Bruhin
f7ae7e7d40 Update for PyQt 5.11 2018-07-02 22:32:59 +02:00
Florian Bruhin
e857400c2c Add missing str() 2018-06-28 13:26:45 +02:00
Florian Bruhin
dbd4ce48e6 Downgrade and filter PyYAML 4.1
This reverts commit 96defc5dc2.

With PyYAML 4.1 we don't have C extensions on Travis CI:
https://github.com/yaml/pyyaml/issues/179
https://github.com/yaml/pyyaml/issues/182

Unfortunately, cython isn't in the APT whitelist either:
https://github.com/travis-ci/apt-source-whitelist/issues/37
2018-06-28 11:46:03 +02:00
Florian Bruhin
a7af5195d1 Set title when showing PDF.js error page
Fixes #3894
2018-06-28 11:22:44 +02:00
Florian Bruhin
e9c78b29ed Ignore Python 3.7 collections.abc warning
Related issues/PRs:
https://github.com/yaml/pyyaml/pull/181
https://github.com/pypa/setuptools/issues/1401
https://github.com/pallets/markupsafe/pull/98
https://github.com/yaml/pyyaml/pull/181
https://github.com/pallets/jinja/pull/867
2018-06-27 16:01:21 +02:00
Florian Bruhin
96defc5dc2 Update PyYAML to 4.1 2018-06-27 15:53:58 +02:00
Florian Bruhin
a804300dc0 Add missing tests for spell.init() 2018-06-26 16:56:22 +02:00
Florian Bruhin
2f612aa6df Update comment 2018-06-26 15:54:56 +02:00
Florian Bruhin
aed964d9f5 Update changelog 2018-06-26 10:41:21 +02:00
Florian Bruhin
8519aa940f Decorate slots properly 2018-06-26 10:40:13 +02:00
Florian Bruhin
ea4ee6f00b Use the url_changed signal in the tab API 2018-06-26 10:39:33 +02:00
Florian Bruhin
1536843f33 Only get greasemonkey object once 2018-06-26 10:39:04 +02:00
Florian Bruhin
61da5d0c7c Merge remote-tracking branch 'origin/pr/4017' 2018-06-26 10:37:17 +02:00
Florian Bruhin
3312c221c4 Stabilize ssl_strict test 2018-06-26 10:26:17 +02:00
Florian Bruhin
85a9f6a08a Fix lint 2018-06-26 10:23:48 +02:00
Jimmy
c43d173197 greasemonkey: s/userscripts/greasemonkey_scripts/
No need to confuse developers as well as users.
2018-06-26 16:42:31 +12:00
Jimmy
ee2c765859 greasemonkey: check _widget is not deleted
Just for good luck.

No crash has been reported here but it is a common pattern for functions
called from signals.
2018-06-26 16:42:31 +12:00
Jimmy
6f1232e621 greasemonkey: move 5.7.1 injection method into _WebEngineScripts
Moves the 5.8 check to `_WebEngineScripts.init()`.

Changes `_inject_userscripts` to allow for the two code paths. With
5.7.1 we need to specify the injection point and not clear all scripts
for each call, since we have to call it three times.

Change the 5.8+ hook to call a new method which passes all the scripts
into `_inject_userscripts` so that doesn't have to have a fallback
conditional inside it because thats an inversion of responsibility!

Pulling the remove scripts part into a seperate function and making it
the callers responsibilty to call that first would tidy it up a little
more but meh.

I was worried about just doing `_widget.page().urlChanged.connect()`
once at tab init, where before it was connected at page init, because I
was under the impression that the child page can be replaced at any
time, eg when navigating to a new origin. But under manual testing I
didn't see that at all. Maybe I was mistaken or maybe that only started
in a later Qt version.
2018-06-26 16:42:31 +12:00
Jimmy
324966cfe7 greasemonkey: also support qute-js-world on 5.7.1
A straight copy from webengintab.

Yes I know I shouldn't be importing a private thing from webenginetab,
I'm working on refactoring now.
2018-06-26 15:00:35 +12:00
Jimmy
521268a1f7 Update comment. 2018-06-26 15:00:35 +12:00
Jimmy
54ca9b34e5 greasemonkey: enable running in isolated js worlds
QtWebEngine (via chromium) has the ability to run injected scripts in
isolated "worlds". What is isolated is just the javascript environment,
so variables and functions defined by the page and the script won't
clobber each other, or be able to interact (including variables saved to
the global `window` object). The DOM is still accessible from "isolated"
scripts.

This is NOT a security measure. You cannot put untrusted scripts in one
of these isolated worlds and expect it to not be able to do whatever
page js can do, it is just for namespacing convenience. See
https://stackoverflow.com/questions/9515704/insert-code-into-the-page-context-using-a-content-script
for some examples of how to inject scripts into the page scope using DOM
elements.

Now you can specify the world ID in a `@qute-js-world` directive like:

```
// ==UserScript==
// @name         Do thing
// @match        *://some.site/*
// @qute-js-world 1234
// ==/UserScript==
document.body.innerHTML = "<strong>overwritten</strong>"
```

The QtWebEngine docs say worldid is a `quint32` so you can put whatever
number (positive, whole, real) you want there. I have chosen to allow
the `qutebrowser.utils.usertypes` enum as aliases for IDs that are
predefined in
`qutebrowser.browser.webengine.webenginetab._JS_WORLD_MAP`. So you can
pass `main`, `application`, `user` or `jseval` in there too. `main` (0)
is the default one and is the only one in which JS disabled when
`content.javascript.enabled` is set to `false`. All others are still
enabled.

I'm not sure whether using any of those already-named worlds makes
sense, apart from `main`. We could stop people from using them I
suppose. Another option is to allow people to pass in `*` as a value to
have scripts put into their own little worlds, probably backed by a
counter in the GreaseMonkeyManager class.

Chrome docs: https://developer.chrome.com/extensions/content_scripts#execution-environment
Webengine docs: https://doc.qt.io/qt-5/qwebenginescript.html#details
2018-06-26 15:00:35 +12:00
Florian Bruhin
1bc3d444b6 Stabilize escaping URLs test 2018-06-25 23:02:50 +02:00
Florian Bruhin
876aa5a9b1 Fix lint 2018-06-25 22:51:55 +02:00
Florian Bruhin
87778277e0 Fix SSL error page tests 2018-06-25 22:51:48 +02:00
Florian Bruhin
81b3ef937e Move handling of certificate errors to webenginetab 2018-06-25 21:04:32 +02:00
Florian Bruhin
8a4bba11ed Disable certificate workaround on Qt >= 5.9
Fixes #4020
2018-06-25 20:35:48 +02:00
Florian Bruhin
3f923b41e0 Revert "Update pyqt5 from 5.10.1 to 5.11.1"
This reverts commit 1f19db0785.
2018-06-25 20:08:50 +02:00
Florian Bruhin
158ccd7d54 Revert "Update pyqt5 from 5.10 to 5.11.1"
This reverts commit e1bc5389a2.
2018-06-25 20:08:42 +02:00
Florian Bruhin
228ca732d5 Merge remote-tracking branch 'origin/pyup-scheduled-update-2018-06-25' 2018-06-25 20:08:24 +02:00
pyup-bot
b3790f7a7e Update pytest from 3.6.1 to 3.6.2 2018-06-25 19:21:17 +02:00
pyup-bot
d6554a131e Update hypothesis from 3.59.1 to 3.61.0 2018-06-25 19:21:15 +02:00
pyup-bot
1f19db0785 Update pyqt5 from 5.10.1 to 5.11.1 2018-06-25 19:21:14 +02:00
pyup-bot
e1bc5389a2 Update pyqt5 from 5.10 to 5.11.1 2018-06-25 19:21:12 +02:00
Jay Kamat
da0a6305df Fix crash when tab is closed after a per-domain forced reload 2018-06-25 12:45:17 -04:00
Florian Bruhin
6c9e23af4a eslint: Turn off max-lines-per-function 2018-06-25 08:14:02 +02:00
Florian Bruhin
13f765a000 Fix changelog formatting 2018-06-24 22:33:46 +02:00
Florian Bruhin
fc19262eaa Fix test_shared.py 2018-06-24 22:31:27 +02:00
Florian Bruhin
f2f481d991 Support URL patterns for permissions and ssl_strict
See #3636
2018-06-24 21:38:37 +02:00
Florian Bruhin
f5e69b2174 Show inspector after creating it 2018-06-24 19:57:52 +02:00
Florian Bruhin
e6e844b039 Support URL patterns for content.headers settings
See #3636
2018-06-24 19:54:24 +02:00
Florian Bruhin
a02c25dfb1 Don't escape URLs for qute://history
We only use the URL to set a 'href' attribute, which does not need escaping.

See #4011
Fixes #4012
2018-06-23 14:27:07 +02:00
Florian Bruhin
d2254ca48b Release v1.3.3
(cherry picked from commit ad9b50601c)
2018-06-21 23:32:56 +02:00
Florian Bruhin
66fc3a30dd Update changelog 2018-06-21 23:30:27 +02:00
Florian Bruhin
0864ad4069 Fix shadowing of 'html' name 2018-06-21 22:28:27 +02:00
Florian Bruhin
9a5439e5d0 Re-add waiting for QQuickWidget
Apparently this is still needed on some PyQt versions.
2018-06-21 22:22:04 +02:00
Florian Bruhin
7a7e04a054 Move fix to v1.3.3 in changelog 2018-06-21 21:42:44 +02:00
Florian Bruhin
d961eab1d2 Update changelog for v1.3.3 2018-06-21 21:42:08 +02:00
Florian Bruhin
5a7869f2fe Fix XSS issue on qute://history
Fixes #4011
2018-06-21 21:20:19 +02:00
Florian Bruhin
62d8b5b574 Don't depend on PyQt5.QtQuickWidgets to get RWHV
Some distributions (at least FreeBSD) don't package that module, so let's not
rely on it.
2018-06-21 17:14:29 +02:00
Florian Bruhin
c87757a913 Revert "Properly add QtQuickWidgets dependency"
Looks like FreeBSD doesn't have QtQuickWidgets packaged at all, so let's do the
same without requiring it...

This reverts commit e5405f0ae9.
2018-06-21 16:35:29 +02:00
Florian Bruhin
9f5ca475c9 Don't try to set focus if prev_focus is None 2018-06-21 01:44:15 +02:00
Florian Bruhin
e7a300865c Fix lint 2018-06-21 01:43:09 +02:00
Florian Bruhin
4887385bdd Fix test_dictionary_dir 2018-06-21 01:40:36 +02:00
Florian Bruhin
1000a1eac2 Merge remote-tracking branch 'origin/pr/4005' 2018-06-21 01:01:27 +02:00
Florian Bruhin
e5405f0ae9 Properly add QtQuickWidgets dependency 2018-06-21 00:21:52 +02:00
pyup-bot
a73a778b9d Update pytest-qt from 2.4.0 to 2.4.1 2018-06-18 19:11:20 +02:00
pyup-bot
7c4eaa80b0 Update hypothesis from 3.57.0 to 3.59.1 2018-06-18 19:11:19 +02:00
pyup-bot
c3b76d1d01 Update cheroot from 6.3.1 to 6.3.2 2018-06-18 19:11:17 +02:00
pyup-bot
fa0e8c1b51 Update requests from 2.18.4 to 2.19.1 2018-06-18 19:11:16 +02:00
pyup-bot
da8f76d082 Update requests from 2.18.4 to 2.19.1 2018-06-18 19:11:14 +02:00
pyup-bot
91c0aae05b Update requests from 2.18.4 to 2.19.1 2018-06-18 19:11:13 +02:00
Florian Bruhin
3399f2df96 Always clear searches between page loads
Looks like this wasn't properly fixed in Qt for some reason.
Fixes #3693
See #2728 and bef372e5f5
2018-06-17 21:03:44 +02:00
Florian Bruhin
2029f52fdc Show cause when ~/.netrc can't be read 2018-06-17 20:53:29 +02:00
Florian Bruhin
663d1a4d2f Read dictionaries from /usr/share/qt on Qt >= 5.10
Fixes #3759
Supersedes #3762
See #2939, #4003
2018-06-17 20:27:52 +02:00
Florian Bruhin
7b7e0c93f5 Update Chromium version in changelog 2018-06-16 11:54:53 +02:00
Florian Bruhin
e2ef39e872 Add Comment to .desktop file 2018-06-14 23:29:16 +02:00
Florian Bruhin
7654467f36 Remove unused import 2018-06-14 17:43:20 +02:00
Florian Bruhin
b1b06fcb43 Fix restore test 2018-06-14 17:42:33 +02:00
Florian Bruhin
746c2986f0 Fix test_stylesheet on Qt 5.11 2018-06-14 16:42:24 +02:00
Florian Bruhin
f4386fa9ea Update changelog 2018-06-14 16:35:16 +02:00
Florian Bruhin
07cf2f5b60 Unconditionally restore mode after prompt 2018-06-14 16:09:30 +02:00
Florian Bruhin
4dddc07753 Make sure modeman.enter(KeyMode.normal) does something sensible 2018-06-14 16:09:26 +02:00
Florian Bruhin
11fce30ed0 Stabilize mode_on_change tests 2018-06-14 15:56:48 +02:00
Florian Bruhin
1335fccba1 Merge remote-tracking branch 'origin/pr/3590' into tab-mode 2018-06-14 15:40:58 +02:00
Florian Bruhin
e4e982c0a7 Remove unused variable 2018-06-14 14:58:07 +02:00
Florian Bruhin
7592186181 Update changelog 2018-06-14 14:58:07 +02:00
Florian Bruhin
868cd115be Remove old focus handling code 2018-06-14 14:58:07 +02:00
Florian Bruhin
cec63ea449 Merge remote-tracking branch 'origin/pr/3906' 2018-06-14 14:49:30 +02:00
Jay Kamat
e5b6552568 Clean up and simplify some logic 2018-06-13 15:37:32 -07:00
Florian Bruhin
a0adee55c9 Quit hard on ignored exceptions
We can't realistically shut down cleanly because we most likely haven't init'ed
properly yet.

Fixes #3993
2018-06-13 21:13:39 +02:00
Florian Bruhin
389c1d11a0 Merge remote-tracking branch 'origin/pr/3990' 2018-06-13 20:28:08 +02:00
Florian Bruhin
b67733b781 Use ImportError for winreg import
This also satisfies pylint.
2018-06-13 20:15:16 +02:00
Florian Bruhin
bcc4bd22ee Merge remote-tracking branch 'origin/pr/3991' 2018-06-13 20:15:06 +02:00
Florian Bruhin
cb2881e0d7 Use info loglevel for :debug-cache-stats
Closes #3994
2018-06-13 19:10:01 +02:00
Florian Bruhin
1541088e76 Use -webkit-filter on old Qt 2018-06-12 17:01:34 +02:00
Florian Bruhin
5435609552 Update changelog 2018-06-12 16:50:32 +02:00
Florian Bruhin
c37134861e Use ES6 template strings 2018-06-12 16:49:54 +02:00
Florian Bruhin
772654bcae Use calculated background color for newer Qt versions 2018-06-12 16:46:03 +02:00
Florian Bruhin
68a7387b6b Merge remote-tracking branch 'origin/pr/3940' 2018-06-12 16:32:05 +02:00
Florian Bruhin
fd4ae2fabe Fix test_set_wrong_backend 2018-06-12 14:17:50 +02:00
Florian Bruhin
a1a5885367 Set parents for tab sub-objects 2018-06-12 14:09:06 +02:00
Florian Bruhin
a6db700886 Fix HTML5 fullscreen 2018-06-12 13:44:47 +02:00
Florian Bruhin
d5dcec4320 Add changelog entry for Qt update 2018-06-12 13:44:41 +02:00
Slackhead
088c7b235d fix for qt 5.7.1 2018-06-12 11:22:41 +01:00
Florian Bruhin
6faff11243 Improve error messages with backend conditionals 2018-06-12 11:10:03 +02:00
Florian Bruhin
2f97a597a4 travis: Run coverage with PyQt 5.10 2018-06-12 10:53:34 +02:00
Florian Bruhin
e066f83a7c Make configinit tests run with all setups 2018-06-12 10:52:22 +02:00
Florian Bruhin
5b5657b0d5 Make sure debug_flags is available for unit tests 2018-06-12 10:38:19 +02:00
Florian Bruhin
3b0c8e46a3 Add an initial cookie filter for Qt 5.11
See #3010
2018-06-12 09:36:05 +02:00
Slackhead
35a1e118f8 combine style nodes 2018-06-12 04:25:13 +01:00
Florian Bruhin
093f07f552 Add content.canvas_reading setting
See #2377
Closes #2235
2018-06-11 23:28:04 +02:00
bitraid
b61c99687d build_release.py: reformat 2018-06-12 00:27:43 +03:00
Florian Bruhin
ad7e080827 Fix lint 2018-06-11 21:47:38 +02:00
Florian Bruhin
67c67db230 Handle multiple visible children when finding lost focusProxy
When we click a QTBUG link (to open in a new tab) from Qt's codereview, we get
two RWHV objects which both are visible.

Experimenting with .setEnabled(False) it looks like it's (hopefully always...)
the last one which is the one to use.
2018-06-11 21:43:27 +02:00
Florian Bruhin
c328d54ebe Add a lost-focusproxy debug flag 2018-06-11 21:27:08 +02:00
Florian Bruhin
a6b314ae91 Don't connect Qt 5.11 signals on PyQt 5.10
Apparently the signal attributes already exist with PyQt 5.10 (*sigh*) but PyQt
doesn't know what to do with the arguments, causing this to happen:

TypeError: unable to convert a C++ 'QWebEngineRegisterProtocolHandlerRequest'
instance to a Python object
2018-06-11 21:21:41 +02:00
Florian Bruhin
d42934af08 Turn off FocusOnNavigationEnabled on Qt 5.9
This way we get the same behavior with Qt 5.9 and 5.10 at least, leaving only
5.7 if we pretend that 5.8 never existed.
2018-06-11 20:28:00 +02:00
Florian Bruhin
e7659cea63 Update changelog 2018-06-11 20:24:00 +02:00
Florian Bruhin
141afff0c6 Add a content.desktop_capture setting
See #2939
2018-06-11 20:18:57 +02:00
bitraid
ddfbe255e7 build_release.py: Get python path from registry on Windows 2018-06-11 20:45:35 +03:00
Florian Bruhin
69abc9a1a1 Add a content.webrtc_public_interfaces_only option
See #3010
Fixes #2163
2018-06-11 19:44:45 +02:00
Florian Bruhin
3bf89bcea4 Add content.autoplay option
See #3010
Closes #1643
2018-06-11 19:32:34 +02:00
Florian Bruhin
c5b7ed350e Make it possible to provide a converter for websettings 2018-06-11 19:14:31 +02:00
pyup-bot
a5447e0e94 Update vulture from 0.26 to 0.27 2018-06-11 19:06:46 +02:00
pyup-bot
4d026bde6f Update vulture from 0.26 to 0.27 2018-06-11 19:06:39 +02:00
pyup-bot
6943677a90 Update pytest from 3.6.0 to 3.6.1 2018-06-11 19:06:37 +02:00
pyup-bot
37d3cf1e9c Update pylint from 1.9.1 to 1.9.2 2018-06-11 19:06:36 +02:00
pyup-bot
21dba1ffda Update astroid from 1.6.4 to 1.6.5 2018-06-11 19:06:34 +02:00
pyup-bot
82a0c94573 Update idna from 2.6 to 2.7 2018-06-11 19:06:33 +02:00
pyup-bot
72f0820204 Update idna from 2.6 to 2.7 2018-06-11 19:06:31 +02:00
pyup-bot
33ba47797d Update idna from 2.6 to 2.7 2018-06-11 19:06:30 +02:00
Florian Bruhin
b7c1d7fe37 Fix broken tests 2018-06-11 18:38:22 +02:00
Florian Bruhin
263d298449 Remove the content.developer_extras setting 2018-06-11 18:09:24 +02:00
Florian Bruhin
e5fbb9f68a Remove pyqtSlot annotations for new types 2018-06-11 18:09:24 +02:00
Florian Bruhin
54011782c8 webenginetab: Move scripts to separate object 2018-06-11 18:09:24 +02:00
Florian Bruhin
b3749df009 webenginetab: Move permissions to separate object 2018-06-11 18:09:18 +02:00
Florian Bruhin
4186577928 Add support for navigator.registerProtocolHandler
See #3010
2018-06-11 18:09:18 +02:00
Florian Bruhin
c020160f75 Add support for navigator.webkitPersistentStorage.requestQuota
See #3010
2018-06-11 18:09:18 +02:00
Florian Bruhin
4ea957b68b Allow Qt 5.11 for backends in configdata.yml 2018-06-11 18:09:18 +02:00
Florian Bruhin
05e73872b6 Add blocking=True to shared.feature_permission 2018-06-11 16:37:53 +02:00
Florian Bruhin
be95d6f505 Remove moved audio API 2018-06-11 16:07:32 +02:00
Florian Bruhin
b954fd4b15 Move _on_feature_permission_requested to WebEngineTab 2018-06-11 16:06:58 +02:00
Florian Bruhin
6e90465cdd Update docs 2018-06-11 15:38:01 +02:00
Florian Bruhin
98c82859b5 Skip "History with view-source URL" on QtWebKit 2018-06-11 15:36:48 +02:00
Florian Bruhin
8964845c18 Remove unused import 2018-06-11 15:30:01 +02:00
Florian Bruhin
c7f57bc111 Tell pylint to shut up about a shadowed argument 2018-06-11 15:29:35 +02:00
Florian Bruhin
09a5ef8140 pylint: Ignore ImportError for windows-specific import 2018-06-11 15:22:34 +02:00
Florian Bruhin
9f5a5a12ba Break long line 2018-06-11 15:22:12 +02:00
Florian Bruhin
f052eff038 Stop using view-source: scheme for Pygments-highlighted URLs
Doing so causes QtWebEngine to load its own view-source: page even if we supply
custom data.

Instead we pass the original page's URL (to not regress #2948).

This partially reverts #3521 and reintroduces TabData.viewing_source.

However, on QtWebEngine we can still ":view-source --pygments" and then
":view-source" (with or without "--pygments") again, because the bit gets
cleaned in _on_load_started.

See #3654.
2018-06-11 15:18:00 +02:00
Florian Bruhin
6e23a6b958 Merge remote-tracking branch 'origin/pr/3654' 2018-06-11 14:56:32 +02:00
Florian Bruhin
cbd9e36e0f Fix typo 2018-06-11 14:13:11 +02:00
Florian Bruhin
12a7aaed95 flake8: Don't require docstrings for stuff under tests/ 2018-06-11 14:12:47 +02:00
Florian Bruhin
2b0e89ab14 Update changelog 2018-06-11 14:01:42 +02:00
Florian Bruhin
b63e06561d Only consider visible render widgets for lost focusProxy
Otherwise, when commenting out the focusProxy way above, and using "foo !npm"
with DuckDuckGo, we get two children (one visible, one invisible).
2018-06-11 14:00:02 +02:00
Florian Bruhin
49b6a512c2 Add missing docstring 2018-06-11 13:17:14 +02:00
Florian Bruhin
8949afc2bf Reformat and document gen_versioninfo 2018-06-11 13:13:58 +02:00
Florian Bruhin
e6dd05a2c7 Merge remote-tracking branch 'origin/pr/3857' 2018-06-11 13:08:29 +02:00
Florian Bruhin
b5d1614c48 Add audio attribute to FakeWebTab 2018-06-11 12:41:55 +02:00
Florian Bruhin
bfae6b357a Update changelog 2018-06-11 12:39:47 +02:00
Florian Bruhin
4dcba2343d Add expected_names to test arguments 2018-06-11 12:37:48 +02:00
Florian Bruhin
e36b6b13e0 Edit docstring :D 2018-06-11 12:35:49 +02:00
Florian Bruhin
2934f4a1ca Merge remote-tracking branch 'origin/pr/3973' 2018-06-11 12:34:18 +02:00
Florian Bruhin
8376278961 Update docs 2018-06-11 12:14:24 +02:00
Florian Bruhin
a1fcdbcfd1 Move muted/audible API to own ".audio" object 2018-06-11 12:12:37 +02:00
Florian Bruhin
1c8d470bd7 Merge remote-tracking branch 'origin/pr/3908' 2018-06-11 12:06:09 +02:00
Jimmy
b7929ef747 Add tests for suppressing debug log records.
For the LogFilter tests I just copied some of the examples that were
already being used and reversed them. To do that without even more
duplication I had to add another parameter to the test.
2018-06-11 21:56:59 +12:00
Florian Bruhin
c08c740793 Update changelog 2018-06-11 11:45:31 +02:00
Florian Bruhin
649c9f37cd Fix import 2018-06-11 11:35:37 +02:00
Florian Bruhin
3999802c71 Add missing 'return' 2018-06-11 11:35:35 +02:00
Florian Bruhin
cf8dbd8bfd Move pressing Enter into a method 2018-06-11 11:35:16 +02:00
Florian Bruhin
7f69920158 Merge remote-tracking branch 'origin/pr/3947' 2018-06-11 11:29:14 +02:00
Jimmy
addd2e74ce Allow negating categories in --logfilter
Sometimes I want to see all the logs _except_ for the sql stuff and
"marked cookies as dirty". with this you should be able to pass
`--logfilter \!sql,save`.
2018-06-11 21:26:54 +12:00
Florian Bruhin
f034abe6a1 Handle showing/hiding of the inspector properly 2018-06-11 11:07:14 +02:00
Florian Bruhin
1ba2e3e24b Implement Qt 5.11 devtools support
See #3010
2018-06-11 11:07:14 +02:00
Jay Kamat
315ed519ee Use ctrl-enter in all cases 2018-06-10 16:27:56 -07:00
Florian Bruhin
20763a87c0 Update changelog 2018-06-10 17:34:17 +02:00
Florian Bruhin
c4add62301 Merge remote-tracking branch 'origin/pr/3825' 2018-06-10 17:30:44 +02:00
Florian Bruhin
50fa7743ba Only use OS-specific line separator for hints 2018-06-10 17:21:31 +02:00
Florian Bruhin
3bffe1ccf8 Release v1.3.2 2018-06-10 15:59:50 +02:00
Florian Bruhin
bd26498f6e Update changelog for v1.3.2
[ci skip]
2018-06-10 15:57:10 +02:00
Florian Bruhin
829a6855a3 Make sure test_env_vars restores variables correctly
Otherwise, we'd get a segfault inside Qt in test_stylesheet, since
88f2873a79.
2018-06-10 15:49:24 +02:00
Florian Bruhin
ca8d3903e3 Improve configinit tests 2018-06-10 10:49:26 +02:00
Jay Kamat
11d8df0e3e Simplify logic and resolve style issues 2018-06-09 16:45:42 -07:00
bitraid
00199ae60e Corrections to gen_versioninfo.py script
- Use main() function
- Call utils.change_cwd()
- Import info text
- Don't use fstrings
2018-06-10 02:24:47 +03:00
Jay Kamat
de127497a2 Press enter to follow links instead of using js
This codepath may trigger a crash which was fixed by
0e75f3272d.
However, this commit does not make it more likely to happen, and this
patch was backported into arch (at least).

In the future, we may be able to use <enter> on qtwebkit with js,
without triggering this crash
2018-06-09 15:42:44 -07:00
Florian Bruhin
292d92d02e Update changelog 2018-06-09 23:01:29 +02:00
Florian Bruhin
c9fddfe458 Fix lint 2018-06-09 23:00:57 +02:00
Florian Bruhin
7fdeeb25b7 Merge remote-tracking branch 'origin/pr/3793' 2018-06-09 22:59:52 +02:00
Florian Bruhin
6022fd2b26 Document how to run a qutebrowser development build
Fixes #3795
Supersedes #3798
2018-06-09 21:56:56 +02:00
Florian Bruhin
1d19a2ebbb Fix typo 2018-06-09 21:54:32 +02:00
Florian Bruhin
7d11c3aa43 Update changelog 2018-06-09 21:32:09 +02:00
Florian Bruhin
96739d0013 Merge remote-tracking branch 'origin/pr/3864' 2018-06-09 21:29:15 +02:00
Florian Bruhin
dcb1191f93 Remove the deprecated :tab-detach 2018-06-09 21:25:09 +02:00
Florian Bruhin
cf3c200fcb Update changelog 2018-06-09 20:50:35 +02:00
Florian Bruhin
a663c857cc Merge remote-tracking branch 'origin/pr/3926' 2018-06-09 20:50:17 +02:00
Slackhead
c3771ca2b2 double quoted and className corrected 2018-06-09 19:45:48 +01:00
Florian Bruhin
c73cb7efad Update changelog 2018-06-09 20:40:57 +02:00
Florian Bruhin
22f05e3ca5 Merge remote-tracking branch 'origin/pr/3929' 2018-06-09 20:40:32 +02:00
Florian Bruhin
9161aa6e17 Merge remote-tracking branch 'origin/pr/3930' 2018-06-09 20:36:28 +02:00
Florian Bruhin
313cc42d8a Link to code.qt.io instead of GitHub for _chromium_version 2018-06-09 20:26:12 +02:00
Florian Bruhin
d486a056e7 Update docs 2018-06-09 20:20:46 +02:00
Florian Bruhin
7ce7176475 Fix lint 2018-06-09 20:20:08 +02:00
Florian Bruhin
1b48b67443 Rename reporter.escape_quits to input.escape_quits_reporter 2018-06-09 20:19:32 +02:00
Florian Bruhin
486a92a710 Merge remote-tracking branch 'origin/pr/3944' 2018-06-09 20:18:25 +02:00
Florian Bruhin
ec88c15390 Fix waiting for initial focus object with Qt 5.11 workarounds
This was broken in d32d541ac0 because now
apparently PyQt knows it's a QQuickWidget.
2018-06-09 20:05:26 +02:00
Florian Bruhin
40b1be6925 Fix test_disable_gpu with QtWebKit 2018-06-09 19:59:11 +02:00
Florian Bruhin
41b5fca9b7 Add additional test 2018-06-09 19:57:22 +02:00
Florian Bruhin
b0325e17b7 Add a YamlConfig._migrate_bool helper 2018-06-09 19:57:11 +02:00
Florian Bruhin
7949335a2b Remove unused import 2018-06-09 19:51:05 +02:00
Florian Bruhin
6e954a1596 Allow to force software rendering with wayland on Qt 5.11
Closes #2932 (hopefully for the last time)
2018-06-09 16:50:48 +02:00
Florian Bruhin
88f2873a79 Allow more values for the qt.force_software_rendering setting 2018-06-09 16:21:10 +02:00
Jay Kamat
5d38d28fee Fix incorrect usage of tab_id 2018-06-08 20:59:25 -07:00
Florian Bruhin
d32d541ac0 Further simplify getting focusProxy children 2018-06-08 17:10:47 +02:00
Florian Bruhin
cc497bf2ea Improve RWHV typecheck for focusProxy 2018-06-08 15:13:48 +02:00
Florian Bruhin
9725d9ce33 Exclude QMenu when trying to find the missing focusProxy 2018-06-08 14:55:34 +02:00
Florian Bruhin
1531961aeb Show children in focusProxy workaround 2018-06-08 14:40:59 +02:00
Florian Bruhin
4e6fede40d Update changelog 2018-06-08 08:56:17 +02:00
Florian Bruhin
91b4106dcf Fix check for reloads on Qt < 5.11
This was broken in 6ccd69dad2
2018-06-08 08:53:10 +02:00
Florian Bruhin
a13618fe2a Merge branch 'pyup-scheduled-update-2018-06-04' 2018-06-07 22:57:48 +02:00
Florian Bruhin
8bf7cb539a Mark modal window test as flaky 2018-06-07 22:57:16 +02:00
Florian Bruhin
d96a98a058 Mention other colorschemes as well 2018-06-07 20:31:27 +02:00
Florian Bruhin
5e6002fcdc Merge remote-tracking branch 'origin/pr/3963' 2018-06-07 20:25:24 +02:00
Florian Bruhin
11b957f24b tests: Adjust getting markers for pytest 3.6 2018-06-07 19:29:01 +02:00
Florian Bruhin
4614ad5063 Remove unused import 2018-06-07 18:01:29 +02:00
Florian Bruhin
6e48648033 Update changelog 2018-06-07 17:52:46 +02:00
Florian Bruhin
d541634a7c Avoid hacks for changing per-domain settings on Qt 5.11.1 2018-06-07 17:51:21 +02:00
Florian Bruhin
b1506274c5 Implement a better workaround for chrome-error:// URLs
It looks like chrome-error://chromewebdata/ triggers another invalid scheme
load which is why the endless loop happens. When we install a custom scheme
handler for chrome-error:// we can at least show an error page.
2018-06-07 16:03:25 +02:00
Florian Bruhin
c6a1d729f4 Update the list of chrome:// URLs
See #3010
2018-06-07 15:42:58 +02:00
Florian Bruhin
596041c40e Go back to using an invalid scheme for invalid_link.html
Otherwise, this breaks the tests on Qt 5.10
2018-06-07 15:30:28 +02:00
Florian Bruhin
9b795c5257 Update changelog 2018-06-07 14:35:09 +02:00
Florian Bruhin
89f4333df1 Make sure external schemes are clickable via hints
This issue was probably introduced in 545539f28d
- with JavaScript, we can't "click" on an external link.

There might be a better solution using
QWebEngineSettings::setUnknownUrlSchemePolicy(QWebEngineSettings::AllowAllUnknownUrlSchemes)
temporarily when using hints with PyQt 5.11.

Fixes #2833
2018-06-07 14:33:49 +02:00
Florian Bruhin
59e5a2a6f1 Update changelog
See #3661
2018-06-07 13:49:39 +02:00
Florian Bruhin
0c0d204fd4 Add a workaround for chrome-error:// loops on Qt 5.11
See #3661
2018-06-07 13:49:33 +02:00
Florian Bruhin
999513d5d8 Skip invalid link tests on Qt 5.11
Qt 5.11 just loads about:blank and doesn't let us catch this in
acceptNavigationRequest, but the same happens in Chromium as well.

See #3661
2018-06-07 13:49:28 +02:00
Florian Bruhin
d059197bc9 Use a valid scheme in invalid_link.html
This is to avoid triggering QTBUG-63378 which fails differently with a custom
scheme.

See #3661
2018-06-07 13:49:22 +02:00
Florian Bruhin
0e9159e8e8 Revert "Fix Qt 5.11 issues with clicking invalid links"
This reverts commit 1956590df84a72c7f9a516e805d01529291fccf8.

Turns out the actual issue wasn't due to *invalid* links - it's with links
which have an unknown scheme.

There's still a change in behavior between Qt 5.10 and 5.11 though: Invalid
links are apparently not passed to acceptNavigationRequest (sometimes?) so we
don't show an error message. Instead, we just load about:blank. However,
Chromium does that too and we can't handle a real click easily, so let's just
ignore that one.

See #3661
2018-06-07 13:49:13 +02:00
Florian Bruhin
3d53d0d2c5 Fix Qt 5.11 issues with clicking invalid links
See #3661
2018-06-07 13:48:04 +02:00
Florian Bruhin
eb6478dd3e Reenable Qt 5.11 tests on Travis
Fixes #3661
2018-06-07 13:48:04 +02:00
Florian Bruhin
8cc3804119 Don't run test with failed download on Qt 5.11
Looks like we can't use an <a> tag with download-attribute to trigger a failed
download in the test on Qt 5.11...

See #2298, #3661
2018-06-06 21:12:23 +02:00
Florian Bruhin
513591348c Update changelog 2018-06-06 20:26:12 +02:00
Florian Bruhin
456fdc55cc Only set PseudoLayout with Qt 5.11 2018-06-06 20:26:12 +02:00
Florian Bruhin
7e31897dcc Fix lint 2018-06-06 20:26:12 +02:00
Florian Bruhin
5147fc832c Handle resizing via PseudoLayout
This fixes the scenario where we just get a grey view when opening a link in a
tab from DuckDuckGo.
2018-06-06 20:26:12 +02:00
Florian Bruhin
ec6c5ebb69 Try harder to get the RenderWidgetHostViewQt 2018-06-06 20:26:12 +02:00
Florian Bruhin
34d054e8a5 Revert "Use WrapperLayout instead of PseudoLayout"
This reverts commit 6cc920472ee4170b257a0b588687b175162e83df.

Since self._widget can go stale in the layout, we need to somehow solve this differently...
2018-06-06 20:26:12 +02:00
Florian Bruhin
e0213e7447 Use WrapperLayout instead of PseudoLayout 2018-06-06 20:26:12 +02:00
Florian Bruhin
cee88cd7ca Initial proof of concept for pseudo layout
Fixes #3920 - hopefully properly this time...
2018-06-06 20:26:12 +02:00
Florian Bruhin
5635639ed4 view-in-mpv: Remove --yes-playlist argument for youtube-dl
Reverts #3057
Fixes #3977
2018-06-06 09:59:03 +02:00
Florian Bruhin
982a42d453 Unbind Ctrl-Shift-Tab by default 2018-06-05 11:33:34 +02:00
pyup-bot
dc203d46de Update pytest-rerunfailures from 4.0 to 4.1 2018-06-04 19:00:21 +02:00
pyup-bot
62e86e1482 Update pytest-qt from 2.3.2 to 2.4.0 2018-06-04 19:00:19 +02:00
pyup-bot
9f9a5d576b Update pytest from 3.5.1 to 3.6.0 2018-06-04 19:00:18 +02:00
pyup-bot
46329b0f03 Update parse from 1.8.2 to 1.8.4 2018-06-04 19:00:16 +02:00
pyup-bot
454d026fdc Update more-itertools from 4.1.0 to 4.2.0 2018-06-04 19:00:14 +02:00
pyup-bot
e30e08a04e Update pyroma from 2.3 to 2.3.1 2018-06-04 19:00:13 +02:00
Slackhead
7858bb97d1 Change default bg colour to #000 2018-05-31 06:33:48 +01:00
Slackhead
8c1080de97 Change filter value to 85% to really darken it 2018-05-31 05:58:05 +01:00
Slackhead
d37b2713cf Change min-width to 0.2em 2018-05-31 05:45:43 +01:00
Slackhead
41a092cd80 Tweak style/animation 2018-05-31 04:50:41 +01:00
Slackhead
0fcbc209bb Add animation stopper/starter 2018-05-31 04:50:41 +01:00
Slackhead
1578a4836c Add fallback colours if inherit fails 2018-05-31 04:50:41 +01:00
Slackhead
f34d1b6ce1 Remove inherit styles 2018-05-31 04:50:41 +01:00
Slackhead
4cc2b919fa Change caret style 2018-05-31 04:50:41 +01:00
Florian Bruhin
5e99da5459 Remove broken shields.io badges
[ci skip]
2018-05-30 11:59:34 +02:00
theova
5543e75abd Add base16-qutebrowser 2018-05-29 20:00:49 +02:00
Florian Bruhin
6fc3546923 travis: Allow Archlinux to fail for now
See #3661
2018-05-29 13:19:05 +02:00
Florian Bruhin
52c44d3da6 setup.py: Set long_description_content_type
Otherwise, Warehouse (new PyPI) refuses the upload...
2018-05-29 11:23:10 +02:00
Florian Bruhin
b3c95c1668 Release v1.3.1
(cherry picked from commit ac29c579ff)
2018-05-29 11:15:15 +02:00
Florian Bruhin
f78410ef2b Update changelog for v1.3.1 2018-05-29 11:09:06 +02:00
Jay Kamat
28fce9a7cb Add support for opening background tabs in 5.11
Adding more workarounds to 442bdd4a4f, *sigh*
2018-05-26 13:52:57 -07:00
Jay Kamat
3392d82f50 Merge branch 'master' of https://github.com/qutebrowser/qutebrowser into jay/tab-bg-focus 2018-05-26 13:52:12 -07:00
Jay Kamat
c33a887b2d Add support for following tab selected elements to :follow-selected 2018-05-25 12:39:36 -07:00
Jay Kamat
cd56b97e7d Add an option to disable escape in the report dialog 2018-05-24 11:50:27 -07:00
Florian Bruhin
12e0edbcd0 Fix lint 2018-05-24 08:47:31 +02:00
Jay Kamat
40e391e199 Prevent closing :report dialog when pressing <escape> 2018-05-23 21:25:43 -07:00
Florian Bruhin
17cfb0d39c Add some more logging for #3920 2018-05-23 21:31:18 +02:00
Florian Bruhin
7162f15348 Use functools instead of a lambda for QTimer
It reads nicer, and this is also speculative fix for #3896 as PyQt5 is
hopefully better at disconnecting partial-objects from dead objects than it is
with lambdas.
2018-05-23 09:44:44 +02:00
Jay Kamat
74ea696a5c Merge branch 'master' of https://github.com/qutebrowser/qutebrowser into jay/tab-bg-focus 2018-05-22 23:17:52 -07:00
Jay Kamat
2f76ef1e53 Revert "Only apply workaround for QTBUG-68076 on non-background tabs"
This reverts commit 77c8575a88.
2018-05-22 23:17:43 -07:00
Florian Bruhin
26e37739e2 Fix typo 2018-05-22 23:07:31 +02:00
Florian Bruhin
e895ee8bdc Update changelog 2018-05-22 12:28:55 +02:00
Florian Bruhin
29ad252278 Handle ² keypress correctly
Turns out str.isdigit() also handles ² as a digit, but int('²') causes a
ValueError.

Here we use `string.digits` instead, which is '0123456789'.

Fixes #3743
2018-05-22 12:25:45 +02:00
Florian Bruhin
db1287cb82 Update changelog 2018-05-22 09:40:54 +02:00
Florian Bruhin
71ad8bdb47 Properly work around Qt 5.11 keyboard focus issues
Please let this be the last attempt... :D

Fixes #3939
Supersedes #3921
Reverts ae295a7f65
See #3661

This should not regress #3872. Might affect #3834 in some way.
2018-05-22 09:36:14 +02:00
Florian Bruhin
bc37af7c95 Document QtWebEngine internals
[ci skip]
2018-05-22 07:42:23 +02:00
Jay Kamat
3bf02aa62f Clarify install command for Ubuntu 17.04 2018-05-21 17:29:23 -07:00
Florian Bruhin
ae06c2cdb7 Merge pull request #3938 from qutebrowser/pyup-scheduled-update-2018-05-21
Scheduled weekly dependency update for week 20
2018-05-21 21:31:03 +02:00
Florian Bruhin
89f019b710 Remove unneeded "except ... as e:" assignments 2018-05-21 20:03:18 +02:00
pyup-bot
228c86c799 Update virtualenv from 15.2.0 to 16.0.0 2018-05-21 18:53:26 +02:00
pyup-bot
c551e29f4c Update pytest-qt from 2.3.1 to 2.3.2 2018-05-21 18:53:25 +02:00
pyup-bot
7596dbdd43 Update pytest-instafail from 0.3.0 to 0.4.0 2018-05-21 18:53:23 +02:00
pyup-bot
616371a3c1 Update hypothesis from 3.56.9 to 3.57.0 2018-05-21 18:53:22 +02:00
pyup-bot
725a133b9a Update cheroot from 6.2.4 to 6.3.1 2018-05-21 18:53:20 +02:00
pyup-bot
f60c42b7fb Update pylint from 1.8.4 to 1.9.1 2018-05-21 18:53:19 +02:00
pyup-bot
9dc9ec3435 Update astroid from 1.6.3 to 1.6.4 2018-05-21 18:53:17 +02:00
pyup-bot
e7a779ef90 Update setuptools from 39.1.0 to 39.2.0 2018-05-21 18:53:16 +02:00
pyup-bot
5ec3f1949a Update pyflakes from 1.6.0 to 2.0.0 2018-05-21 18:53:14 +02:00
pyup-bot
5df4128e8d Update pep8-naming from 0.6.1 to 0.7.0 2018-05-21 18:53:13 +02:00
Jimmy
47446baf29 s/obj/target/
Would a bit of consistency in variable names be too much to ask?
2018-05-21 21:23:38 +12:00
Jimmy
749b29e599 tests: Don't pretend to be using webkit if unavailable
Since `objects.backend` was being set to usertypes.Backend.QtWebKit by
default some feature detection code was calling
`version.qWebKitVersion()` which was failing because the import of
`PyQt5.QtWebKit` was failing in version.

This should not change behavior where both backends are available on if
any tests fail because they are expecting their environment to say they
are on webkit when they either aren't actually using any webkit features
or all mocked webkit features then they should probably be changed to
patch `objects.backend` or not depend on it.
2018-05-21 20:59:27 +12:00
Wayne Cheng
9a85796ac3 CR changes 2018-05-20 20:30:24 -04:00
Florian Bruhin
a31542269a Remove old files from MANIFEST.in 2018-05-20 16:12:33 +02:00
Wayne Cheng
36ddf3a328 Code review changes 2018-05-20 05:35:48 -04:00
Jimmy
d162e01422 Greasemonkey: remove extra window scope IIFE
Also change the block scoped `window` declaration to be const because it
seemed like a good idea. The real window seems to just silently ignore
attempts to overwrite it.
2018-05-20 18:42:40 +12:00
Jimmy
6573a4d616 Tell pylint to shut its fat mouth.
I just want to return something I can refer to the attributes of via dot
syntax without having to pointlessly write the names both when I declare
the data class and when I assign the variables.

Such a stupid warning.
2018-05-20 18:42:40 +12:00
Jimmy
b0d1a137da Greasemonkey: Don't attempt scope isolation on webkit
Since the JSCore used by WebKit 602.1 doesn't fully support Proxy and I
can't think of a way to provide isolation otherwise just revert to the
old behaviour in that case. I am checking for the specific WebKit
version because I'm pretty sure that version just happened to be
released when Proxy support was only partially done, any later release
will presumably have a newer JSCore where it works.

There I changed the indentation of a block in the jinja template which
will have inflated the diff.

I added mocking of `objects.backend` to the `webview` and
`webenginewebview` fixtures, I am pretty sure they are mutually
exclusive so don't expect any issues from that.

Because of the feature detection being at template compile time I had to
tweak the test setup to be done via a fixture instead of the setupClass
functionality that I was using before.
2018-05-20 18:42:40 +12:00
Jimmy
13249329f7 Greasemonkey: skip window scoping test on webkit
The implementation of Proxy in JSCore used by current QtWebkit (webkit
602.1) doesn't support the `set()` handler for whatever reason. So
instead of testing for a specific behaviour that we can't ensure on that
version let's just skip the tests and handle user complaints with
sympathy.
2018-05-20 18:42:40 +12:00
Jimmy
19242b6cb2 Greasemonkey: fix scoping function decleration.
Apparently making a function like `function foo() {};` in block scope is
illegal. It should be named via assignment.

Switched to an IIFE anyway because it doesn't need a name.
2018-05-20 18:42:40 +12:00
Jimmy
c7a9792b67 Greasemonkey: Add test for window scoping refinements.
Adds a test to codify what I think greasemonkey scripts expect from
their scope chains. Particularly that they can:

1. access the global `window` object
2. access all of the attributes of the global window object as global
   objects themselves
3. see any changes the page made to the global scope
4. write to attributes of `window` and have those attributes, and changes
   to existing attributes, accessable via global scope
5. do number 4 without breaking the pages expectations, that is what
   `unsafeWindow` is for

There are some other points about greasemonkey scripts' environment that
I believe to be true but am not testing in this change:

* changes a page makes to `window` _after_ a greasemonkey script is
  injected will still be visible to the script if it cares to check and
  it hasn't already shadowed them
* said changes will not overwrite changes that the greasemonkey script
  has made.
2018-05-20 18:42:40 +12:00
Jimmy
23bfe6daa2 Greasemonkey: fix window proxy for new attributes
Previously scripts were failing to find attributes that they assigned to
window and then tried to use from the global scope. Eg

    window.newthing = function() {...};
    newthing(...);  // newthing is not defined error

This wasn't the case for things that already existed in the global scope
and were just being overwritten.

This change just overrides the `Proxy.has()` function which seems to fix
it. Probably the `while` implementation was failing to pick up new
attributes because of the lack.

I also tweaked some comments and variable names and const-ness to be a
little more production ready.
2018-05-20 18:42:40 +12:00
Jimmy
ab50ad735b Greasemonkey: isolates scripts' writes to window
Since the global namespace of javascript in the browser is accessible
there where issues with scripts clobbering things that the page expected
to be attributes of window pages clobbering things that userscripts
saved to `window`. The latter was occuring with OneeChan. OneeChan was
setting `window.$` to a function that took a css selector and the 4chan
catalog script was setting `window.$` to some object. Since OneeChan was
set to run at document-start this broke OneeChan, switching it to
document-end broke scripts on 4chan.

I used OneeChan and 4chan-X on 4chan as the test case for this and
TamperMonkey as a guide for what is the correct way to handle scoping. I
didn't manage to pick apart just how TamperMonkey does what it does (I
think it might just be the environment that Chrome provides extensions
actually) but I got close to the same behaviour.

TamperMonkey provides a `window` object that appears to be what the
global window looked like before the webpage modified it. The global
scope though does have the pages modifications accessible. If the script
assigns something to an attribute `window` it can see that attribute in
the global scope. This implementation differs from that one in that, to
the scipt, `window` and the global scope always look the same, and that
is the same as the global scope looks in the environment provided by
TamperMonkey.

I am using the ES6 `Proxy` feature to allow the `window` object to look
like the actual (unsafe) one while allowing writing to it that doesn't
clobber the unsafe one. I am then using the ES4 `with` function to make
attributes of that window (proxy) object visible in the scope chain.
There may be other ways to do this without using `with` by using nested
functions and setting `this` creatively. There are notes around
alleging `with` to be various states of uncool[1].

I also ran into an issue where a userscript calling
`window.addEventListener(...)` would fail with `TypeError: Illegal
Execution` which is apparently due to `this` not being set correctly. I
looked at the functions which threw that error and those that didn't and
am using whether they have a `prototype` attribute or not to tell
whether I need to bind them with `window` as `this`. I am not sure how
correct that is but it worked for all the cases I ran into.

[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
2018-05-20 18:42:40 +12:00
Wayne Cheng
7130f863cb Create lastpass implementation based off qute-pass code 2018-05-19 15:53:08 -04:00
Sebastian Heinlein
547edb3c4e appdata: install the metainfo using the Makefile
Furthermore add some information about the latest releases
and remove a few trailing whitespaces.
2018-05-19 13:57:26 +02:00
Jay Kamat
77c8575a88 Only apply workaround for QTBUG-68076 on non-background tabs
Previously, we would focus webviews even if they were in the
background to work around https://bugreports.qt.io/browse/QTBUG-68076.
This adjusts that to only occur when needed.
2018-05-18 10:46:53 -07:00
Joakim Särehag
4966a70709 Merge from main repo master, resolved conflict in webenginetab.py 2018-05-18 13:59:25 +02:00
Florian Bruhin
046c0a7ea2 Merge remote-tracking branch 'origin/pr/3914' 2018-05-17 17:53:47 +02:00
Florian Bruhin
d208c48532 Update changelog 2018-05-17 17:53:15 +02:00
Florian Bruhin
dacca0d2ed Remove double settings 2018-05-17 15:54:26 +02:00
Florian Bruhin
6ccd69dad2 Fix reload for JavaScript support on Qt 5.11 2018-05-17 15:54:17 +02:00
Florian Bruhin
d8569a36f9 Update changelog 2018-05-17 14:24:29 +02:00
Florian Bruhin
44d26f77a5 Add workaround for the "split page" Qt bug (QTBUG-68224)
Fixes #3920
2018-05-17 14:21:14 +02:00
Florian Bruhin
6eadff4a10 Add Python executable to :version 2018-05-14 22:13:15 +02:00
pyup-bot
6784356d9d Update hypothesis from 3.56.5 to 3.56.9 2018-05-14 18:48:20 +02:00
pyup-bot
b0105b0157 Update python-dateutil from 2.7.2 to 2.7.3 2018-05-14 18:48:18 +02:00
pyup-bot
553a91c012 Update python-dateutil from 2.7.2 to 2.7.3 2018-05-14 18:48:17 +02:00
pyup-bot
b0f83615b6 Update wheel from 0.31.0 to 0.31.1 2018-05-14 18:48:15 +02:00
pyup-bot
0c674c7110 Update flake8-builtins from 1.3.1 to 1.4.1 2018-05-14 18:48:14 +02:00
Jay Kamat
71d55e9213 Refocus command prompt after a new tab is opened when in command mode 2018-05-11 08:49:13 -07:00
Jay Kamat
95093b82c9 Refocus webview after spawning a background tab 2018-05-10 10:15:01 -07:00
Jay Kamat
538b4fafdb Revert "Restore focus to webview after clicking tab-bg"
This reverts commit 262cea8e75.
2018-05-10 09:30:28 -07:00
Jay Kamat
563afb277d Fix style issues 2018-05-10 08:04:48 -07:00
Jay Kamat
4bb048cb6a Fix mocked tests 2018-05-09 21:41:59 -07:00
Jay Kamat
ae04da7b78 Upgrade indicator to show recentlyAudible status 2018-05-09 18:57:53 -07:00
Jay Kamat
0642695c7d Update docs 2018-05-09 18:33:52 -07:00
Jay Kamat
7a297e2e3f Add support for muting tabs 2018-05-09 18:28:05 -07:00
Jay Kamat
bc9f178a08 Add test for tab-bg focus 2018-05-09 15:34:01 -07:00
Jay Kamat
262cea8e75 Restore focus to webview after clicking tab-bg 2018-05-09 11:05:07 -07:00
Joakim Särehag
dc7f39514d pull request #3864, reverted webview changes, fixed regression 2018-05-09 18:28:52 +02:00
Joakim Särehag
72c2962908 removed trailing whitespace 2018-05-09 15:52:22 +02:00
Joakim Särehag
cbf95d76bd Merge branch 'master' of https://github.com/qutebrowser/qutebrowser 2018-05-09 14:37:27 +02:00
Joakim Särehag
0e756d2f68 changes to pull request 3864, window.print for webengine 2018-05-09 14:36:03 +02:00
Florian Bruhin
48c44e1b4d Fix and update docs 2018-05-08 15:22:49 +02:00
Florian Bruhin
20efaeff19 Improve configuration docs 2018-05-08 14:57:56 +02:00
Florian Bruhin
d194a8ddb0 Merge pull request #3898 from qutebrowser/pyup-scheduled-update-2018-05-07
Scheduled weekly dependency update for week 18
2018-05-08 14:21:23 +02:00
Florian Bruhin
c8d539a4c6 Update changelog 2018-05-08 11:46:07 +02:00
Florian Bruhin
8531f89ca3 Merge remote-tracking branch 'origin/pr/3789' 2018-05-08 11:45:20 +02:00
Florian Bruhin
4fee9b9637 Update changelog 2018-05-08 11:44:06 +02:00
Florian Bruhin
89a1c43b4c Merge remote-tracking branch 'origin/pr/3790' 2018-05-08 11:43:30 +02:00
Florian Bruhin
a39858c436 Update changelog 2018-05-08 11:41:14 +02:00
Florian Bruhin
b718c8b43a Merge remote-tracking branch 'origin/pr/3899' 2018-05-08 11:40:43 +02:00
Florian Bruhin
b28bfbe8f5 Update docs 2018-05-08 11:39:35 +02:00
Florian Bruhin
70c7a42a63 Merge remote-tracking branch 'origin/pr/3900' 2018-05-08 11:37:52 +02:00
Florian Bruhin
648b1219a2 Filter flake8-builtins 1.4.0
See https://github.com/gforcada/flake8-builtins/issues/36
2018-05-08 11:34:17 +02:00
Florian Bruhin
d005088b6b Add missing fake_args fixture 2018-05-08 11:26:45 +02:00
Jay Kamat
906da44d70 Simplify visited link clearing by looping over connections 2018-05-07 14:10:20 -07:00
Jay Kamat
ca48f9f100 Clear url from visitedLinks if a single url is deleted 2018-05-07 13:48:38 -07:00
Jay Kamat
d0b2360745 Clear visited links db when running :history-clear on webengine 2018-05-07 13:35:41 -07:00
Pol Van Aubel
f1b481dcc7 Short options for --debug and --debug-flags 2018-05-07 22:35:32 +02:00
Pol Van Aubel
431a52da6c Short options for --basedir and --temp-basedir 2018-05-07 22:10:08 +02:00
pyup-bot
b109fc569f Update pytest-mock from 1.9.0 to 1.10.0 2018-05-07 18:39:22 +02:00
pyup-bot
fcd98443f9 Update flask from 1.0.1 to 1.0.2 2018-05-07 18:39:21 +02:00
pyup-bot
e455d8c13a Update pep8-naming from 0.5.0 to 0.6.1 2018-05-07 18:39:19 +02:00
pyup-bot
d4e5a2eb7f Update flake8-builtins from 1.3.1 to 1.4.0 2018-05-07 18:39:18 +02:00
pyup-bot
a4424f2863 Update attrs from 17.4.0 to 18.1.0 2018-05-07 18:39:17 +02:00
pyup-bot
9e33f1a275 Update attrs from 17.4.0 to 18.1.0 2018-05-07 18:39:15 +02:00
pyup-bot
470c392ee0 Update attrs from 17.4.0 to 18.1.0 2018-05-07 18:39:13 +02:00
Florian Bruhin
b9fc068af5 Add a log-requests debug-flag 2018-05-07 10:23:56 +02:00
Florian Bruhin
7762017f00 Release v1.3.0 2018-05-03 19:25:04 +02:00
Florian Bruhin
2ba40acf74 Update changelog for v1.3.0 2018-05-03 19:25:04 +02:00
Florian Bruhin
388c155ebb Add test for #3753 2018-05-03 18:02:20 +02:00
Florian Bruhin
adf2f9860d Disable spellcheck when it's unneeded.
Fixes #3753
2018-05-03 17:58:56 +02:00
Florian Bruhin
f528a5dd21 Update changelog 2018-05-03 17:56:24 +02:00
Florian Bruhin
f0f1a4a1d1 Merge remote-tracking branch 'origin/pr/3702' 2018-05-03 17:56:03 +02:00
Florian Bruhin
ae295a7f65 Call setFocus() when navigating
See #3661:
https://github.com/qutebrowser/qutebrowser/issues/3661#issuecomment-386308601
This doesn't seem to fully fix this, but at least the top four failed tests
there...

This should not regress #3872. Might affect #3834 in some way.
2018-05-03 17:43:19 +02:00
Jay Kamat
2663feea2f Add :hint inputs --first as a default binding to gi 2018-05-03 11:37:31 -04:00
Florian Bruhin
68794cc2e2 Revert "Always set FocusOnNavigationEnabled"
This reverts commit fa41af63b6.

See #3661
Fixes #3872
2018-05-03 15:25:41 +02:00
Florian Bruhin
acdf0a1c60 Call _handle_search before leaving the mode 2018-05-03 15:23:45 +02:00
Florian Bruhin
626abd3c83 Fix lint 2018-05-03 15:18:21 +02:00
Florian Bruhin
979b7cfaba Add a stop-gap solution for AssertionError when retrying downloads
See #3847
2018-05-03 14:52:10 +02:00
bitraid
1e5b325ea5 Move file_version_info.txt to misc/ and add it to .gitignore 2018-05-03 15:50:18 +03:00
Florian Bruhin
2b6b4e82a7 Handle event_target() being None
As a stop-gap solution for #3888
2018-05-03 14:45:55 +02:00
Florian Bruhin
80778a9ff3 Update changelog 2018-05-03 14:04:56 +02:00
Florian Bruhin
6eb8284fe0 Refactor handling search in command.py 2018-05-03 14:04:56 +02:00
Florian Bruhin
49bdcd5a97 Merge remote-tracking branch 'origin/pr/3796' 2018-05-03 13:58:26 +02:00
Florian Bruhin
106e591a36 Refactor matching of Greasemonkey scripts 2018-05-03 13:53:10 +02:00
Florian Bruhin
a70f864ff5 Update changelog 2018-05-03 13:33:53 +02:00
Florian Bruhin
4932cc4d24 Merge remote-tracking branch 'origin/pr/3804' 2018-05-03 13:33:08 +02:00
Florian Bruhin
2b5f133726 Update changelog 2018-05-03 13:23:30 +02:00
Florian Bruhin
5436e27e41 Merge remote-tracking branch 'origin/pr/3756' 2018-05-03 13:21:45 +02:00
Florian Bruhin
4adf10a2f2 Update changelog 2018-05-03 09:13:48 +02:00
Florian Bruhin
b80fa7a197 Merge remote-tracking branch 'origin/pr/3858' 2018-05-03 09:13:31 +02:00
Florian Bruhin
3cc790afb3 Update changelog 2018-05-03 09:10:14 +02:00
Florian Bruhin
91aa9f6c0c Merge remote-tracking branch 'origin/pr/3884' 2018-05-03 09:09:27 +02:00
Florian Bruhin
d6cacdb42f Merge pull request #3876 from qutebrowser/pyup-scheduled-update-2018-04-30
Scheduled weekly dependency update for week 17
2018-05-02 16:22:25 +02:00
Jimmy
19554ba4a1 Update PyPI api URL.
Flask 1.0 is out, pip made breaking changes, warehouse is a thing, new
requests soon. So
much fun in python world lately.
2018-05-02 23:08:51 +12:00
toofar
d16d9e403a Make HTTPClient follow redirects by default.
Closes #3875

The autoupdator, which uses `qutebrowser.misc.httpclient` has been failing recently because the URL that it hits to check version information is now serving a 301 moved permanently. By default QNetworkRequest doesn't follow redirects so it was getting back a (non-json, despite the request) body pointing to the new location, instead or version information. This changes fixes that by changing HTTPClient to use a QNetworkRequest subclass which follows redirects by default.

It lookes like HTTPClient is currently only used in autoupdate.py, version.py, and crashdialog.py so I don't expect any breakage.

5.6-5.8 Only had a boolean setting available which allows redirects, but not from the https scheme to http, 5.9 introduces a more nuanced setting. I have tested locally on 5.7.1 and 5.10.
2018-05-02 23:08:51 +12:00
pyup-bot
432d666d25 Update pytest from 3.5.0 to 3.5.1 2018-04-30 18:29:17 +02:00
pyup-bot
1c3ee0db20 Update flask from 0.12.2 to 1.0.1 2018-04-30 18:29:16 +02:00
pyup-bot
bdc0c0ddc1 Update setuptools from 39.0.1 to 39.1.0 2018-04-30 18:29:14 +02:00
pyup-bot
b9c8a79f10 Update flake8-builtins from 1.3.0 to 1.3.1 2018-04-30 18:29:13 +02:00
cryzed
801e9e0334 qute-pass: Improve fake_key_raw() 2018-04-29 15:22:52 +02:00
Joakim Särehag
199eac2db8 window.print() support for WebEngine 2018-04-24 14:05:53 +02:00
Florian Bruhin
cfa5ee2835 Merge pull request #3862 from qutebrowser/pyup-scheduled-update-2018-04-23
Scheduled weekly dependency update for week 16
2018-04-24 11:32:56 +02:00
Florian Bruhin
9c6437b3b9 Update comment 2018-04-24 09:53:41 +02:00
Florian Bruhin
486488e2cd Filter pycodestyle 2.4.0 for pyup
See https://github.com/PyCQA/pycodestyle/issues/741
2018-04-24 09:48:47 +02:00
Florian Bruhin
b4f877d991 Update changelog 2018-04-24 09:44:54 +02:00
cryzed
92aedf84f5 qute-pass: Don't use f-strings 2018-04-23 19:16:51 +02:00
cryzed
6825dfb8d8 qute-pass: Fake strings letter-by-letter to avoid issues 2018-04-23 19:01:12 +02:00
pyup-bot
d6c6014b85 Update pytest-mock from 1.8.0 to 1.9.0 2018-04-23 18:24:29 +02:00
pyup-bot
c1ac1d702f Update hypothesis from 3.55.1 to 3.56.5 2018-04-23 18:24:27 +02:00
pyup-bot
9e50b7afcc Update cheroot from 6.1.2 to 6.2.4 2018-04-23 18:24:25 +02:00
pyup-bot
1388880e7b Update github3.py from 1.0.2 to 1.1.0 2018-04-23 18:24:24 +02:00
pyup-bot
30d60ea740 Update github3.py from 1.0.2 to 1.1.0 2018-04-23 18:24:22 +02:00
pyup-bot
28cac01a1f Update pycodestyle from 2.3.1 to 2.4.0 2018-04-23 18:24:21 +02:00
pyup-bot
1689cb09f8 Update flake8-builtins from 1.2.2 to 1.3.0 2018-04-23 18:24:19 +02:00
pyup-bot
286c71a48a Update certifi from 2018.1.18 to 2018.4.16 2018-04-23 18:24:18 +02:00
pyup-bot
c073234a8d Update certifi from 2018.1.18 to 2018.4.16 2018-04-23 18:24:16 +02:00
pyup-bot
8c286412cb Update certifi from 2018.1.18 to 2018.4.16 2018-04-23 18:24:15 +02:00
pyup-bot
b3cef948b0 Update check-manifest from 0.36 to 0.37 2018-04-23 18:24:13 +02:00
Florian Bruhin
fa41af63b6 Always set FocusOnNavigationEnabled
This fixes some focus issues after Qt 5.11 changes. There might be better ways
to solve them, but for now, this will work.

See https://codereview.qt-project.org/#/c/221408/10 and #3661:
https://github.com/qutebrowser/qutebrowser/issues/3661#issuecomment-375969315

Might also negatively affect #3834 as it essentially reintroduces QTBUG-52999 on
any Qt version: https://bugreports.qt.io/browse/QTBUG-52999

Might be reverted at a later date, but for now, I want an easy way to make tests
work on Qt 5.11 to spot further issues.
2018-04-23 16:57:10 +02:00
Florian Bruhin
e789296b7f Handle new focus object for Qt 5.11
See https://codereview.qt-project.org/#/c/221408/10 and #3661:
https://github.com/qutebrowser/qutebrowser/issues/3661#issuecomment-375969315
2018-04-23 16:54:47 +02:00
Florian Bruhin
bc9a8dd63f Handle focusProxy being None
This fixes running with Qt 5.11

See https://codereview.qt-project.org/#/c/221408/10 and #3661:
https://github.com/qutebrowser/qutebrowser/issues/3661#issuecomment-375969315
2018-04-23 16:52:53 +02:00
Florian Bruhin
6640768860 Enable libGL workaround on any system where it's available
Fixes #3772
2018-04-23 11:20:56 +02:00
bitraid
3f1041eb0a gen_versioninfo.py: remove unused import, rename most variables and reformat 2018-04-22 20:35:34 +03:00
Michal Siedlaczek
c94ea5f8d4 Merge remote-tracking branch 'upstream/master' into filter-dict-names
Merging to investigate failed tests that seem unrelated to the PR.
2018-04-21 13:29:18 -04:00
Michal Siedlaczek
e2d249541d Fix test function comment 2018-04-21 12:33:10 -04:00
cryzed
2de6428830 qute-pass: Also escape backslashes in the username 2018-04-20 18:23:50 +02:00
cryzed
c2472d88f1 qute-pass: Escape backslashes, so that they are inserted correctly 2018-04-20 18:21:55 +02:00
bitraid
8c16d81d89 Pyinstaller: add VERSIONINFO resource to Windows executables. 2018-04-20 17:48:51 +03:00
Jay Kamat
1d2dd5bf55 Use CommandDispatcher directly for / searches 2018-04-19 21:16:33 -04:00
rr-
537aa22d64 Change clipboard mocking 2018-04-18 11:00:05 +02:00
rr-
30d3612a17 Add test for rapid yanking 2018-04-18 10:59:54 +02:00
Florian Bruhin
178eeaed0d Update changelog 2018-04-17 07:49:10 +02:00
Florian Bruhin
f1967718b7 Merge remote-tracking branch 'origin/pr/3791' 2018-04-17 07:48:46 +02:00
Jay Kamat
cbb246fd0b Update tests for new implementation 2018-04-16 23:28:32 -04:00
Jay Kamat
646e92707a Call search command directly instead of using arguments 2018-04-16 23:15:56 -04:00
Florian Bruhin
1021c3a330 Merge remote-tracking branch 'origin/pr/3826' 2018-04-16 17:26:59 +02:00
Florian Bruhin
ec57e58530 Update changelog 2018-04-16 17:21:42 +02:00
Florian Bruhin
06a8a68fcb Merge remote-tracking branch 'origin/pr/3844' 2018-04-16 17:21:13 +02:00
Florian Bruhin
4a78519b63 Mark opening/closing window via JS test as flaky 2018-04-16 17:14:47 +02:00
Florian Bruhin
d2207f66f1 Skip test_set_error entirely
See #3771
2018-04-16 17:14:14 +02:00
Florian Bruhin
23d4d72f3b Update changelog 2018-04-16 17:05:10 +02:00
Florian Bruhin
4a93389356 Merge remote-tracking branch 'origin/pr/3813' 2018-04-16 17:04:53 +02:00
Sebastian Heinlein
3704e3ddd5 Fix DESTDIR and PREFIX in makefile 2018-04-16 13:44:22 +02:00
rr-
563d9bd097 Fix crash in non-rapid link yanking 2018-04-16 08:52:06 +02:00
Florian Bruhin
643bf4bc20 Remove 32-bit check in build_release.py
This is probably not needed anymore, see #3842
2018-04-16 08:33:01 +02:00
Florian Bruhin
b89b38fd3c Use latest release of PyInstaller
This is as a stop-gap solution for #3842.
2018-04-16 08:28:24 +02:00
Jimmy
c5334fb683 Greasemonkey: use UrlPatterns for match directives
The greasemonkey `@match` directive is used to match urls against
chromium url patterns (as opposed to `@include` which treats its
argument as a glob expression). I was using fnmatch for both here
because I am lazy and knew someone else was going to implement chromium
url patterns for me eventually. Now it is done and I should switch to
using them instead. The most common failing case that this will fix is
something matching on `*://*.domain.com/*` because it wouldn't match
the url with no subdomain.

This codepath is only used on webengine 5.7.1 and webkit backends.
2018-04-14 10:31:20 +12:00
Jay Kamat
0829511221 Merge pull request #3803 from toofar/fix/greasemonkey_includes_fallback
Greasemonkey: fix default include value
2018-04-13 18:26:46 -04:00
Jay Kamat
48b865073c Update changelog 2018-04-13 12:21:12 -04:00
Jay Kamat
3f9099613b Merge pull request #3807 from slackhead/tabs.mute_messages
Add option to mute the Last Tab/First Tab messages when tabs.wrap is false
2018-04-13 12:11:45 -04:00
rr-
d705e600e2 Simplify num in hinting to first_run 2018-04-13 11:03:39 +02:00
Jay Kamat
77fa0730c8 Merge pull request #3802 from jgkamat/jay/tab-take-completion
Fix win_id 0 always being included in :tab-take completion
2018-04-10 16:24:48 -04:00
rr-
46e4aeb3e9 Don't hardcode newline 2018-04-10 08:45:23 +02:00
Jay Kamat
ed76d689b0 Add test for completing other buffer excluding id0 2018-04-10 02:45:12 -04:00
pyup-bot
ca311f8588 Update tox from 2.9.1 to 3.0.0 2018-04-09 18:13:31 +02:00
pyup-bot
849e427231 Update pytest-mock from 1.7.1 to 1.8.0 2018-04-09 18:13:29 +02:00
pyup-bot
9e628901e9 Update pytest-bdd from 2.20.0 to 2.21.0 2018-04-09 18:13:27 +02:00
pyup-bot
28126055da Update py-cpuinfo from 3.3.0 to 4.0.0 2018-04-09 18:13:26 +02:00
pyup-bot
3d75d86123 Update hypothesis from 3.52.0 to 3.55.1 2018-04-09 18:13:24 +02:00
pyup-bot
03ea07e99f Update cheroot from 6.0.0 to 6.1.2 2018-04-09 18:13:23 +02:00
pyup-bot
780ced8a52 Update pylint from 1.8.3 to 1.8.4 2018-04-09 18:13:21 +02:00
pyup-bot
fc33b065c2 Update astroid from 1.6.2 to 1.6.3 2018-04-09 18:13:20 +02:00
pyup-bot
03b7459b00 Update github3.py from 1.0.1 to 1.0.2 2018-04-09 18:13:18 +02:00
pyup-bot
f964bf1b67 Update github3.py from 1.0.1 to 1.0.2 2018-04-09 18:13:17 +02:00
pyup-bot
ef2a2702f5 Update wheel from 0.30.0 to 0.31.0 2018-04-09 18:13:15 +02:00
pyup-bot
6374b6dd4c Update flake8-builtins from 1.1.1 to 1.2.2 2018-04-09 18:13:14 +02:00
rr-
945bc44550 Support rapid hinting mode in yanking 2018-04-09 17:20:35 +02:00
toofar
69d642cab8 Merge pull request #3792 from toofar/fix/gm_addstyle_earlyload
Greasemonkey: fix early GM_addStyle with fast sites.
Seems to be a working fix, despite noone having clarity on what is the ideal approach.
2018-04-09 19:11:10 +12:00
Slackhead
62aa9bdbb3 Added debug() logging for next/prev-tab and test scenarios 2018-04-09 02:03:02 +01:00
Florian Bruhin
e35c91043e pyinstaller: Use '.' as path for git-commit-id
See #384
2018-04-08 20:46:43 +02:00
Slackhead
fac546e9b4 Remove test scenarios for last/first tab when wrap is off 2018-04-08 18:56:16 +01:00
Jay Kamat
b74ddc3493 Merge pull request #3820 from AlvaroLuken/master
Added surrounding gray box to launch command for Ubuntu + Tox
2018-04-08 13:39:16 -04:00
Slackhead
b7964d9baf Remove first/last tab messages 2018-04-08 10:31:12 +01:00
Jay Kamat
76dbfa7305 Allow searching for double semicolons
Possibly breaks scripts using :search with ;; to split commands. A
workaround is to put the :search command at the end.
2018-04-05 17:20:50 -04:00
AlvaroLuken
feb2f99ea9 Added surrounding gray box to launch command for Ubuntu + Tox 2018-04-05 11:22:38 -04:00
Jay Kamat
d0d5ad2eda Stop read timer when download is cancelled 2018-04-04 01:17:37 -04:00
Slackhead
eb18f0a2ac Adjust the help docs entry 2018-04-03 17:49:51 +01:00
Slackhead
39c08cb582 Add option to mute the Last Tab/First Tab messages when tabs.wrap is false 2018-04-03 16:27:04 +01:00
Jimmy
164ea98a5b Greasemonkey: fix default include value
Greasemonkey scripts are supposed to default to running on all pages.
@jgkamat and @nemanjan00 repurted some script not running on all pages
unless they either removed (or broke) the metadata block or added an
include directive. Indeed I had a logic error when it only defaulted to
being included on all pages when no metadata block at all was included.
Whoops.
2018-04-03 20:11:15 +12:00
Jay Kamat
3b2c0823af Fix win_id 0 always being included in :tab-take completion 2018-04-02 20:34:34 -04:00
Jay Kamat
9ad6cef369 Add a test for leading arguments 2018-04-01 21:00:02 -04:00
Jay Kamat
423192e9c9 Join text arguments for :search 2018-04-01 21:00:02 -04:00
Florian Bruhin
79823a9a0b Regenerate docs 2018-04-01 13:22:01 +02:00
Jimmy
1c0616f3ec Greasemonkey: fix early addstyle with fast sites.
nemanjan00 reported this script not having any effect da850e49cc/duckduckgo-deepdark.user.js

It turns out that the previous implementation of GM_addStyle was relying
on `document.onreadystatechange` when the script ran early enough that
there was no `head` element. That event wasn't getting fired for the
main frame of duckduckgo.com for whatever reason. Maybe using
`DOMContentLoaded` or something would have worked but I just copied the
fallback in the above linked script which seems to work just fine.
2018-04-01 12:38:48 +12:00
Ryan Roden-Corrent
bca07eebba Unit-test configmodel arg filtering.
Test that values the user has already input are omitted from the
suggestions. This brings configmodel coverage back to 100%.
2018-03-31 18:55:54 -04:00
pylipp
599a3d75a4 Add userscript to download bibtex for DOI scraped from current tab 2018-03-31 21:43:20 +02:00
Ryan Roden-Corrent
707fc1176d Regen completion if args change.
Allow completion functions to react dynamically to args as the user
inputs them. This allows config-cycle to filter out values that were
already provided.

Args provided after the maxsplit do not cause the completion to regen.
For example, successive words typed after `:open` just set the filter
pattern and do not spuriously regenerate the completion model.
2018-03-31 11:45:50 -04:00
Jay Kamat
cb8a75577e Add tests for hinting with --first 2018-03-30 15:03:08 -04:00
Jay Kamat
3d5d679561 Add basic implementation for clicking first hinted element 2018-03-30 15:03:08 -04:00
Florian Bruhin
6151d077e5 Update changelog 2018-03-30 11:49:39 +02:00
Florian Bruhin
d438aa15fa Simplify setting _qute_script_id 2018-03-30 11:48:06 +02:00
Jay Kamat
7f5a79cdfd Escape strings with string_escape rather than tojson 2018-03-30 01:40:49 -04:00
Florian Bruhin
5bc794f85a Update changelog 2018-03-28 21:11:20 +02:00
Florian Bruhin
d7455bcdba Merge remote-tracking branch 'origin/pr/3765' into adblock 2018-03-28 20:32:47 +02:00
Florian Bruhin
0b667e4701 Merge remote-tracking branch 'origin/pr/3763' into adblock 2018-03-28 20:32:39 +02:00
Michal Siedlaczek
d606cd5550 spell test formatting and docstrings 2018-03-28 14:13:46 -04:00
Michal Siedlaczek
7c1de99876 Fix test coverage 2018-03-28 12:16:50 -04:00
George Edward Bulmer
2789bec1e7 Modify assert_url to treat localhost differently 2018-03-28 14:27:17 +01:00
George Edward Bulmer
1ccb464d1c Return removed comment about hosts format 2018-03-28 14:17:13 +01:00
Florian Bruhin
758ea8b171 Update changelog 2018-03-28 09:36:32 +02:00
Florian Bruhin
32568a6da4 Simplify tests 2018-03-28 09:33:27 +02:00
Florian Bruhin
14792472db Merge remote-tracking branch 'origin/pr/3680' 2018-03-28 09:29:54 +02:00
Florian Bruhin
005fa8b675 Fix newline 2018-03-28 09:14:26 +02:00
Florian Bruhin
c7e5033eaa Set MainWindow as parent of TabbedBrowser
If we close the MainWindow (and it gets deleted), we need to make sure to delete
the TabbedBrowser as well.

Fixes #3781
2018-03-28 08:58:07 +02:00
Florian Bruhin
a828851640 Update recommended Qt version in readme 2018-03-28 08:48:18 +02:00
Jay Kamat
b873cfb18a Fix style issues in qute-keepass 2018-03-27 23:43:40 -04:00
Jussi Timperi
046a3dc159 Add option to only show favicons for pinned tabs
Closes #3440
2018-03-28 00:45:57 +03:00
Florian Bruhin
68b707c749 Update changelog 2018-03-27 12:01:42 +02:00
Florian Bruhin
9cff0e7367 Merge remote-tracking branch 'origin/pr/3742' 2018-03-27 12:01:18 +02:00
Florian Bruhin
7e66c3cf46 Update changelog 2018-03-27 11:11:34 +02:00
Florian Bruhin
bf4aab79ac Merge remote-tracking branch 'origin/pr/3751' 2018-03-27 11:11:12 +02:00
Florian Bruhin
a6f6fdf19b Update docs 2018-03-27 11:09:18 +02:00
Florian Bruhin
e5ffcbd49f Merge remote-tracking branch 'origin/pr/3750' 2018-03-27 11:07:29 +02:00
Florian Bruhin
730347e449 Merge branch 'pyup-scheduled-update-2018-03-26' 2018-03-27 10:27:59 +02:00
Florian Bruhin
55112b52e0 Merge remote-tracking branch 'origin/pr/3776' 2018-03-27 10:25:41 +02:00
Florian Bruhin
2249a88e3a Make test_simple_js_webengine work correctly 2018-03-27 07:29:43 +02:00
Florian Bruhin
6dbd6d1ddf Move test_qt_javascript.py 2018-03-27 07:14:05 +02:00
Florian Bruhin
6ecea8ef17 Revert accidental changes to test_qt_javascript.py 2018-03-27 07:13:17 +02:00
Florian Bruhin
021bb25622 Add regenerating website to contributing.asciidoc
[ci skip]

(cherry picked from commit 9f95736bbe4a00c9cc4a8b222ab3dc55d6bdf96c)
2018-03-26 22:53:46 +02:00
Florian Bruhin
22e887045b Remove flake8-builtins filter 2018-03-26 18:27:15 +02:00
pyup-bot
4896765fcc Update virtualenv from 15.1.0 to 15.2.0 2018-03-26 18:17:25 +02:00
pyup-bot
d98590d712 Update pytest-faulthandler from 1.4.1 to 1.5.0 2018-03-26 18:17:23 +02:00
pyup-bot
496e6fc624 Update pytest from 3.4.2 to 3.5.0 2018-03-26 18:17:22 +02:00
pyup-bot
55a818b156 Update py from 1.5.2 to 1.5.3 2018-03-26 18:17:20 +02:00
pyup-bot
85be0f2801 Update py from 1.5.2 to 1.5.3 2018-03-26 18:17:19 +02:00
pyup-bot
b7b4cc7f31 Update hypothesis from 3.50.0 to 3.52.0 2018-03-26 18:17:17 +02:00
pyup-bot
056a901da0 Update python-dateutil from 2.7.0 to 2.7.2 2018-03-26 18:17:16 +02:00
pyup-bot
ba9b166962 Update python-dateutil from 2.7.0 to 2.7.2 2018-03-26 18:17:15 +02:00
pyup-bot
edf2652431 Update flake8-builtins from 1.0.post0 to 1.1.1 2018-03-26 18:17:13 +02:00
Florian Bruhin
6755e17630 Update changelog 2018-03-26 10:54:15 +02:00
Florian Bruhin
d4899240de Break long lines 2018-03-26 10:51:04 +02:00
Florian Bruhin
ddbb6b5198 Merge remote-tracking branch 'origin/pr/3769' 2018-03-26 10:45:31 +02:00
Florian Bruhin
1087ce075d Merge remote-tracking branch 'origin/pr/3767' 2018-03-26 10:45:23 +02:00
Ryan Roden-Corrent
f237a87ad0 Completion for varargs.
When a command has positional varargs, keep offering the configured
completion for each successive argument.

Right now this only influences `config-cycle`.

Previously, `config-cycle <option> ` would offer a value completion for
only the first argument after the option. Now it will keep offering
value completion for each successive argument.

This will be useful for passing multiple tags to the new bookmark
commands that will be added for #882.
2018-03-25 21:59:30 -04:00
Philip Lewis
cecb79cf05 Fix keyhints for special characters
`prefix` is a string and `seq` is a key sequence, so removing `len(prefix)`
items from `seq` will remove too many if `prefix` contains a special character
(ex "<Ctrl+x>").  Remove the number of characters from `str(seq)` instead.
2018-03-25 15:18:02 -04:00
Florian Bruhin
d4ea1df232 Improve window_open.html tests 2018-03-25 19:56:48 +02:00
Florian Bruhin
91ca7d0911 tests: Rename close function in window_open.html
Naming it close() conflicts with the global JS close()
2018-03-25 19:39:34 +02:00
bitraid
68e3ad6cba Pyinstaller: don't use upx 2018-03-25 19:43:59 +03:00
Florian Bruhin
12a405965a Make QtWebEngine inspector work with JS disabled 2018-03-25 14:55:03 +02:00
George Edward Bulmer
a85ac1725f Missing fullstop in a docstring 2018-03-24 22:56:47 +00:00
George Edward Bulmer
eb5684e5f7 Pylint fix 2018-03-24 21:52:26 +00:00
George Edward Bulmer
b9bcad9c14 Grammar change 2018-03-24 21:13:22 +00:00
George Edward Bulmer
64b01cc076 Remove extraneous part 2018-03-24 21:10:23 +00:00
George Edward Bulmer
1380fef600 Add test for parsing multiple lines 2018-03-24 21:08:55 +00:00
George Edward Bulmer
8809ef02a1 Add support for more than 1 host on a given line 2018-03-24 20:20:16 +00:00
George Edward Bulmer
3f37fcf8fa Modify tests, localhost should never be blocked 2018-03-24 20:15:34 +00:00
George Edward Bulmer
c8db9e1c76 Remove WHITELISTED, making file parsing satisfy:
1) 'dotless' hosts, e.g. localhost, cannot be blocked by a file
2) hosts ending in '.localdomain' cannot be blocked by a file
2018-03-24 19:42:34 +00:00
George Edward Bulmer
01d8314dd8 Change default blocklist to StevenBlack combined 2018-03-24 18:35:03 +00:00
rien333
fa21d280fa Remove unnecessary hide operation 2018-03-24 05:09:03 +01:00
rien333
e211801e16 Handle wayland decoration option rename through configdata.yml 2018-03-23 15:24:18 +01:00
rien333
6db1ab0a58 Cosmetic changes 2018-03-23 15:21:02 +01:00
rien333
aa70395925 Merge branch 'master' of https://github.com/rien333/qutebrowser 2018-03-23 15:19:58 +01:00
rien333
880b33fff5 Restore correct window visibility after decoration config change 2018-03-23 15:19:37 +01:00
Florian Bruhin
f1789effdc Stabilize navigate.feature on Qt 5.11
Looks like we get qute://help as URL from the previous test otherwise?
See #3661
2018-03-23 10:29:25 +01:00
Florian Bruhin
e095f64eb6 Merge remote-tracking branch 'origin/pr/3752' 2018-03-23 08:25:59 +01:00
Florian Bruhin
06df88075e Merge remote-tracking branch 'origin/pr/3749' 2018-03-23 08:25:49 +01:00
Florian Bruhin
00bdb60627 Ignore "Dropping message on closed channel." message
This seems to happen with this test in tabs.feature with Qt 5.11:
Scenario: :buffer with wrong argument (-1)

It only happens ~1/50 times though, and seems like some Qt bug.

See #3661
2018-03-23 07:59:46 +01:00
rien333
1fc0abb064 Delete .#configfiles.py 2018-03-23 02:50:36 +01:00
Jay Kamat
a1776087e0 Fix login when only one entry is available 2018-03-22 21:35:00 -04:00
Jay Kamat
948866f4f2 Add support for keepass keyfiles 2018-03-22 21:21:59 -04:00
rien333
e2250d65e9 Merge branch 'master' of git://github.com/qutebrowser/qutebrowser 2018-03-23 01:43:23 +01:00
rien333
bb0c79b5a2 Fix wayland test 2018-03-23 01:38:45 +01:00
rien333
ff299c87a8 Reinsert wayland specific code for toggling decoration visibility 2018-03-22 23:32:37 +01:00
rien333
2d2bdad2ca Do not require restart after decoration option change 2018-03-22 23:26:45 +01:00
Jay Kamat
07d043fe81 Add basic tests for tab width sizing 2018-03-22 14:27:33 -04:00
Jay Kamat
477da6002a Fix minimum size for vertical tabs 2018-03-22 12:59:35 -04:00
Jay Kamat
d2c01d7ee6 Always display plain titles in tab tooltips
Closes #3741
2018-03-22 12:03:15 -04:00
Florian Bruhin
b67a031151 Rephrase autoconfig.yml docs
[ci skip]
2018-03-22 08:37:12 +01:00
Jay Kamat
a9a7f5da45 Fix choking on passwords with <x> syntax in them 2018-03-22 03:01:50 -04:00
Jay Kamat
b169a1c802 Add raw first draft of qute-keepass
This needs a lot more work...
2018-03-22 02:43:15 -04:00
rien333
764e79e505 Small refactor 2018-03-22 03:44:24 +01:00
rien333
7b7faa9f66 Fix silly redefinition 2018-03-22 03:42:57 +01:00
rien333
a6b92dbbd3 General window decoration hiding option 2018-03-22 02:23:21 +01:00
bitraid
4fb940241c Add 32bit color for Windows icon 2018-03-21 23:13:26 +02:00
George Edward Bulmer
51f9464eb2 Add test for hints with numberpad numbers 2018-03-21 17:06:13 +00:00
George Edward Bulmer
0645865d22 Add test case for is_special in keyutils 2018-03-21 16:38:58 +00:00
George Edward Bulmer
991ba54499 Change the formatting of the numpad keys
This makes it consistent with as before
2018-03-21 15:41:08 +00:00
George Edward Bulmer
1cf3d66a22 Revert test and modify returned key 2018-03-21 15:34:32 +00:00
Florian Bruhin
300d873b18 Update Debian install instructions
Supersedes #3707
Fixes #3737

[ci skip]
2018-03-21 10:55:54 +01:00
Florian Bruhin
a8bbd5fa4d Update docs for TimestampTemplate 2018-03-21 10:14:48 +01:00
Florian Bruhin
2d655a7230 Update changelog 2018-03-21 08:27:09 +01:00
George Edward Bulmer
4d7f8e4878 Pylint fix 2018-03-21 00:28:52 +00:00
George Edward Bulmer
bc885cc9ee Modify the keypad test
The new behaviour is for the keypad to yield its usual key,
such that instead of a special <Num+2> keypress, it yields <2>
2018-03-20 23:19:36 +00:00
George Edward Bulmer
a5dc8a3025 Fix crash in string representation of key 2018-03-20 23:13:56 +00:00
George Edward Bulmer
d6463d5ade Remove Qt.KeypadModifier as a special key 2018-03-20 22:33:11 +00:00
bitraid
7601e58c81 Merge remote-tracking branch 'upstream/master' 2018-03-20 23:54:18 +02:00
Ryan Roden-Corrent
16bda94e2b Remove unused import and TODO from urlmarks test. 2018-03-20 21:41:19 +00:00
Ryan Roden-Corrent
5a20052bce Add unit tests for urlmarks. 2018-03-20 21:41:19 +00:00
bitraid
e316f1768e More compatible icon for Windows
Fixes #3601
2018-03-20 23:28:42 +02:00
Florian Bruhin
11696f0073 Fix test_configinit 2018-03-20 22:16:16 +01:00
Florian Bruhin
f9d976880e Disable shared web workers on Qt < 5.11 2018-03-20 21:14:04 +01:00
Florian Bruhin
a5f1022330 Log document.body in JS tests 2018-03-20 11:56:46 +01:00
Florian Bruhin
85d3d4baba Mark test_set_error as flaky 2018-03-20 10:25:03 +01:00
Florian Bruhin
561295238d Another try at stabilizing test_set_error 2018-03-20 08:58:48 +01:00
Florian Bruhin
0e670a597e Update docs 2018-03-20 08:53:59 +01:00
Florian Bruhin
0fd3674d9e Update changelog 2018-03-20 07:07:05 +01:00
Florian Bruhin
0ee9d73fe2 Merge remote-tracking branch 'origin/pr/3692' 2018-03-20 07:05:43 +01:00
Florian Bruhin
81827a3150 Try to stabilize stylesheet tests 2018-03-20 07:05:03 +01:00
Florian Bruhin
32145d579b Merge branch 'pyup-scheduled-update-2018-03-19' 2018-03-20 07:00:37 +01:00
Florian Bruhin
f230fd3abb Rebuild requirement files 2018-03-20 06:59:57 +01:00
Florian Bruhin
51318d66c8 Fix ignore for new flake8-per-file-ignores release 2018-03-20 06:55:05 +01:00
Florian Bruhin
59602ec5b5 requirements: Blacklist flake8-builtins 1.1.0
See https://github.com/gforcada/flake8-builtins/issues/19
2018-03-20 06:55:05 +01:00
Florian Bruhin
a374698693 Fix lint 2018-03-20 06:38:11 +01:00
Florian Bruhin
b56988f0a4 Update changelog 2018-03-20 06:27:48 +01:00
Florian Bruhin
32df91fbae Merge remote-tracking branch 'origin/pr/3604' 2018-03-20 06:24:57 +01:00
Jay Kamat
f6c00babbe Prevent minimumTabsizeHint from being called when booting on mac
Move workaround higher up to the start of tabSizeHint
2018-03-19 18:29:51 -04:00
Florian Bruhin
7eaad59be3 caret: Ignore None value from setInitialCursor
See #3583
2018-03-19 22:32:26 +01:00
Florian Bruhin
ea1e52ea7c Load page before loading stylesheets
If we don't do this, when doing:

   self.config_stub.val.content.user_stylesheets = css_path

then _update_stylesheet gets called before the stylesheet QWebEngineScript did
run (as there was no load yet), so we get:

  [:2] Uncaught TypeError: Cannot read property 'stylesheet' of undefined!

Instead, load the page first and then update the stylesheet.
This tests that live updating works properly, and also makes sure we don't run
into the problem described above.
2018-03-19 21:44:47 +01:00
Florian Bruhin
b588f54a53 Fail javascript tests on logging messages 2018-03-19 20:43:55 +01:00
Florian Bruhin
9031b3e535 Remove @pyqtSlot for on_download_requested
For some reason, this breaks when test_pac is run...
2018-03-19 20:17:15 +01:00
Florian Bruhin
1162e640c5 Remove unused imports 2018-03-19 19:42:56 +01:00
Florian Bruhin
0ea7a1457d Make test_position_caret work again
The tests only work properly with QtWebKit (and aren't needed on QtWebEngine).
Also, for some reason the scrolled_down tests only work without Xvfb.
2018-03-19 19:38:21 +01:00
Florian Bruhin
f5d7605ae0 Add a :scroll-to-anchor command
Fixes #2784
2018-03-19 19:18:33 +01:00
Florian Bruhin
e50068021d Use signals to update statusbar in caret mode
This means we don't use objreg anymore to get the status bar, and also makes the
bar more accurately reflect reality.

See #3583
2018-03-19 18:44:06 +01:00
Florian Bruhin
460bd86579 Initial attempt at using the tab API for tests/unit/javascript 2018-03-19 18:18:21 +01:00
Florian Bruhin
e43f0a61b9 Move all QWebEngineScript related code out of webenginesettings
It looks like there's some issue with QWebEngineScript in a profile, at least
with older Qt versions...

See #3497, #3377
2018-03-19 17:33:02 +01:00
pyup-bot
8eb4d15805 Update hypothesis from 3.49.0 to 3.50.0 2018-03-19 17:13:26 +01:00
pyup-bot
3b7e1b3fe2 Update pylint from 1.8.2 to 1.8.3 2018-03-19 17:13:25 +01:00
pyup-bot
650aa532cd Update astroid from 1.6.1 to 1.6.2 2018-03-19 17:13:23 +01:00
pyup-bot
1f3fc756db Update github3.py from 0.9.6 to 1.0.1 2018-03-19 17:13:22 +01:00
pyup-bot
2d5d485daf Update github3.py from 0.9.6 to 1.0.1 2018-03-19 17:13:20 +01:00
pyup-bot
b77e43d74f Update setuptools from 38.5.2 to 39.0.1 2018-03-19 17:13:18 +01:00
pyup-bot
5a26858e07 Update flake8-per-file-ignores from 0.5 to 0.6 2018-03-19 17:13:17 +01:00
pyup-bot
99ea4b98e8 Update flake8-builtins from 1.0.post0 to 1.1.0 2018-03-19 17:13:15 +01:00
Florian Bruhin
da8b6fb50a Decrease maximum repetitions for QtWebEngine scrolling
At least for Qt debug builds, 5000 seems to be much too much.
See #3661
2018-03-19 14:11:01 +01:00
Florian Bruhin
33066af51d Break long comment 2018-03-19 13:59:30 +01:00
Florian Bruhin
6a971e2846 Ignore OnDidStopLoading error message
See #3661, https://bugreports.qt.io/browse/QTBUG-66661
2018-03-19 12:13:10 +01:00
Florian Bruhin
f28a39571c Fix caret.js indent 2018-03-19 11:49:24 +01:00
Florian Bruhin
bee04a1eec Wait until runner is finished in test_custom_env
This seems to at least lead to less warnings when running the test.
2018-03-19 11:43:08 +01:00
Florian Bruhin
39d25c1127 Update _chromium_version comment
[ci skip]
2018-03-19 11:15:19 +01:00
Florian Bruhin
07e831cee5 Update changelog 2018-03-19 10:28:25 +01:00
Florian Bruhin
b3342d8f70 Merge remote-tracking branch 'origin/pr/3728' 2018-03-19 10:28:04 +01:00
Florian Bruhin
6465d64738 Fix lint 2018-03-19 10:22:21 +01:00
Florian Bruhin
232fd19422 Fix unit tests after refactoring 2018-03-19 09:53:35 +01:00
Florian Bruhin
a4530797ea Add a ProfileSetter class to webenginesettings
Easier than passing a profile around everywhere.
2018-03-19 09:40:57 +01:00
Florian Bruhin
1b84bbd61d Refactor initialization of internal JavaScript
- Initialize JavaScript in webenginesettings.py instead of webenginetab.py
- Move JavaScript snippet into a .js file
- Make sure scripts can be re-run and do nothing if already run.
- Run scripts on DocumentCreation *and* DocumentReady. Closes #3717.
- Give each script an unique name for debugging.
- Also make custom stylesheets work on chrome:// pages
2018-03-19 09:14:55 +01:00
Florian Bruhin
f2864c6253 Break greasemonkey_wrapper lines differently 2018-03-19 09:13:50 +01:00
Florian Bruhin
8ae3047f2a Update changelog 2018-03-18 19:00:07 +01:00
Florian Bruhin
b154846bdc Merge remote-tracking branch 'origin/pr/3733' 2018-03-18 18:59:41 +01:00
Florian Bruhin
8a3d9c0c01 Adjust ignored log messages for Qt 5.11 2018-03-18 18:58:29 +01:00
AlternateData
62d30fe589 use 0 and maxint as bounds 2018-03-18 10:06:41 +01:00
AlternateData
a52d18b700 Add correct maximum and minimum value for tabs.switching_delay 2018-03-17 17:59:31 +01:00
Marc Jauvin
b7159d780a Merge 'origin/master' into tab-input-mode 2018-03-16 14:28:36 -04:00
Michal Siedlaczek
f9e702bae5 Warn about malformed dictionaries 2018-03-16 11:28:45 -04:00
gammelon
f57e47c742 Separate tests for _get_search_url 2018-03-16 11:42:51 +01:00
Bryan Bugyi
18146e2fbc Fix: prevent unmatched quote (#3726) 2018-03-16 06:16:16 -04:00
Florian Bruhin
f7074b80d0 Fix lint 2018-03-16 09:07:25 +01:00
Bryan Bugyi
fa282d574d Fix: preserve exit status of task command (#3726) 2018-03-16 03:44:22 -04:00
Bryan Bugyi
3b0b4ffe66 Fix: restrict output of task to one line (closes #3726) 2018-03-16 03:28:44 -04:00
Florian Bruhin
a6ce188e0d Update changelog 2018-03-16 08:21:11 +01:00
Florian Bruhin
01845faac5 Set window title/icon on correct object
This was a regression introduced in #3613.
Fixes #3727
2018-03-16 08:20:27 +01:00
Florian Bruhin
69a013bc82 Update changelog 2018-03-15 14:53:20 +01:00
Florian Bruhin
6f8eb419ae Emit predicted_navigation when loading sessions
This avoids reloads (because of changed settings) after a session has been
loaded.

Related to #3718
2018-03-15 14:51:36 +01:00
Florian Bruhin
1e4b80d1ac Don't emit predicted_navigation when reloading because of it
When we reload because of a config change in _on_load_finished, we can't use
self.reload() as no URL is set yet. Instead, we call self.openurl with the
current URL.

However, we need to make sure we don't emit predicted_navigation again at that
point.

This should (finally) fix #3718
2018-03-15 14:44:44 +01:00
Florian Bruhin
5dbda3016b Clean up predicted_navigation handling
This also adds some more logging for #3718
2018-03-15 14:16:10 +01:00
Florian Bruhin
1d25b212d5 Add missing qapp fixtures to tests
See #3723
2018-03-15 09:06:40 +01:00
Florian Bruhin
f538fc8b74 Update release checklist
[ci skip]
2018-03-14 21:50:22 +01:00
Florian Bruhin
c2b995edde Update build_release for github3.py 1.0 2018-03-14 21:11:03 +01:00
Florian Bruhin
1d562d919e Include requirements files in built release
This is needed to use "tox -e mkvenv"
2018-03-14 21:10:35 +01:00
Florian Bruhin
a60bae30b7 Release v1.2.1
(cherry picked from commit 6145786e46)
2018-03-14 20:20:20 +01:00
Florian Bruhin
523502785a Update changelog for v1.2.1 2018-03-14 20:17:34 +01:00
Florian Bruhin
84c7c37e8e Swap Control/Meta back on macOS
Fixes #3697

(cherry picked from commit fd9e7bed7fd9842eac22ed304a094a92cc953577)
2018-03-14 19:40:56 +01:00
Florian Bruhin
d232b3ea57 Disable test_software_rendering on macOS
For some reason, macOS doesn't care about us disabling software rendering
2018-03-14 19:31:36 +01:00
Florian Bruhin
7a861b7119 Update changelog 2018-03-14 18:19:11 +01:00
Florian Bruhin
a22f973c99 Don't emit predicted_navigation for reloads at all
When we reload a page because of a config change, we won't get another
titleChanged signal (at least sometimes).

Also, the predicted_navigation signal is worthless when reloading anyways, as
we're going to load the same URL and not something different.

Fixes #3718
2018-03-14 18:19:11 +01:00
Florian Bruhin
724e531087 Merge pull request #3715 from toofar/docs/greasy-faq
Greasemonkey: add FAQ entry.
2018-03-14 11:13:21 +01:00
Florian Bruhin
5c73910a33 Revert "Insert qutebrowser scripts on DocumentCreation and DocumentReady"
This reverts commit fac0f66e52.
2018-03-14 10:19:23 +01:00
Florian Bruhin
84bae210ab Fix wrong number in cheatsheet 2018-03-14 09:06:04 +01:00
Jimmy
e5edc0f940 fixup! Greasemonkey: add FAQ entry.
Change the wording and the `--debug` suggestion.

Add comma after "this is currently not supported", I was tempted to add
a semicolon before it to but I resisted.

Added a "then" after QtWebEngine version list, not a comma.
2018-03-14 20:25:59 +13:00
Florian Bruhin
0d21265005 Update changelog 2018-03-14 08:07:38 +01:00
Florian Bruhin
c0fdf19756 Merge remote-tracking branch 'origin/pr/3704' 2018-03-14 08:06:24 +01:00
Florian Bruhin
102b2be361 Update changelog 2018-03-14 07:56:00 +01:00
Florian Bruhin
fac0f66e52 Insert qutebrowser scripts on DocumentCreation and DocumentReady
In #3521, the injection point was changed to DocumentReady as a fix for
https://bugreports.qt.io/browse/QTBUG-66011 / #3490.

However, that prevents e.g. using hints before a page is fully loaded, which can
be annoying on a mobile connection.

Instead, just run the scripts twice, which won't hurt and makes sure they're
available.
2018-03-14 07:50:41 +01:00
Jimmy
2563ecf6d8 Greasemonkey: add FAQ entry.
The most common questsions regarding greasemonkey support on IRC are
"how do I use it" and "why doesn't my script work", hopefully this can
be a starting point for most people experiencing issues.
2018-03-14 19:48:46 +13:00
Florian Bruhin
64530375ab Update changelog 2018-03-14 07:44:51 +01:00
Marc Jauvin
c9f6cd507b address requested changes
- add INPUT_MODES & PROMPT_MODES constants in modeman
- use those in tabbedbrowser and modeman
- fix debug logs format to be more human readable
- fix associated tests for new debug logs
2018-03-13 23:31:48 -04:00
Jay Kamat
7278b7c2e5 Improve wording of documentation 2018-03-13 22:25:26 -04:00
Jay Kamat
35beff98a9 Add test for #3711 2018-03-13 19:18:42 -04:00
Jay Kamat
a6e94cf30c Fix hinting in frames on qt5.9 with input ranges 2018-03-13 18:54:08 -04:00
Florian Bruhin
8e01353a94 Update changelog 2018-03-13 14:41:40 +01:00
Florian Bruhin
8b9c6ccee2 Split up BaseKeyParser.handle into functions 2018-03-13 14:40:54 +01:00
Florian Bruhin
b88ac51d25 Fall back to non-keypad keys without any keypad bindings
Fixes #3701
2018-03-13 14:40:54 +01:00
Ryan Roden-Corrent
73517f0a51 Fix test_backup_error.
- Need caplog at level error
- Rename test to be unique
2018-03-13 08:50:34 -04:00
Markus Ongyerth
40364ce774 view-source pygments feedback pass 2018-03-13 12:40:51 +01:00
Ryan Roden-Corrent
27966c94a6 Fix up editor backup patch.
- Use qutebrowser-editor-backup as the backup file prefix
- Consistently use message.error instead of cmdexc
- Improve test coverage for the backup function
- Fix lint errors in the unit test code
2018-03-13 07:34:18 -04:00
Florian Bruhin
a7b6d179d4 Update changelog 2018-03-13 09:51:03 +01:00
Florian Bruhin
1c9598d2c0 Don't emit predicted_navigation with invalid URLs
Fixes #3706
2018-03-13 09:46:09 +01:00
Florian Bruhin
dcd6bcd2f4 Apply changes from PR review 2018-03-13 08:47:41 +01:00
Florian Bruhin
c590648077 Merge remote-tracking branch 'origin/pr/3613' 2018-03-13 08:39:36 +01:00
Florian Bruhin
80843c0b53 Update changelog 2018-03-13 07:39:04 +01:00
Florian Bruhin
14d6e737fa Merge remote-tracking branch 'origin/pr/3606' 2018-03-13 07:37:57 +01:00
Florian Bruhin
9c613fb700 Merge pull request #3705 from qutebrowser/pyup-scheduled-update-2018-03-12
Scheduled weekly dependency update for week 10
2018-03-12 18:57:36 +01:00
pyup-bot
01aa1f755d Update pytest from 3.4.1 to 3.4.2 2018-03-12 17:10:19 +01:00
pyup-bot
3855d49821 Update hypothesis from 3.48.0 to 3.49.0 2018-03-12 17:10:18 +01:00
pyup-bot
55c24cad9a Update setuptools from 38.5.1 to 38.5.2 2018-03-12 17:10:16 +01:00
Ryan Roden-Corrent
38bb3673db Preserve a backup if editor callback fails.
Currently the editor deletes its temp file whenever editing is finished.
With this patch, the file will not be deleted if the editor callback
encounters an exception.

One example is if the tab containing the edited element is closed. The
editor errors with "Edited element vanished", but with this patch it
will also print "Backup at ..." so the user does not lose their work.

Resolves #1596.

Supersedes #3641, using the cleaner approach started in #1677.
2018-03-12 08:34:50 -04:00
gammelon
455f6b8a70 Fix blank lines 2018-03-12 12:37:52 +01:00
Florian Bruhin
8c5b7bcd03 Fix lint 2018-03-12 08:51:36 +01:00
Florian Bruhin
a6885a0d41 Update changelog 2018-03-12 08:03:20 +01:00
Florian Bruhin
9941812127 Normalize keys read from the config
This makes sure the internal bindings.commands object only contains normalized
key sequences.

Fixes #3699
2018-03-12 08:00:56 +01:00
Florian Bruhin
990c0707f4 Make from_obj() work for List/Dict configtypes
We can't easily make it work for ListOrValue as we don't know which of both we
get at this point.
2018-03-12 08:00:18 +01:00
Florian Bruhin
c03ef10d54 tests: Add a yaml_config_stub fixture 2018-03-12 07:39:20 +01:00
Florian Bruhin
d72691ee49 Simplify ListOrValue configtype 2018-03-12 07:38:56 +01:00
Michal Siedlaczek
29eadf7141 Filter installed dictionaries using a regex to ensure correct name 2018-03-11 17:50:20 -04:00
Florian Bruhin
27c2650245 build_release: Wait before detaching volume
This hopefully helps with detaching it properly.
2018-03-11 21:06:31 +01:00
Florian Bruhin
b0bf02e23a Update changelog 2018-03-11 21:05:10 +01:00
Florian Bruhin
30ab1d0218 Force PyQt 5.10.0 with "tox -e mkvenv-pypi"
Fixes #3662
2018-03-11 20:47:01 +01:00
Florian Bruhin
7f68affa30 Merge remote-tracking branch 'origin/pr/3695' 2018-03-11 14:36:06 +01:00
Florian Bruhin
b6e29d8eae Be explicit about expected output in test 2018-03-11 14:35:15 +01:00
Florian Bruhin
591883656e Merge remote-tracking branch 'origin/pr/3700' 2018-03-11 14:34:06 +01:00
Florian Bruhin
f0a649e101 Mark another GreaseMonkey test as flaky
See #3238
2018-03-11 14:29:54 +01:00
Roman Bogorodskiy
d0342bffc4 Show version for POSIX OSes
For POSIX OSes other than Linux and macOS set OS Version to
platform.uname() instead of showing 'OS Version: ?'.
2018-03-11 13:28:53 +04:00
Florian Bruhin
75ab8f077d Fix keybinding cheatsheet URLs in quickstart.asciidoc
The URLs and the patching were changed in
96e8151cce but not in quickstart.asciidoc.
2018-03-11 08:30:41 +01:00
Florian Bruhin
d9f7d401c6 Handle ImportError in version.opengl_vendor
Fixes #3698
2018-03-11 08:15:22 +01:00
Jay Kamat
996561b50e Apply tabs.min_width to all tabs when tabs are unshrunk 2018-03-09 14:36:01 -05:00
Sebastian Noack
4cf0311d7f Updated flake8-per-file-ignores to version 0.5 2018-03-09 14:09:49 -05:00
Johannes Wegener
cf4e472461 add basic completion to file dialog 2018-03-09 16:21:57 +01:00
gammelon
0ce94dae1c forgot one bit 2018-03-09 15:55:40 +01:00
gammelon
7e3c966afe rewrite tests 2018-03-09 15:52:03 +01:00
Jay Kamat
1672995639 Clean up style issues 2018-03-09 02:19:49 -05:00
Jay Kamat
4a78b0519d Add tabs.min_width setting
Controls min width in pixels of non pinned tabs

Closes #3690
2018-03-09 02:05:49 -05:00
Jay Kamat
46533c3367 Fix pinned tabs being too small in extreme situations 2018-03-09 02:02:31 -05:00
Marc Jauvin
7c2802e843 beautify code as requested 2018-03-07 11:46:14 -05:00
Marc Jauvin
5992688926 Save input modes when mode_on_change=='restore' 2018-03-07 11:43:17 -05:00
gammelon
a730290d40 Use QUrl for parsing, add tests 2018-03-05 16:32:41 +01:00
Markus Ongyerth
f7bcdfc818 Add --pygment argument to view-source
The --pygment argument allows to use the pygment version of view-source
over the qtwebengine internal one.
This version is slightly different in what's processed before the site
is generated, so some javascript created texts can be available.
2018-02-28 16:53:28 +01:00
gammelon
16218a9900 Remove unnecessary try, rephrase to imperative mood 2018-02-20 18:11:50 +01:00
bttner
e169e2165d Refactor TabbedBrowser from inheritance to composition 2018-02-19 14:29:05 +01:00
gammelon
42ac3dcda0 Add Option url.open_base_url
when set to true, invoking a searchengine shortcut without argument
opens the baseurl of that searchengine instead of DEFAULT searchengine
2018-02-17 11:21:22 +01:00
Marc Jauvin
872cff2ae1 make sure tab is not None either, had a crash because of this 2018-02-16 14:03:11 -05:00
Marc Jauvin
620a966d1e add debug logs and adjust tests to use them 2018-02-14 09:58:23 -05:00
Marc Jauvin
b791dd3eb4 no restore while in prompt modes on tab change 2018-02-14 09:44:40 -05:00
Marc Jauvin
8a3049f09b make sure there IS a current widget before using it 2018-02-14 08:33:32 -05:00
Marc Jauvin
9b8a182a78 history-clear does nothing to help here 2018-02-13 17:03:01 -05:00
Marc Jauvin
f94e12008a fix the tests by clearing history 2018-02-13 16:23:56 -05:00
Marc Jauvin
e38df261cb skip this test for qt>=5.10 until the log problem gets resolved 2018-02-13 13:00:44 -05:00
Marc Jauvin
fbb78f1133 hook into modeman's entered and left signals
- save previous mode when a prompt is shown
- restore previous mode when finished prompting
2018-02-12 21:57:53 -05:00
Marc Jauvin
6214c38d7e add input_mode tests for tabs.mode_on_change 2018-02-12 18:11:32 -05:00
Marc Jauvin
d04fc29163 save input_mode when entering/leaving mode
instead of when changing tab
2018-02-12 18:10:22 -05:00
214 changed files with 5334 additions and 1877 deletions

View File

@@ -5,15 +5,15 @@ cache:
build: off
environment:
PYTHONUNBUFFERED: 1
PYTHON: C:\Python36\python.exe
PYTHON: C:\Python36-x64\python.exe
matrix:
- TESTENV: py36-pyqt510
- TESTENV: py36-pyqt511
- TESTENV: pylint
install:
- '%PYTHON% -m pip install -U pip'
- '%PYTHON% -m pip install -r misc\requirements\requirements-tox.txt'
- 'set PATH=%PATH%;C:\Python36'
- 'set PATH=C:\Python36-x64;%PATH'
test_script:
- '%PYTHON% -m tox -e %TESTENV%'

12
.flake8
View File

@@ -32,7 +32,7 @@ exclude = .*,__pycache__,resources.py
# D403: First word of the first line should be properly capitalized
# (false-positives)
# D413: Missing blank line after last section (not in pep257?)
# A003: Builtin name for class attribute (needed for attrs)
# A003: Builtin name for class attribute (needed for overridden methods)
ignore =
B001,B008,B305,
E128,E226,E265,E501,E402,E266,E722,E731,
@@ -44,11 +44,11 @@ ignore =
min-version = 3.4.0
max-complexity = 12
per-file-ignores =
tests/*/test_*.py : D100,D101,D401
tests/unit/browser/test_history.py : N806
tests/helpers/fixtures.py : N806
tests/unit/browser/webkit/http/test_content_disposition.py : D400
scripts/dev/ci/appveyor_install.py : FI53
/tests/**/*.py : D100,D101,D401
/tests/unit/browser/test_history.py : N806
/tests/helpers/fixtures.py : N806
/tests/unit/browser/webkit/http/test_content_disposition.py : D400
/scripts/dev/ci/appveyor_install.py : FI53
copyright-check = True
copyright-regexp = # Copyright [\d-]+ .*
copyright-min-file-size = 110

View File

@@ -1,3 +1,8 @@
IMPORTANT: I'm currently (July 2018) more busy than usual until September,
because of exams coming up. Review of non-trivial pull requests will thus be
delayed until then. If you're reading this note after mid-September, please
open an issue.
- Before you start to work on something, please leave a comment on the relevant
issue (or open one). This makes sure there is no duplicate work done.

1
.gitignore vendored
View File

@@ -41,3 +41,4 @@ TODO
/scripts/testbrowser/cpp/webengine/testbrowser
/scripts/testbrowser/cpp/webengine/.qmake.stash
/scripts/dev/pylint_checkers/qute_pylint.egg-info
/misc/file_version_info.txt

View File

@@ -18,20 +18,19 @@ matrix:
python: 3.5
env: TESTENV=py35-pyqt571
- os: linux
env: TESTENV=py36-pyqt59-cov
env: TESTENV=py36-pyqt59
- os: linux
env: TESTENV=py36-pyqt510
# We need a newer Xvfb as a WORKAROUND for:
# https://bugreports.qt.io/browse/QTBUG-64928
- os: linux
env: TESTENV=py36-pyqt511-cov
# https://github.com/travis-ci/travis-ci/issues/9069
- os: linux
python: 3.7
sudo: required
addons:
apt:
sources:
- sourceline: "deb http://us.archive.ubuntu.com/ubuntu/ xenial main universe"
packages:
- xvfb
dist: xenial
env: TESTENV=py37-pyqt511
- os: osx
env: TESTENV=py36 OSX=sierra
env: TESTENV=py37 OSX=sierra
osx_image: xcode9.2
language: generic
# https://github.com/qutebrowser/qutebrowser/issues/2013

View File

@@ -1,6 +1,5 @@
recursive-include qutebrowser *.py
recursive-include qutebrowser/img *.svg *.png
recursive-include qutebrowser/test *.py
recursive-include qutebrowser/javascript *.js
graft qutebrowser/html
graft qutebrowser/3rdparty
@@ -8,6 +7,7 @@ graft icons
graft doc/img
graft misc/apparmor
graft misc/userscripts
graft misc/requirements
recursive-include scripts *.py *.sh *.js
include qutebrowser/utils/testfile
include qutebrowser/git-commit-id
@@ -26,22 +26,18 @@ prune scripts/dev
prune scripts/testbrowser/cpp
prune .github
exclude scripts/asciidoc2html.py
exclude doc/notes
recursive-exclude doc *.asciidoc
include doc/qutebrowser.1.asciidoc
include doc/changelog.asciidoc
prune tests
prune qutebrowser/3rdparty
prune misc/requirements
prune misc/docker
exclude pytest.ini
exclude qutebrowser.rcc
exclude qutebrowser/javascript/.eslintrc.yaml
exclude qutebrowser/javascript/.eslintignore
exclude doc/help
exclude .*
exclude misc/appveyor_install.py
exclude misc/qutebrowser.spec
exclude misc/qutebrowser.nsi
exclude misc/qutebrowser.rcc
global-exclude __pycache__ *.pyc *.pyo

View File

@@ -9,8 +9,6 @@ qutebrowser
// QUTE_WEB_HIDE
image:icons/qutebrowser-64x64.png[qutebrowser logo] *A keyboard-driven, vim-like browser based on PyQt5 and Qt.*
image:https://img.shields.io/pypi/l/qutebrowser.svg?style=flat["license badge",link="https://github.com/qutebrowser/qutebrowser/blob/master/LICENSE"]
image:https://img.shields.io/pypi/v/qutebrowser.svg?style=flat["version badge",link="https://pypi.python.org/pypi/qutebrowser/"]
image:https://travis-ci.org/qutebrowser/qutebrowser.svg?branch=master["Build Status", link="https://travis-ci.org/qutebrowser/qutebrowser"]
image:https://ci.appveyor.com/api/projects/status/5pyauww2k68bbow2/branch/master?svg=true["AppVeyor build status", link="https://ci.appveyor.com/project/qutebrowser/qutebrowser"]
image:https://codecov.io/github/qutebrowser/qutebrowser/coverage.svg?branch=master["coverage badge",link="https://codecov.io/github/qutebrowser/qutebrowser?branch=master"]
@@ -99,7 +97,7 @@ Requirements
The following software and libraries are required to run qutebrowser:
* http://www.python.org/[Python] 3.5 or newer (3.6 recommended)
* http://qt.io/[Qt] 5.7.1 or newer with the following modules:
* http://qt.io/[Qt] 5.7.1 or newer (5.11.1 recommended) with the following modules:
- QtCore / qtbase
- QtQuick (part of qtbase in some distributions)
- QtSQL (part of qtbase in some distributions)
@@ -109,7 +107,7 @@ The following software and libraries are required to run qutebrowser:
link:https://github.com/annulen/webkit/wiki[updated fork] (5.212) is
supported
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.7.0 or newer
(5.9.2 recommended) for Python 3
(5.11.2 recommended) for Python 3
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
* http://fdik.org/pyPEG/[pyPEG2]
* http://jinja.pocoo.org/[jinja2]

View File

@@ -15,6 +15,286 @@ breaking changes (such as renamed commands) can happen in minor releases.
// `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities.
v1.5.0 (unreleased)
-------------------
Fixed
~~~~~
- Rare crash when an error occurs in downloads.
v1.4.0
------
Added
~~~~~
- Support for the bundled `sip` module in PyQt 5.11 and other changes in
Qt/PyQt 5.11.x.
- New `--debug-flag log-requests` to log requests to the debug log for
debugging.
- New `--first` flag for `:hint` (bound to `gi` for inputs) which automatically
selects the first hint.
- New `input.escape_quits_reporter` setting which can be used to avoid
accidentally quitting the crash reporter when pressing escape.
- New `qute-lastpass` userscript which uses the LastPass CLI to fill passwords.
- The Makefile now installs a `/usr/share/metainfo/qutebrowser.appdata.xml` file.
- QtWebEngine: Support for printing from webpages via `window.print`.
- QtWebEngine: Support for muting tabs:
* New `{audio}` field for `window.title_format` and `tabs.title.format` which
displays `[M]`/`[A]` for muted/recently audible tabs.
* New `:tab-mute` command (bound to `<Alt-m>`) to mute/unmute a tab.
- QtWebEngine: Support for `content.cookies.accept` with third-party cookies
blocked by default (requires Qt 5.11).
- QtWebEngine: New settings:
* Support for requesting persistent storage via
`navigator.webkitPersistentStorage.requestQuota` with a new
`content.persistent_storage` setting (requires Qt 5.11).
This setting also supports URL patterns.
* Support for registering custom protocol handlers via
`navigator.registerProtocolHandler` with a new
`content.register_protocol_handler` setting (requires Qt 5.11).
This setting also supports URL patterns.
* Support for WebRTC screen sharing with a new `content.desktop_capture`
setting (requires Qt 5.10).
This setting also supports URL patterns.
* New `content.autoplay` setting to enable/disable automatic video playback
(requires Qt 5.10).
* New `content.webrtc_public_interfaces_only` setting to only expose public
interfaces over WebRTC (requires Qt 5.9.2 or 5.11).
* New `content.canvas_reading` setting to disable reading from canvas
elements.
Changed
~~~~~~~
- The following settings now support URL patterns:
* `content.headers.do_not_track`
* `content.headers.custom`
* `content.headers.accept_language`
* `content.headers.user_agent`
* `content.ssl_strict`
* `content.geolocation`
* `content.notifications`
* `content.media_capture`
- The Windows/macOS releases now bundle Qt 5.11.1 which is based on
Chromium 65.0.3325.151 with security fixes up to Chromium 67.0.3396.87.
- New short flags for commandline arguments: `-B` and `-T` for `--basedir` and
`--temp-basedir`; `-d` and `-D` for `--debug` and `--debug-flag`.
- Deleting history items via `:history-clear` or `:completion-item-del` now
also removes that URL from QtWebEngine's visited links.
- There's now completion for commands taking a variable count of arguments
(like `:config-cycle`).
- QtWebEngine: On Qt 5.11.1, no reloads are needed anymore when switching
between pages with changed settings (e.g. `content.javascript.enabled`).
- The `qt.force_software_rendering` setting changed from a boolean to taking
different values (`software-opengl`, `qt-quick` and `chromium`) for different
kinds of software rendering workarounds.
- On Qt 5.11, using wayland with QtWebEngine is now possible when using
software rendering.
- GreaseMonkey scripts now get their own global scope (based on the page's
one), which allows scripts like OneeChan to work.
- Rapid hinting is now supported with the `yank` and `yank-primary` targets,
copying newline-separated links.
- QtWebEngine: On Qt 5.11, the developer tools (inspector) can now be used
securely and without requiring the `--enable-webengine-inspector` option.
- The `<Enter>` key (`:follow-selected`) now follows the currently focused
element if there's no selection.
- The `--logfilter` argument now can be prepended with an exclamation mark
(e.g. `--logfilter '!init,destroy'`) to invert the filter.
- `:view-source` now has a `--pygments` flag which uses the "old" way of
rendering sources even with QtWebEngine.
- Improved error messages when a setting needs a newer Qt version.
- QtWebEngine: Various improvements to make the cursor more visible in caret
browsing.
- When a prompt is opened in insert/passthrough mode, the mode is restored
after closing the prompt.
- On Qt 5.10 or newer, dictionaries are now read from the qutebrowser data
directory (e.g. `~/.local/share/qutebrowser`) instead of `/usr/share/qt`.
Existing dictionaries are copied over.
- If an error while parsing `~/.netrc` occurs, the cause of the error is now
logged.
- On Qt 5.9 or newer, certificate errors now show Chromium's detailed error
page.
- Greasemonkey scripts now support a "@qute-js-world" tag to run them in a
different JavaScript context.
Fixed
~~~~~
- Various subtle keyboard focus issues.
- The security fix in v1.3.3 caused URLs with ampersands
(`www.example.com?one=1&two=2`) to send the wrong arguments when clicked on
the `qute://history` page.
- Crash when opening a PDF page with PDF.js enabled (on QtWebKit), but no
PDF.js installed.
- Crash when closing a tab shortly after opening it.
Removed
~~~~~~~
- No prebuilt binaries for 32-bit Windows are supplied anymore. This is due to
Qt removing QtWebEngine support for those upstream. It might be possible to
distribute 32-bit binaries again with Qt 5.12 in December, but that will only
happen if it turns out enough people actually need 32-bit support.
- `:tab-detach` which has been deprecated in v1.1.0 has been removed.
- The `content.developer_extras` setting got removed. On QtWebKit, developer
extras are now automatically enabled when opening the inspector.
v1.3.3
------
Security
~~~~~~~~
- An XSS vulnerability on the `qute://history` page allowed websites to inject
HTML into the page via a crafted title tag. This could allow them to steal
your browsing history. If you're currently unable to upgrade, avoid using
`:history`. A CVE request for this issue is pending, see
https://github.com/qutebrowser/qutebrowser/issues/4011[#4011] for updates.
Fixed
~~~~~
- Crash in a workaround for a Qt 5.11 bug in rare circumstances.
- Workaround for a Qt bug which preserves searches between page loads.
- In v1.3.2 a dependency on the `PyQt5.QtQuickWidgets` module was accidentally
introduced. Since that module isn't packaged everywhere, it's been removed
again.
v1.3.2
------
Fixed
~~~~~
- QtWebEngine: Improved workaround for a bug in Qt 5.11 where only the
top/bottom half of the window is used.
- QtWebEngine: Work around a bug in Qt 5.11 where an endless loading-loop is
triggered when clicking a link with an unknown scheme.
- QtWebEngine: When switching between pages with changed settings, less
unnecessary reloads are done now.
- QtWebEngine: It's now possible to open external links such as `magnet://` or
`mailto:` via hints.
v1.3.1
------
Fixed
~~~~~
- Work around a bug in Qt 5.11 where only the top/bottom half of the window is used.
This workaround is incomplete, but fixes the majority of the cases where this happens.
- Work around keyboard focus issues with Qt 5.11.
- Work around an issue in Qt 5.11 where e.g. activating JavaScript per-domain
needed a manual reload in some cases.
- Don't crash when a ² key is pressed (e.g. on AZERTY keyboards).
- Don't crash when a tab is opened and quickly closed again.
v1.3.0
------
Added
~~~~~
- New `:scroll-to-anchor` command to scroll to an anchor in the document.
- New `url.open_base_url` option to open the base URL of a searchengine when no
search term is given.
- New `tabs.min_width` setting to configure the minimal width for tabs.
- New userscripts:
* `getbib` to download bibtex information for DOIs on a page.
* `qute-keepass` to get passwords from KeePassX.
Changed
~~~~~~~
- QtWebEngine: Support for JavaScript Shared Web Workers have been disabled on
Qt versions older than 5.11 because of security issues in in Chromium.
You can get the same effect in earlier versions via
`:set qt.args ['disable-shared-workers']`. An equivalent workaround is also
contained in Qt 5.9.5 and 5.10.1.
- The file dialog for downloads now has basic tab completion based on the
entered text.
- `:version` now shows OS information for POSIX OS other than Linux/macOS.
- When there's an error inserting the text from an external editor, a backup
file is now saved.
- The `window.hide_wayland_decoration` setting got renamed to
`window.hide_decoration` and now also works outside of wayland.
- The `tabs.favicons.show` setting now can take three values: `'always'` (was
`True`), `'never'` (was `False`) and `'pinned'` (to only show favicons for
pinned tabs).
- Hover tooltips on tabs now always show the webpage's title.
- The default value for `content.host_blocking.lists` was changed to only
include https://github.com/StevenBlack/hosts[Steven Black's hosts-list] which
combines various sources.
- Error messages when trying to wrap when `tabs.wrap` is `False` are now logged
to debug instead of messages.
Fixed
~~~~~
- Using hints before a page is fully loaded is now possible again.
- Selecting hints with the number keypad now works again.
- Tab titles for tabs loaded from sessions should now really be correct instead
of showing the URL.
- Loading URLs with customized settings from a session now avoids an additional
reload.
- The window icon and title now get set correctly again.
- The `tabs.switching_delay` setting now has a correct maximum value limit set.
- The `taskadd` script now works properly when there's multi-line output.
- QtWebEngine: Worked around issues with GreaseMonkey/stylesheets not being
loaded correctly in some situations.
- The statusbar now more closely reflects the caret mode state.
- The icon on Windows should now be displayed in a higher resolution.
- The QtWebEngine development tools (inspector) now also work when JavaScript is
disabled globally.
- Building `.exe` files now works when `upx` is installed on the system.
- The keyhint widget now shows the correct text for chained modifiers.
- Loading GreaseMonkey scripts now also works with Jinja2 2.8 (e.g. on Debian
Stable).
- Adding styles with GreaseMonkey on fast sites now works properly.
- Window ID 0 is now excluded properly from `:tab-take` completion.
- A rare crash when cancelling a download has been fixed.
- The Makefile (intended for packagers) now supports `PREFIX` properly.
- The workaround for a black window with Nvidia graphics is now enabled on
non-Linux systems (like FreeBSD) as well.
- Initial support for Qt 5.11.
- Checking for a new version after sending a crash report now works properly
again.
- `@match` in Greasemonkey scripts now more closely matches the proper pattern
syntax.
- Searching via `/` or `?` now doesn't handle any characters in a special way.
- Fixed crash when trying to retry some failed downloads on QtWebEngine.
- An invalid spellcheck dictionary filename now doesn't crash anymore.
- When no spellcheck dictionaries are configured, it's now disabled internally.
This works around an issue with entering special characters on Facebook
messenger.
- The macOS release now should work again on macOS 10.11 and newer.
v1.2.1
------
Fixed
~~~~~
- qutebrowser now starts properly when the PyQt5 QOpenGLFunctions package wasn't
found.
- The keybinding cheatsheet on the quickstart page is now loaded from a local
`qute://` URL again.
- With "tox -e mkvenv-pypi", PyQt 5.10.0 is used again instead of Qt 5.10.1,
because of an issue with Qt 5.10.1 which causes qutebrowser to fail to start
("Could not find QtWebEngineProcess").
- Unbinding keys which were bound in older qutebrowser versions now doesn't
crash anymore.
- Fixed a crash when reloading a page which wasn't fully loaded with v1.2.0
- Keys on the numeric keypad now fall back to the same bindings without `Num+`
if no `Num+` binding was found.
- Fixed hinting on some pages with Qt < 5.10.
- Titles are now displayed correctly again for tabs which are cloned or loaded
from sessions.
- Shortcuts now correctly use `Ctrl` instead of `Command` on macOS again.
v1.2.0
------

View File

@@ -5,6 +5,11 @@ The Compiler <mail@qutebrowser.org>
:data-uri:
:toc:
IMPORTANT: I'm currently (July 2018) more busy than usual until September,
because of exams coming up. Review of non-trivial pull requests will thus be
delayed until then. If you're reading this note after mid-September, please
open an issue.
I `&lt;3` footnote:[Of course, that says `<3` in HTML.] contributors!
This document contains guidelines for contributing to qutebrowser, as well as
@@ -85,6 +90,16 @@ git format-patch origin/master <1>
<1> Replace `master` by the branch your work was based on, e.g.,
`origin/develop`.
Running qutebrowser
-------------------
After link:install.asciidoc#tox[installing qutebrowser via tox], you can run
`.venv/bin/qutebrowser --debug --temp-basedir` to test your changes with debug
logging enabled and without affecting existing running instances.
Alternatively, you can install qutebrowser's dependencies system-wide and run
`python3 -m qutebrowser --debug --temp-basedir`.
Useful utilities
----------------
@@ -188,8 +203,8 @@ There are some useful functions for debugging in the `qutebrowser.utils.debug`
module.
When starting qutebrowser with the `--debug` flag, you also get useful debug
logs. You can add +--logfilter _category[,category,...]_+ to restrict logging
to the given categories.
logs. You can add +--logfilter _[!]category[,category,...]_+ to restrict
logging to the given categories.
With `--debug` there are also some additional +debug-_*_+ commands available,
for example `:debug-all-objects` and `:debug-all-widgets` which print a list of
@@ -566,6 +581,23 @@ can be useful for debugging:
- chrome://gpuclean/ (crashes the current renderer process!)
- chrome://ppapiflashcrash/
- chrome://ppapiflashhang/
- chrome://quota-internals/ (Qt 5.11)
- chrome://taskscheduler-internals/ (Qt 5.11)
- chrome://sandbox/ (Qt 5.11, Linux only)
QtWebEngine internals
~~~~~~~~~~~~~~~~~~~~~
This is mostly useful for qutebrowser maintainers to work around issues in Qt - if you don't understand it, don't worry, just ignore it.
The hierarchy of widgets when QtWebEngine is involved looks like this:
- qutebrowser has a `WebEngineTab` object, which is its abstraction over QtWebKit/QtWebEngine.
- The `WebEngineTab` has a `_widget` attribute, which is the https://doc.qt.io/qt-5/qwebengineview.html[QWebEngineView]
- That view has a https://doc.qt.io/qt-5/qwebenginepage.html[QWebEnginePage] for everything which doesn't require rendering.
- The view also has a layout with exactly one element (which also is its `focusProxy()`)
- That element is the http://code.qt.io/cgit/qt/qtwebengine.git/tree/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp[RenderWidgetHostViewQtDelegateWidget] (it inherits https://doc.qt.io/qt-5/qquickwidget.html[QQuickWidget]) - also often referred to as RWHV or RWHVQDW. It can be obtained via `sip.cast(tab._widget.focusProxy(), QQuickWidget)`.
- Calling `rootObject()` on that gives us the https://doc.qt.io/qt-5/qquickitem.html[QQuickItem] where Chromium renders into (?). With it, we can do things like `.setRotation(20)`.
Style conventions
-----------------
@@ -662,18 +694,17 @@ New PyQt release
~~~~~~~~~~~~~~~~
* See above.
* Install new PyQt in Windows VM (32- and 64-bit).
* Download new installer and update PyQt installer path in `ci_install.py`.
* Update `tox.ini`/`.travis.yml`/`.appveyor.yml` to test new versions.
qutebrowser release
~~~~~~~~~~~~~~~~~~~
* Make sure there are no unstaged changes and the tests are green.
* Make sure all issues with the related milestone are closed.
* Run `x=... y=...` to set the respective shell variables.
* Adjust `__version_info__` in `qutebrowser/__init__.py`.
* Update changelog (remove *(unreleased)*).
* Adjust `__version_info__` in `qutebrowser/__init__.py`.
* Commit.
* Create annotated git tag (`git tag -s "v1.$x.$y" -m "Release v1.$x.$y"`).
@@ -683,9 +714,11 @@ qutebrowser release
* Mark the milestone at https://github.com/qutebrowser/qutebrowser/milestones
as closed.
* Linux: Run `git checkout v1.$x.$y && python3 scripts/dev/build_release.py --upload v1.$x.$y`.
* Windows: Run `git checkout v1.X.Y; C:\Python36-32\python scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py --upload v1.X.Y` (replace X/Y by hand).
* macOS: Run `git checkout v1.X.Y && python3 scripts/dev/build_release.py --upload v1.X.Y` (replace X/Y by hand).
* On server: Run `python3 scripts/dev/download_release.py v1.X.Y` (replace X/Y by hand).
* Linux: Run `git checkout v1.$x.$y && ./.venv/bin/python3 scripts/dev/build_release.py --upload v1.$x.$y`.
* Windows: Run `git checkout v1.X.Y; py -3.6 scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py --upload v1.X.Y` (replace X/Y by hand).
* macOS: Run `pyenv shell 3.6.6 && git checkout v1.X.Y && python3 scripts/dev/build_release.py --upload v1.X.Y` (replace X/Y by hand).
* On server:
- Run `python3 scripts/dev/download_release.py v1.X.Y` (replace X/Y by hand).
- Run `git pull github master && sudo python3 scripts/asciidoc2html.py --website /srv/http/qutebrowser`
* Update `qutebrowser-git` PKGBUILD if dependencies/install changed.
* Announce to qutebrowser and qutebrowser-announce mailinglist.

View File

@@ -212,6 +212,37 @@ Why takes it longer to open an URL in qutebrowser than in chromium?::
qutebrowser if it is not running already. Also check if you want
to use webengine as backend in line 17 and change it to your
needs.
How do I make qutebrowser use greasemonkey scripts?::
There is currently no UI elements to handle managing greasemonkey scripts.
All management of what scripts are installed or disabled is done in the
filesystem by you. qutebrowser reads all files that have an extension of
`.js` from the `<data>/greasemonkey/` folder and attempts to load them.
Where `<data>` is the qutebrowser data directory shown in the `Paths`
section of the page displayed by `:version`. If you want to disable a
script just rename it, for example, to have `.disabled` on the end, after
the `.js` extension. To reload scripts from that directory run the command
`:greasemonkey-reload`.
+
Troubleshooting: to check that your script is being loaded when
`:greasemonkey-reload` runs you can start qutebrowser with the arguments
`--debug --logfilter greasemonkey,js` and check the messages on the
program's standard output for errors parsing or loading your script.
You may also see javascript errors if your script is expecting an environment
that we fail to provide.
+
Note that there are some missing features which you may run into:
. Some scripts expect `GM_xmlhttpRequest` to ignore Cross Origin Resource
Sharing restrictions, this is currently not supported, so scripts making
requests to third party sites will often fail to function correctly.
. If your backend is a QtWebEngine version 5.8, 5.9 or 5.10 then regular
expressions are not supported in `@include` or `@exclude` rules. If your
script uses them you can re-write them to use glob expressions or convert
them to `@match` rules.
See https://wiki.greasespot.net/Metadata_Block[the wiki] for more info.
. Any greasemonkey API function to do with adding UI elements is not currently
supported. That means context menu extentensions and background pages.
== Troubleshooting

View File

@@ -93,6 +93,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<scroll,scroll>>|Scroll the current tab in the given direction.
|<<scroll-page,scroll-page>>|Scroll the frame page-wise.
|<<scroll-px,scroll-px>>|Scroll the current tab by 'count * dx/dy' pixels.
|<<scroll-to-anchor,scroll-to-anchor>>|Scroll to the given anchor in the document.
|<<scroll-to-perc,scroll-to-perc>>|Scroll to a specific percentage of the page.
|<<search,search>>|Search for a text on the current page. With no text, clear results.
|<<search-next,search-next>>|Continue the search to the ([count]th) next term.
@@ -110,6 +111,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<tab-focus,tab-focus>>|Select the tab given as argument/[count].
|<<tab-give,tab-give>>|Give the current tab to a new or existing window if win_id given.
|<<tab-move,tab-move>>|Move the current tab according to the argument and [count].
|<<tab-mute,tab-mute>>|Mute/Unmute the current/[count]th tab.
|<<tab-next,tab-next>>|Switch to the next tab, or switch [count] tabs forward.
|<<tab-only,tab-only>>|Close all tabs except for the current one.
|<<tab-pin,tab-pin>>|Pin/Unpin the current/[count]th tab.
@@ -529,7 +531,7 @@ Show help about a command or setting.
[[hint]]
=== hint
Syntax: +:hint [*--mode* 'mode'] [*--add-history*] [*--rapid*]
Syntax: +:hint [*--mode* 'mode'] [*--add-history*] [*--rapid*] [*--first*]
['group'] ['target'] ['args' ['args' ...]]+
Start hinting.
@@ -599,6 +601,7 @@ Start hinting.
`tab` (with `tabs.background_tabs=true`), `tab-bg`,
`window`, `run`, `hover`, `userscript` and `spawn`.
* +*-f*+, +*--first*+: Click the first hinted element without prompting.
==== note
* This command does not split arguments after the last argument and handles quotes literally.
@@ -1024,6 +1027,15 @@ Scroll the current tab by 'count * dx/dy' pixels.
==== count
multiplier
[[scroll-to-anchor]]
=== scroll-to-anchor
Syntax: +:scroll-to-anchor 'name'+
Scroll to the given anchor in the document.
==== positional arguments
* +'name'+: The anchor to scroll to.
[[scroll-to-perc]]
=== scroll-to-perc
Syntax: +:scroll-to-perc [*--horizontal*] ['perc']+
@@ -1273,6 +1285,13 @@ If moving relatively: Offset. If moving absolutely: New position (default: 0). T
overrides the index argument, if given.
[[tab-mute]]
=== tab-mute
Mute/Unmute the current/[count]th tab.
==== count
The tab index to mute or unmute
[[tab-next]]
=== tab-next
Switch to the next tab, or switch [count] tabs forward.
@@ -1346,12 +1365,16 @@ Show version information.
[[view-source]]
=== view-source
Syntax: +:view-source [*--edit*]+
Syntax: +:view-source [*--edit*] [*--pygments*]+
Show the source of the current page in a new tab.
==== optional arguments
* +*-e*+, +*--edit*+: Edit the source in the editor instead of opening a tab.
* +*-p*+, +*--pygments*+: Use pygments to generate the view. This is always the case for QtWebKit. For QtWebEngine it may display
slightly different source.
Some JavaScript processing may be applied.
[[window-only]]
=== window-only

View File

@@ -3,44 +3,28 @@ Configuring qutebrowser
IMPORTANT: qutebrowser's configuration system was completely rewritten in
September 2017. This information is not applicable to older releases, and older
information elsewhere might be outdated. **If you had an old configuration
around and upgraded, this page will automatically open once**. To view it at a
later time, use the `:help` command.
information elsewhere might be outdated.
Migrating older configurations
------------------------------
qutebrowser's config files
--------------------------
qutebrowser does no automatic migration for the new configuration. However,
there's a special link:qute://configdiff/old[configdiff] page
(`qute://configdiff/old`) in qutebrowser, which will show you the changes you
did in your old configuration, compared to the old defaults.
qutebrowser releases before v1.0.0 had a `qutebrowser.conf` and `keys.conf`
file. Those are not used anymore since that release - see
<<migrating,"Migrating older configurations">> for information on how to
migrate to the new config.
Other changes in default settings:
When using `:set` and `:bind`, changes are saved to an `autoconfig.yml` file
automatically. If you don't want to have a config file which is curated by
hand, you can simply use those - see
<<autoconfig,"Configuring qutebrowser via the user interface">> for details.
- In v1.1.x and newer, `<Up>` and `<Down>` navigate through command history
if no text was entered yet.
With v1.0.x, they always navigate through command history instead of selecting
completion items. Use `<Tab>`/`<Shift-Tab>` to cycle through the completion
instead.
You can get back the old behavior by doing:
+
----
:bind -m command <Up> completion-item-focus prev
:bind -m command <Down> completion-item-focus next
----
+
or always navigate through command history with
+
----
:bind -m command <Up> command-history-prev
:bind -m command <Down> command-history-next
----
- The default for `completion.web_history_max_items` is now set to `-1`, showing
an unlimited number of items in the completion for `:open` as the new
sqlite-based completion is much faster. If the `:open` completion is too slow
on your machine, set an appropriate limit again.
For more advanced configuration, you can write a `config.py` file - see
<<configpy,"Configuring qutebrowser via config.py">>. As soon as a `config.py`
exists, the `autoconfig.yml` file **is not read anymore** by default. You need
to <<configpy-autoconfig,load it by hand>> if you want settings done via
`:set`/`:bind` to still persist.
[[autoconfig]]
Configuring qutebrowser via the user interface
----------------------------------------------
@@ -88,6 +72,7 @@ link:commands.html#config-clear[`:config-clear`] to reset the entire configurati
and link:commands.html#config-cycle[`:config-cycle`] to cycle a setting between
different values.
[[configpy]]
Configuring qutebrowser via config.py
-------------------------------------
@@ -239,13 +224,14 @@ config.bind(',v', 'spawn mpv {url}')
To suppress loading of any default keybindings, you can set
`c.bindings.default = {}`.
[[configpy-autoconfig]]
Loading `autoconfig.yml`
~~~~~~~~~~~~~~~~~~~~~~~~
By default, all customization done via `:set`, `:bind` and `:unbind` is
temporary as soon as a `config.py` exists. The settings done that way are always
saved in the `autoconfig.yml` file, but you'll need to explicitly load it in
your `config.py` by doing:
All customization done via the UI (`:set`, `:bind` and `:unbind`) is
stored in the `autoconfig.yml` file, which is not loaded automatically as soon
as a `config.py` exists. If you want those settings to be loaded, you'll need to
explicitly load the `autoconfig.yml` file in your `config.py` by doing:
.config.py:
[source,python]
@@ -404,6 +390,12 @@ xresources = read_xresources('*')
c.colors.statusbar.normal.bg = xresources['*.background']
----
Pre-built colorschemes
^^^^^^^^^^^^^^^^^^^^^^
- A collection of https://github.com/chriskempson/base16[base16] color-schemes can be found in https://github.com/theova/base16-qutebrowser[base16-qutebrowser] and used with https://github.com/AuditeMarlow/base16-manager[base16-manager].
- Two implementations of the https://github.com/arcticicestudio/nord[Nord] colorscheme for qutebrowser exist: https://github.com/Linuus/nord-qutebrowser[Linuus], https://github.com/KnownAsDon/QuteBrowser-Nord-Theme[KnownAsDon]
Avoiding flake8 errors
^^^^^^^^^^^^^^^^^^^^^^
@@ -429,3 +421,38 @@ from qutebrowser.config.config import ConfigContainer # noqa: F401
config = config # type: ConfigAPI # noqa: F821 pylint: disable=E0602,C0103
c = c # type: ConfigContainer # noqa: F821 pylint: disable=E0602,C0103
----
[[migrating]]
Migrating older configurations
------------------------------
qutebrowser does no automatic migration for the new configuration. However,
there's a special link:qute://configdiff/old[configdiff] page
(`qute://configdiff/old`) in qutebrowser, which will show you the changes you
did in your old configuration, compared to the old defaults.
Other changes in default settings:
- In v1.1.x and newer, `<Up>` and `<Down>` navigate through command history
if no text was entered yet.
With v1.0.x, they always navigate through command history instead of selecting
completion items. Use `<Tab>`/`<Shift-Tab>` to cycle through the completion
instead.
You can get back the old behavior by doing:
+
----
:bind -m command <Up> completion-item-focus prev
:bind -m command <Down> completion-item-focus next
----
+
or always navigate through command history with
+
----
:bind -m command <Up> command-history-prev
:bind -m command <Down> command-history-next
----
- The default for `completion.web_history_max_items` is now set to `-1`, showing
an unlimited number of items in the completion for `:open` as the new
sqlite-based completion is much faster. If the `:open` completion is too slow
on your machine, set an appropriate limit again.

View File

@@ -109,13 +109,15 @@
|<<completion.use_best_match,completion.use_best_match>>|Execute the best-matching command on a partial match.
|<<completion.web_history_max_items,completion.web_history_max_items>>|Number of URLs to show in the web history.
|<<confirm_quit,confirm_quit>>|Require a confirmation before quitting the application.
|<<content.autoplay,content.autoplay>>|Automatically start playing `<video>` elements.
|<<content.cache.appcache,content.cache.appcache>>|Enable support for the HTML 5 web application cache feature.
|<<content.cache.maximum_pages,content.cache.maximum_pages>>|Maximum number of pages to hold in the global memory page cache.
|<<content.cache.size,content.cache.size>>|Size (in bytes) of the HTTP network cache. Null to use the default value.
|<<content.canvas_reading,content.canvas_reading>>|Allow websites to read canvas elements.
|<<content.cookies.accept,content.cookies.accept>>|Which cookies to accept.
|<<content.cookies.store,content.cookies.store>>|Store cookies.
|<<content.default_encoding,content.default_encoding>>|Default encoding to use for websites.
|<<content.developer_extras,content.developer_extras>>|Enable extra tools for Web developers.
|<<content.desktop_capture,content.desktop_capture>>|Allow websites to share screen content.
|<<content.dns_prefetch,content.dns_prefetch>>|Try to pre-fetch DNS entries to speed up browsing.
|<<content.frame_flattening,content.frame_flattening>>|Expand each subframe to its contents.
|<<content.geolocation,content.geolocation>>|Allow websites to request geolocations.
@@ -144,14 +146,17 @@
|<<content.netrc_file,content.netrc_file>>|Netrc-file for HTTP authentication.
|<<content.notifications,content.notifications>>|Allow websites to show notifications.
|<<content.pdfjs,content.pdfjs>>|Allow pdf.js to view PDF files in the browser.
|<<content.persistent_storage,content.persistent_storage>>|Allow websites to request persistent storage quota via `navigator.webkitPersistentStorage.requestQuota`.
|<<content.plugins,content.plugins>>|Enable plugins in Web pages.
|<<content.print_element_backgrounds,content.print_element_backgrounds>>|Draw the background color and images also when the page is printed.
|<<content.private_browsing,content.private_browsing>>|Open new windows in private browsing mode which does not record visited pages.
|<<content.proxy,content.proxy>>|Proxy to use.
|<<content.proxy_dns_requests,content.proxy_dns_requests>>|Send DNS requests over the configured proxy.
|<<content.register_protocol_handler,content.register_protocol_handler>>|Allow websites to register protocol handlers via `navigator.registerProtocolHandler`.
|<<content.ssl_strict,content.ssl_strict>>|Validate SSL handshakes.
|<<content.user_stylesheets,content.user_stylesheets>>|List of user stylesheet filenames to use.
|<<content.webgl,content.webgl>>|Enable WebGL.
|<<content.webrtc_public_interfaces_only,content.webrtc_public_interfaces_only>>|Only expose public interfaces via WebRTC.
|<<content.windowed_fullscreen,content.windowed_fullscreen>>|Limit fullscreen to the browser window (does not expand to fill the screen).
|<<content.xss_auditing,content.xss_auditing>>|Monitor load requests for cross-site scripting attempts.
|<<downloads.location.directory,downloads.location.directory>>|Directory to save downloads to.
@@ -200,6 +205,7 @@
|<<hints.scatter,hints.scatter>>|Scatter hint key chains (like Vimium) or not (like dwb).
|<<hints.uppercase,hints.uppercase>>|Make characters in hint strings uppercase.
|<<history_gap_interval,history_gap_interval>>|Maximum time (in minutes) between two history items for them to be considered being from the same browsing session.
|<<input.escape_quits_reporter,input.escape_quits_reporter>>|Allow Escape to quit the crash reporter.
|<<input.forward_unbound_keys,input.forward_unbound_keys>>|Which unbound keys to forward to the webview in normal mode.
|<<input.insert_mode.auto_enter,input.insert_mode.auto_enter>>|Enter insert mode if an editable element is clicked.
|<<input.insert_mode.auto_leave,input.insert_mode.auto_leave>>|Leave insert mode if a non-editable element is clicked.
@@ -236,10 +242,11 @@
|<<tabs.close_mouse_button,tabs.close_mouse_button>>|Mouse button with which to close tabs.
|<<tabs.close_mouse_button_on_bar,tabs.close_mouse_button_on_bar>>|How to behave when the close mouse button is pressed on the tab bar.
|<<tabs.favicons.scale,tabs.favicons.scale>>|Scaling factor for favicons in the tab bar.
|<<tabs.favicons.show,tabs.favicons.show>>|Show favicons in the tab bar.
|<<tabs.favicons.show,tabs.favicons.show>>|When to show favicons in the tab bar.
|<<tabs.indicator.padding,tabs.indicator.padding>>|Padding (in pixels) for tab indicators.
|<<tabs.indicator.width,tabs.indicator.width>>|Width (in pixels) of the progress indicator (0 to disable).
|<<tabs.last_close,tabs.last_close>>|How to behave when the last tab is closed.
|<<tabs.min_width,tabs.min_width>>|Minimum width (in pixels) of tabs (-1 for the default minimum size behavior).
|<<tabs.mode_on_change,tabs.mode_on_change>>|When switching tabs, what input mode is applied.
|<<tabs.mousewheel_switching,tabs.mousewheel_switching>>|Switch between tabs using the mouse wheel.
|<<tabs.new_position.related,tabs.new_position.related>>|Position of new tabs opened from another tab.
@@ -259,10 +266,11 @@
|<<url.auto_search,url.auto_search>>|What search to start when something else than a URL is entered.
|<<url.default_page,url.default_page>>|Page to open if :open -t/-b/-w is used without URL.
|<<url.incdec_segments,url.incdec_segments>>|URL segments where `:navigate increment/decrement` will search for a number.
|<<url.open_base_url,url.open_base_url>>|Open base URL of the searchengine if a searchengine shortcut is invoked without parameters.
|<<url.searchengines,url.searchengines>>|Search engines which can be used via the address bar.
|<<url.start_pages,url.start_pages>>|Page(s) to open at the start.
|<<url.yank_ignored_parameters,url.yank_ignored_parameters>>|URL parameters to strip with `:yank url`.
|<<window.hide_wayland_decoration,window.hide_wayland_decoration>>|Hide the window decoration when using wayland.
|<<window.hide_decoration,window.hide_decoration>>|Hide the window decoration.
|<<window.title_format,window.title_format>>|Format to use for the window title. The same placeholders like for
|<<zoom.default,zoom.default>>|Default zoom level.
|<<zoom.levels,zoom.levels>>|Available zoom levels.
@@ -485,6 +493,7 @@ Default:
* +pass:[&lt;Alt-7&gt;]+: +pass:[tab-focus 7]+
* +pass:[&lt;Alt-8&gt;]+: +pass:[tab-focus 8]+
* +pass:[&lt;Alt-9&gt;]+: +pass:[tab-focus -1]+
* +pass:[&lt;Alt-m&gt;]+: +pass:[tab-mute]+
* +pass:[&lt;Ctrl-A&gt;]+: +pass:[navigate increment]+
* +pass:[&lt;Ctrl-Alt-p&gt;]+: +pass:[print]+
* +pass:[&lt;Ctrl-B&gt;]+: +pass:[scroll-page 0 -1]+
@@ -498,6 +507,7 @@ Default:
* +pass:[&lt;Ctrl-Return&gt;]+: +pass:[follow-selected -t]+
* +pass:[&lt;Ctrl-Shift-N&gt;]+: +pass:[open -p]+
* +pass:[&lt;Ctrl-Shift-T&gt;]+: +pass:[undo]+
* +pass:[&lt;Ctrl-Shift-Tab&gt;]+: +pass:[nop]+
* +pass:[&lt;Ctrl-Shift-W&gt;]+: +pass:[close]+
* +pass:[&lt;Ctrl-T&gt;]+: +pass:[open -t]+
* +pass:[&lt;Ctrl-Tab&gt;]+: +pass:[tab-focus last]+
@@ -560,6 +570,7 @@ Default:
* +pass:[gd]+: +pass:[download]+
* +pass:[gf]+: +pass:[view-source]+
* +pass:[gg]+: +pass:[scroll-to-perc 0]+
* +pass:[gi]+: +pass:[hint inputs --first]+
* +pass:[gl]+: +pass:[tab-move -]+
* +pass:[gm]+: +pass:[tab-move]+
* +pass:[go]+: +pass:[set-cmd-text :open {url:pretty}]+
@@ -1460,6 +1471,19 @@ Default:
- +pass:[never]+
[[content.autoplay]]
=== content.autoplay
Automatically start playing `<video>` elements.
Note this option needs a restart with QtWebEngine on Qt < 5.11.
Type: <<types,Bool>>
Default: +pass:[true]+
On QtWebEngine, this setting requires Qt 5.10 or newer.
On QtWebKit, this setting is unavailable.
[[content.cache.appcache]]
=== content.cache.appcache
Enable support for the HTML 5 web application cache feature.
@@ -1494,6 +1518,18 @@ Type: <<types,Int>>
Default: empty
[[content.canvas_reading]]
=== content.canvas_reading
Allow websites to read canvas elements.
Note this is needed for some websites to work properly.
This setting requires a restart.
Type: <<types,Bool>>
Default: +pass:[true]+
This setting is only available with the QtWebEngine backend.
[[content.cookies.accept]]
=== content.cookies.accept
Which cookies to accept.
@@ -1504,12 +1540,12 @@ Valid values:
* +all+: Accept all cookies.
* +no-3rdparty+: Accept cookies from the same origin only.
* +no-unknown-3rdparty+: Accept cookies from the same origin only, unless a cookie is already set for the domain.
* +no-unknown-3rdparty+: Accept cookies from the same origin only, unless a cookie is already set for the domain. On QtWebEngine, this is the same as no-3rdparty.
* +never+: Don't accept cookies at all.
Default: +pass:[no-3rdparty]+
This setting is only available with the QtWebKit backend.
On QtWebEngine, this setting requires Qt 5.11 or newer.
[[content.cookies.store]]
=== content.cookies.store
@@ -1529,16 +1565,22 @@ Type: <<types,String>>
Default: +pass:[iso-8859-1]+
[[content.developer_extras]]
=== content.developer_extras
Enable extra tools for Web developers.
This needs to be enabled for `:inspector` to work and also adds an _Inspect_ entry to the context menu. For QtWebEngine, see `--enable-webengine-inspector` in `qutebrowser --help` instead.
[[content.desktop_capture]]
=== content.desktop_capture
Allow websites to share screen content.
On Qt < 5.10, a dialog box is always displayed, even if this is set to "true".
Type: <<types,Bool>>
This setting supports URL patterns.
Default: +pass:[false]+
Type: <<types,BoolAsk>>
This setting is only available with the QtWebKit backend.
Valid values:
* +true+
* +false+
* +ask+
Default: +pass:[ask]+
[[content.dns_prefetch]]
=== content.dns_prefetch
@@ -1569,6 +1611,8 @@ This setting is only available with the QtWebKit backend.
=== content.geolocation
Allow websites to request geolocations.
This setting supports URL patterns.
Type: <<types,BoolAsk>>
Valid values:
@@ -1582,6 +1626,9 @@ Default: +pass:[ask]+
[[content.headers.accept_language]]
=== content.headers.accept_language
Value to send in the `Accept-Language` header.
Note that the value read from JavaScript is always the global value.
This setting supports URL patterns.
Type: <<types,String>>
@@ -1591,6 +1638,8 @@ Default: +pass:[en-US,en]+
=== content.headers.custom
Custom headers for qutebrowser HTTP requests.
This setting supports URL patterns.
Type: <<types,Dict>>
Default: empty
@@ -1600,6 +1649,8 @@ Default: empty
Value to send in the `DNT` header.
When this is set to true, qutebrowser asks websites to not track your identity. If set to null, the DNT header is not sent at all.
This setting supports URL patterns.
Type: <<types,Bool>>
Default: +pass:[true]+
@@ -1624,6 +1675,9 @@ This setting is only available with the QtWebKit backend.
[[content.headers.user_agent]]
=== content.headers.user_agent
User agent to send. Unset to send the default.
Note that the value read from JavaScript is always the global value.
This setting supports URL patterns.
Type: <<types,String>>
@@ -1653,11 +1707,7 @@ Type: <<types,List of Url>>
Default:
- +pass:[https://www.malwaredomainlist.com/hostslist/hosts.txt]+
- +pass:[http://someonewhocares.org/hosts/hosts]+
- +pass:[http://winhelp2002.mvps.org/hosts.zip]+
- +pass:[http://malwaredomains.lehigh.edu/files/justdomains.zip]+
- +pass:[https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&amp;mimetype=plaintext]+
- +pass:[https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts]+
[[content.host_blocking.whitelist]]
=== content.host_blocking.whitelist
@@ -1807,6 +1857,8 @@ Default: +pass:[true]+
=== content.media_capture
Allow websites to record audio/video.
This setting supports URL patterns.
Type: <<types,BoolAsk>>
Valid values:
@@ -1832,6 +1884,8 @@ Default: empty
=== content.notifications
Allow websites to show notifications.
This setting supports URL patterns.
Type: <<types,BoolAsk>>
Valid values:
@@ -1855,6 +1909,26 @@ Default: +pass:[false]+
This setting is only available with the QtWebKit backend.
[[content.persistent_storage]]
=== content.persistent_storage
Allow websites to request persistent storage quota via `navigator.webkitPersistentStorage.requestQuota`.
This setting supports URL patterns.
Type: <<types,BoolAsk>>
Valid values:
* +true+
* +false+
* +ask+
Default: +pass:[ask]+
On QtWebEngine, this setting requires Qt 5.11 or newer.
On QtWebKit, this setting is unavailable.
[[content.plugins]]
=== content.plugins
Enable plugins in Web pages.
@@ -1909,10 +1983,32 @@ Default: +pass:[true]+
This setting is only available with the QtWebKit backend.
[[content.register_protocol_handler]]
=== content.register_protocol_handler
Allow websites to register protocol handlers via `navigator.registerProtocolHandler`.
This setting supports URL patterns.
Type: <<types,BoolAsk>>
Valid values:
* +true+
* +false+
* +ask+
Default: +pass:[ask]+
On QtWebEngine, this setting requires Qt 5.11 or newer.
On QtWebKit, this setting is unavailable.
[[content.ssl_strict]]
=== content.ssl_strict
Validate SSL handshakes.
This setting supports URL patterns.
Type: <<types,BoolAsk>>
Valid values:
@@ -1941,6 +2037,19 @@ Type: <<types,Bool>>
Default: +pass:[true]+
[[content.webrtc_public_interfaces_only]]
=== content.webrtc_public_interfaces_only
Only expose public interfaces via WebRTC.
On Qt 5.9, this option requires a restart. On Qt 5.10, this option doesn't work at all because of a Qt bug. On Qt >= 5.11, no restart is required.
Type: <<types,Bool>>
Default: +pass:[false]+
On QtWebEngine, this setting requires Qt 5.9.2 or newer.
On QtWebKit, this setting is unavailable.
[[content.windowed_fullscreen]]
=== content.windowed_fullscreen
Limit fullscreen to the browser window (does not expand to fill the screen).
@@ -2387,6 +2496,14 @@ Type: <<types,Int>>
Default: +pass:[30]+
[[input.escape_quits_reporter]]
=== input.escape_quits_reporter
Allow Escape to quit the crash reporter.
Type: <<types,Bool>>
Default: +pass:[true]+
[[input.forward_unbound_keys]]
=== input.forward_unbound_keys
Which unbound keys to forward to the webview in normal mode.
@@ -2579,12 +2696,19 @@ Default: empty
[[qt.force_software_rendering]]
=== qt.force_software_rendering
Force software rendering for QtWebEngine.
This is needed for QtWebEngine to work with Nouveau drivers.
This is needed for QtWebEngine to work with Nouveau drivers and can be useful in other scenarios related to graphic issues.
This setting requires a restart.
Type: <<types,Bool>>
Type: <<types,String>>
Default: +pass:[false]+
Valid values:
* +software-opengl+: Tell LibGL to use a software implementation of GL (`LIBGL_ALWAYS_SOFTWARE` / `QT_XCB_FORCE_SOFTWARE_OPENGL`)
* +qt-quick+: Tell Qt Quick to use a software renderer instead of OpenGL. (`QT_QUICK_BACKEND=software`)
* +chromium+: Tell Chromium to disable GPU support and use Skia software rendering instead. (`--disable-gpu`)
* +none+: Don't force software rendering.
Default: +pass:[none]+
This setting is only available with the QtWebEngine backend.
@@ -2823,11 +2947,17 @@ Default: +pass:[1.0]+
[[tabs.favicons.show]]
=== tabs.favicons.show
Show favicons in the tab bar.
When to show favicons in the tab bar.
Type: <<types,Bool>>
Type: <<types,String>>
Default: +pass:[true]+
Valid values:
* +always+: Always show favicons.
* +never+: Always hide favicons.
* +pinned+: Show favicons only on pinned tabs.
Default: +pass:[always]+
[[tabs.indicator.padding]]
=== tabs.indicator.padding
@@ -2866,6 +2996,16 @@ Valid values:
Default: +pass:[ignore]+
[[tabs.min_width]]
=== tabs.min_width
Minimum width (in pixels) of tabs (-1 for the default minimum size behavior).
This setting only applies when tabs are horizontal.
This setting does not apply to pinned tabs, unless `tabs.pinned.shrink` is False.
Type: <<types,Int>>
Default: +pass:[-1]+
[[tabs.mode_on_change]]
=== tabs.mode_on_change
When switching tabs, what input mode is applied.
@@ -3030,11 +3170,12 @@ The following placeholders are defined:
* `{private}`: Indicates when private mode is enabled.
* `{current_url}`: URL of the current web page.
* `{protocol}`: Protocol (http/https/...) of the current web page.
* `{audio}`: Indicator for audio/mute status.
Type: <<types,FormatString>>
Default: +pass:[{index}: {title}]+
Default: +pass:[{audio}{index}: {title}]+
[[tabs.title.format_pinned]]
=== tabs.title.format_pinned
@@ -3102,6 +3243,14 @@ Default:
- +pass:[path]+
- +pass:[query]+
[[url.open_base_url]]
=== url.open_base_url
Open base URL of the searchengine if a searchengine shortcut is invoked without parameters.
Type: <<types,Bool>>
Default: +pass:[false]+
[[url.searchengines]]
=== url.searchengines
Search engines which can be used via the address bar.
@@ -3137,10 +3286,12 @@ Default:
- +pass:[utm_term]+
- +pass:[utm_content]+
[[window.hide_wayland_decoration]]
=== window.hide_wayland_decoration
Hide the window decoration when using wayland.
This setting requires a restart.
[[window.hide_decoration]]
=== window.hide_decoration
Hide the window decoration.
This setting requires a restart on Wayland.
Type: <<types,Bool>>
@@ -3273,7 +3424,7 @@ See the setting's valid values for more information on allowed values.
|TextAlignment|Alignment of text.
|TimestampTemplate|An strftime-like template for timestamps.
See https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior for reference.
See https://sqlite.org/lang_datefunc.html for reference.
|UniqueCharString|A string which may not contain duplicate chars.
|Url|A URL as a string.
|VerticalPosition|The position of the download bar.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -47,17 +47,27 @@ Debian Stretch / Ubuntu 17.04 and 17.10
Those versions come with QtWebEngine in the repositories. This makes it possible
to install qutebrowser via the Debian package.
Download the https://packages.debian.org/sid/all/qutebrowser/download[qutebrowser] and
https://packages.debian.org/sid/all/python3-pypeg2/download[PyPEG2]
package from the Debian repositories.
You'll need to download three packages:
Install the packages:
- https://packages.debian.org/sid/all/python3-pypeg2/download[PyPEG2] (a library
used by qutebrowser which is not in the earlier repositories)
- https://packages.debian.org/sid/all/qutebrowser/download[qutebrowser] itself
- Either https://packages.debian.org/sid/all/qutebrowser-qtwebengine/download[qutebrowser-qtwebengine]
or https://packages.debian.org/sid/all/qutebrowser-qtwebkit/download[qutebrowser-qtwebkit]
(or both) depending on the backend you want to use. QtWebEngine is the
default/recommended choice.
After downloading, install the packages (make sure to install all the
downloaded qutebrowser deb files in one apt command):
----
# apt install ./python3-pypeg2_*_all.deb
# apt install ./qutebrowser_*_all.deb
# apt install ./qutebrowser*.deb
----
For an update after the initial install, you only need to download/install the
qutebrowser package.
Debian Testing / Ubuntu 18.04
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -372,8 +382,8 @@ $ git clone https://github.com/qutebrowser/qutebrowser.git
$ cd qutebrowser
----
Installing depdendencies (including Qt)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Installing dependencies (including Qt)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Then run tox inside the qutebrowser repository to set up a
https://docs.python.org/3/library/venv.html[virtual environment]:
@@ -382,6 +392,10 @@ https://docs.python.org/3/library/venv.html[virtual environment]:
$ tox -e mkvenv-pypi
----
If your system comes with Python 3.5.3 or older (such as Ubuntu 16.04 LTS), use
`tox -e mkvenv-pypi-old` instead. This installs an older Qt version (5.10) due
to bugs in newer versions.
This installs all needed Python dependencies in a `.venv` subfolder.
This comes with an up-to-date Qt/PyQt including QtWebEngine, but has a few
@@ -417,7 +431,11 @@ Creating a wrapper script
~~~~~~~~~~~~~~~~~~~~~~~~~
Running `tox` does not install a system-wide `qutebrowser` script. You can
launch qutebrowser by doing `.venv/bin/python3 -m qutebrowser`.
launch qutebrowser by doing:
----
.venv/bin/python3 -m qutebrowser
----
You can create a simple wrapper script to start qutebrowser somewhere in your
`$PATH` (e.g. `/usr/local/bin/qutebrowser` or `~/bin/qutebrowser`):

View File

@@ -22,9 +22,9 @@ Basic keybindings to get you started
What to do now
--------------
* View the link:https://qutebrowser.org/img/cheatsheet-big.png[key binding cheatsheet]
* View the link:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png[key binding cheatsheet]
to make yourself familiar with the key bindings: +
image:https://qutebrowser.org/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="https://qutebrowser.org/img/cheatsheet-big.png"]
image:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png"]
* There's also a https://www.shortcutfoo.com/app/dojos/qutebrowser[free training
course] on shortcutfoo for the keybindings - note that you need to be in
insert mode (i) for it to work.

View File

@@ -38,7 +38,7 @@ show it.
*-h*, *--help*::
show this help message and exit
*--basedir* 'BASEDIR'::
*-B* 'BASEDIR', *--basedir* 'BASEDIR'::
Base directory for all storage.
*-V*, *--version*::
@@ -60,7 +60,7 @@ show it.
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.
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. This is not needed anymore since Qt 5.11 where the inspector is always enabled and secure.
=== debug arguments
*-l* '{critical,error,warning,info,debug,vdebug}', *--loglevel* '{critical,error,warning,info,debug,vdebug}'::
@@ -72,7 +72,7 @@ show it.
*--loglines* 'LOGLINES'::
How many lines of the debug log to keep in RAM (-1: unlimited).
*--debug*::
*-d*, *--debug*::
Turn on debugging options.
*--json-logging*::
@@ -87,7 +87,7 @@ show it.
*--nowindow*::
Don't show the main window.
*--temp-basedir*::
*-T*, *--temp-basedir*::
Use a temporary basedir.
*--no-err-windows*::
@@ -99,7 +99,7 @@ show it.
*--qt-flag* 'QT_FLAG'::
Pass an argument to Qt as flag.
*--debug-flag* 'DEBUG_FLAGS'::
*-D* 'DEBUG_FLAGS', *--debug-flag* 'DEBUG_FLAGS'::
Pass name of debugging feature to be turned on.
// QUTE_OPTIONS_END

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -1,25 +1,33 @@
PYTHON = python3
DESTDIR = /
PREFIX = /usr/local
DESTDIR =
ICONSIZES = 16 24 32 48 64 128 256 512
SETUPTOOLSOPTIONS =
ifdef DESTDIR
SETUPTOOLSOPTS = --root="$(DESTDIR)"
endif
.PHONY: install
doc/qutebrowser.1.html:
a2x -f manpage doc/qutebrowser.1.asciidoc
install: doc/qutebrowser.1.html
$(PYTHON) setup.py install --root="$(DESTDIR)" --optimize=1
$(PYTHON) setup.py install --prefix="$(PREFIX)" --optimize=1 $(SETUPTOOLSOPTS)
install -Dm644 misc/qutebrowser.appdata.xml \
"$(DESTDIR)$(PREFIX)/share/metainfo/qutebrowser.appdata.xml"
install -Dm644 doc/qutebrowser.1 \
"$(DESTDIR)/usr/share/man/man1/qutebrowser.1"
"$(DESTDIR)$(PREFIX)/share/man/man1/qutebrowser.1"
install -Dm644 misc/qutebrowser.desktop \
"$(DESTDIR)/usr/share/applications/qutebrowser.desktop"
"$(DESTDIR)$(PREFIX)/share/applications/qutebrowser.desktop"
$(foreach i,$(ICONSIZES),install -Dm644 "icons/qutebrowser-$(i)x$(i).png" \
"$(DESTDIR)/usr/share/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";)
"$(DESTDIR)$(PREFIX)/share/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";)
install -Dm644 icons/qutebrowser.svg \
"$(DESTDIR)/usr/share/icons/hicolor/scalable/apps/qutebrowser.svg"
install -Dm755 -t "$(DESTDIR)/usr/share/qutebrowser/userscripts/" \
"$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/qutebrowser.svg"
install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/userscripts/" \
$(wildcard misc/userscripts/*)
install -Dm755 -t "$(DESTDIR)/usr/share/qutebrowser/scripts/" \
install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/scripts/" \
$(filter-out scripts/__init__.py scripts/__pycache__ scripts/dev \
scripts/testbrowser scripts/asciidoc2html.py scripts/setupcommon.py \
scripts/link_pyqt.py,$(wildcard scripts/*))

View File

@@ -33,7 +33,7 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.7536248"
inkscape:cx="376.55567"
inkscape:cx="430.72917"
inkscape:cy="268.64059"
inkscape:document-units="px"
inkscape:current-layer="layer1"
@@ -3710,7 +3710,7 @@
style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"
id="flowPara5701-9-2"><flowSpan
style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
id="flowSpan5705-5-1">(10)</flowSpan> toggling settings:</flowPara><flowPara
id="flowSpan5705-5-1">(12)</flowSpan> toggling settings:</flowPara><flowPara
style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
id="flowPara6196">tsh - toggle scripts for the current host (temporarily)</flowPara><flowPara
style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 181 KiB

View File

@@ -9,9 +9,9 @@
<description>
<p>
qutebrowser is a keyboard-focused browser with a minimal GUI.
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl,
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl,
and is based on Python and PyQt5.
</p>
</p>
</description>
<categories>
<category>Network</category>
@@ -40,4 +40,9 @@
<url type="help">https://qutebrowser.org/doc/help/</url>
<url type="bugtracker">https://github.com/qutebrowser/qutebrowser/issues/</url>
<url type="donation">https://github.com/qutebrowser/qutebrowser#donating</url>
<releases>
<release version="1.3.0" date="2018-05-04"/>
<release version="1.2.1" date="2018-03-14"/>
<release version="1.2.0" date="2018-03-09"/>
</releases>
</component>

View File

@@ -1,6 +1,7 @@
[Desktop Entry]
Name=qutebrowser
GenericName=Web Browser
Comment=A keyboard-driven, vim-like browser based on PyQt5
Icon=qutebrowser
Type=Application
Categories=Network;WebBrowser;

View File

@@ -40,6 +40,8 @@ Section "Install"
; Uninstall old versions
ExecWait 'MsiExec.exe /quiet /qn /norestart /X{633F41F9-FE9B-42D1-9CC4-718CBD01EE11}'
ExecWait 'MsiExec.exe /quiet /qn /norestart /X{9331D947-AC86-4542-A755-A833429C6E69}'
RMDir /r "$INSTDIR\*.*"
CreateDirectory "$INSTDIR"
SetOutPath "$INSTDIR"

View File

@@ -15,7 +15,7 @@ def get_data_files():
('../qutebrowser/img', 'img'),
('../qutebrowser/javascript', 'javascript'),
('../qutebrowser/html/doc', 'html/doc'),
('../qutebrowser/git-commit-id', ''),
('../qutebrowser/git-commit-id', '.'),
('../qutebrowser/config/configdata.yml', 'config'),
]
@@ -58,14 +58,15 @@ exe = EXE(pyz,
icon=icon,
debug=False,
strip=False,
upx=True,
console=False )
upx=False,
console=False,
version='misc/file_version_info.txt')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx=False,
name='qutebrowser')
app = BUNDLE(coll,

View File

@@ -1,3 +1,3 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
check-manifest==0.36
check-manifest==0.37

View File

@@ -1,9 +1,9 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
certifi==2018.1.18
certifi==2018.4.16
chardet==3.0.4
codecov==2.0.15
coverage==4.5.1
idna==2.6
requests==2.18.4
idna==2.7
requests==2.19.1
urllib3==1.22

View File

@@ -1,9 +1,9 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
attrs==17.4.0
attrs==18.1.0
flake8==3.5.0
flake8-bugbear==18.2.0
flake8-builtins==1.0.post0
flake8-builtins==1.4.1 # rq.filter: != 1.4.0
flake8-comprehensions==1.4.1
flake8-copyright==0.2.0
flake8-debugger==3.1.0
@@ -11,15 +11,17 @@ flake8-deprecated==1.3
flake8-docstrings==1.3.0
flake8-future-import==0.4.4
flake8-mock==0.3
flake8-per-file-ignores==0.4
flake8-per-file-ignores==0.6
flake8-polyfill==1.0.2
flake8-string-format==0.2.3
flake8-tidy-imports==1.1.0
flake8-tuple==0.2.13
mccabe==0.6.1
pep8-naming==0.5.0
pycodestyle==2.3.1
pathmatch==0.2.1
pep8-naming==0.7.0
pycodestyle==2.3.1 # rq.filter: < 2.4.0
pydocstyle==2.1.1
pyflakes==1.6.0
pyflakes==2.0.0
six==1.11.0
snowballstemmer==1.2.1
typing==3.6.4

View File

@@ -1,6 +1,6 @@
flake8
flake8-bugbear
flake8-builtins
flake8-builtins!=1.4.0
flake8-comprehensions
flake8-copyright
flake8-debugger
@@ -15,3 +15,9 @@ flake8-tuple
pep8-naming
pydocstyle
pyflakes
# https://github.com/PyCQA/pycodestyle/issues/741
#@ filter: pycodestyle < 2.4.0
# https://github.com/gforcada/flake8-builtins/issues/36
#@ filter: flake8-builtins != 1.4.0

View File

@@ -3,6 +3,6 @@
appdirs==1.4.3
packaging==17.1
pyparsing==2.2.0
setuptools==38.5.1
setuptools==39.2.0
six==1.11.0
wheel==0.30.0
wheel==0.31.1

View File

@@ -4,4 +4,4 @@ altgraph==0.15
future==0.16.0
macholib==1.9
pefile==2017.11.5
-e git+https://github.com/pyinstaller/pyinstaller.git@develop#egg=PyInstaller
PyInstaller==3.3.1

View File

@@ -1,4 +1 @@
-e git+https://github.com/pyinstaller/pyinstaller.git@develop#egg=PyInstaller
# remove @commit-id for scm installs
#@ replace: @.*# @develop#
PyInstaller

View File

@@ -1,18 +1,18 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-e git+https://github.com/PyCQA/astroid.git#egg=astroid
certifi==2018.1.18
certifi==2018.4.16
chardet==3.0.4
github3.py==0.9.6
idna==2.6
github3.py==1.1.0
idna==2.7
isort==4.3.4
lazy-object-proxy==1.3.1
mccabe==0.6.1
-e git+https://github.com/PyCQA/pylint.git#egg=pylint
python-dateutil==2.7.3
./scripts/dev/pylint_checkers
requests==2.18.4
requests==2.19.1
six==1.11.0
uritemplate==3.0.0
uritemplate.py==3.0.2
urllib3==1.22
wrapt==1.10.11

View File

@@ -1,18 +1,18 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
astroid==1.6.1
certifi==2018.1.18
astroid==1.6.5
certifi==2018.4.16
chardet==3.0.4
github3.py==0.9.6
idna==2.6
github3.py==1.1.0
idna==2.7
isort==4.3.4
lazy-object-proxy==1.3.1
mccabe==0.6.1
pylint==1.8.2
pylint==1.9.2
python-dateutil==2.7.3
./scripts/dev/pylint_checkers
requests==2.18.4
requests==2.19.1
six==1.11.0
uritemplate==3.0.0
uritemplate.py==3.0.2
urllib3==1.22
wrapt==1.10.11

View File

@@ -1,4 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.10.1
sip==4.19.8
PyQt5==5.11.2
PyQt5-sip==4.19.11

View File

@@ -1,4 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
docutils==0.14
pyroma==2.3
pyroma==2.3.1

View File

@@ -1,7 +1,9 @@
Jinja2
Pygments
pyPEG2
PyYAML
PyYAML!=4.1
colorama
cssutils
attrs
#@ filter: PyYAML != 4.1

View File

@@ -35,8 +35,4 @@ git+https://github.com/pallets/markupsafe.git
hg+http://bitbucket.org/birkenfeld/pygments-main
hg+https://bitbucket.org/fdik/pypeg
git+https://github.com/python-attrs/attrs.git
# Fails to build:
# gcc: error: ext/_yaml.c: No such file or directory
# hg+https://bitbucket.org/xi/pyyaml
PyYAML==3.12
git+https://github.com/yaml/pyyaml.git

View File

@@ -1,40 +1,40 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
attrs==17.4.0
attrs==18.1.0
beautifulsoup4==4.6.0
cheroot==6.0.0
cheroot==6.3.2
click==6.7
# colorama==0.3.9
coverage==4.5.1
EasyProcess==0.2.3
fields==5.0.0
Flask==0.12.2
Flask==1.0.2
glob2==0.6
hunter==2.0.2
hypothesis==3.48.0
hypothesis==3.65.0
itsdangerous==0.24
# Jinja2==2.10
Mako==1.0.7
# MarkupSafe==1.0
more-itertools==4.1.0
parse==1.8.2
more-itertools==4.2.0
parse==1.8.4
parse-type==0.4.2
pluggy==0.6.0
py==1.5.2
py-cpuinfo==3.3.0
pytest==3.4.1
pytest-bdd==2.20.0
py==1.5.4
py-cpuinfo==4.0.0
pytest==3.6.2
pytest-bdd==2.21.0
pytest-benchmark==3.1.1
pytest-cov==2.5.1
pytest-faulthandler==1.4.1
pytest-instafail==0.3.0
pytest-mock==1.7.1
pytest-qt==2.3.1
pytest-faulthandler==1.5.0
pytest-instafail==0.4.0
pytest-mock==1.10.0
pytest-qt==2.4.1
pytest-repeat==0.4.1
pytest-rerunfailures==4.0
pytest-rerunfailures==4.1
pytest-travis-fold==1.3.0
pytest-xvfb==1.1.0
PyVirtualDisplay==0.2.1
six==1.11.0
vulture==0.26
vulture==0.27
Werkzeug==0.14.1

View File

@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
pluggy==0.6.0
py==1.5.2
py==1.5.4
six==1.11.0
tox==2.9.1
virtualenv==15.1.0
tox==3.0.0
virtualenv==16.0.0

View File

@@ -1,4 +1 @@
tox
# The latest tox release still depends on pluggy < 0.4...
pluggy==0.4.0

View File

@@ -1,3 +1,3 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
vulture==0.26
vulture==0.27

69
misc/userscripts/getbib Executable file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""Qutebrowser userscript scraping the current web page for DOIs and downloading
corresponding bibtex information.
Set the environment variable 'QUTE_BIB_FILEPATH' to indicate the path to
download to. Otherwise, bibtex information is downloaded to '/tmp' and hence
deleted at reboot.
Installation: see qute://help/userscripts.html
Inspired by
https://ocefpaf.github.io/python4oceanographers/blog/2014/05/19/doi2bibtex/
"""
import os
import sys
import shutil
import re
from collections import Counter
from urllib import parse as url_parse
from urllib import request as url_request
FIFO_PATH = os.getenv("QUTE_FIFO")
def message_fifo(message, level="warning"):
"""Send message to qutebrowser FIFO. The level must be one of 'info',
'warning' (default) or 'error'."""
with open(FIFO_PATH, "w") as fifo:
fifo.write("message-{} '{}'".format(level, message))
source = os.getenv("QUTE_TEXT")
with open(source) as f:
text = f.read()
# find DOIs on page using regex
dval = re.compile(r'(10\.(\d)+/([^(\s\>\"\<)])+)')
# https://stackoverflow.com/a/10324802/3865876, too strict
# dval = re.compile(r'\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+)\b')
dois = dval.findall(text)
dois = Counter(e[0] for e in dois)
try:
doi = dois.most_common(1)[0][0]
except IndexError:
message_fifo("No DOIs found on page")
sys.exit()
message_fifo("Found {} DOIs on page, selecting {}".format(len(dois), doi),
level="info")
# get bibtex data corresponding to DOI
url = "http://dx.doi.org/" + url_parse.quote(doi)
headers = dict(Accept='text/bibliography; style=bibtex')
request = url_request.Request(url, headers=headers)
response = url_request.urlopen(request)
status_code = response.getcode()
if status_code >= 400:
message_fifo("Request returned {}".format(status_code))
sys.exit()
# obtain content and format it
bibtex = response.read().decode("utf-8").strip()
bibtex = bibtex.replace(" ", "\n ", 1).\
replace("}, ", "},\n ").replace("}}", "}\n}")
# append to file
bib_filepath = os.getenv("QUTE_BIB_FILEPATH", "/tmp/qute.bib")
with open(bib_filepath, "a") as f:
f.write(bibtex + "\n\n")

261
misc/userscripts/qute-keepass Executable file
View File

@@ -0,0 +1,261 @@
#!/usr/bin/env python3
# Copyright 2018 Jay Kamat <jaygkamat@gmail.com>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""This userscript allows for insertion of usernames and passwords from keepass
databases using pykeepass. Since it is a userscript, it must be run from
qutebrowser.
A sample invocation of this script is:
:spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx
And a sample binding
:bind --mode=insert <ctrl-i> spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx
-p or --path is a required argument.
--keyfile-path allows you to specify a keepass keyfile. If you only use a
keyfile, also add --no-password as well. Specifying --no-password without
--keyfile-path will lead to an error.
login information is inserted using :insert-text and :fake-key <Tab>, which
means you must have a cursor in position before initiating this userscript. If
you do not do this, you will get 'element not editable' errors.
If keepass takes a while to open the DB, you might want to consider reducing
the number of transform rounds in your database settings.
Dependencies: pykeepass (in python3), PyQt5. Without pykeepass, you will get an
exit code of 100.
********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!******************
WARNING: The login details are viewable as plaintext in qutebrowser's debug log
(qute://log) and could be compromised if you decide to submit a crash report!
********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!******************
"""
# pylint: disable=bad-builtin
import argparse
import enum
import functools
import os
import shlex
import subprocess
import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QInputDialog, QLineEdit
try:
import pykeepass
except ImportError as e:
print("pykeepass not found: {}".format(str(e)), file=sys.stderr)
# Since this is a common error, try to print it to the FIFO if we can.
if 'QUTE_FIFO' in os.environ:
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
fifo.write('message-error "pykeepass failed to be imported."\n')
fifo.flush()
sys.exit(100)
argument_parser = argparse.ArgumentParser(
description="Fill passwords using keepass.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__)
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
argument_parser.add_argument('--path', '-p', required=True,
help='Path to the keepass db.')
argument_parser.add_argument('--keyfile-path', '-k', default=None,
help='Path to a keepass keyfile')
argument_parser.add_argument(
'--no-password', action='store_true',
help='Supply if no password is required to unlock this database. '
'Only allowed with --keyfile-path')
argument_parser.add_argument(
'--dmenu-invocation', '-d', default='dmenu',
help='Invocation used to execute a dmenu-provider')
argument_parser.add_argument(
'--dmenu-format', '-f', default='{title}: {username}',
help='Format string for keys to display in dmenu.'
' Must generate a unique string.')
argument_parser.add_argument(
'--no-insert-mode', '-n', dest='insert_mode', action='store_false',
help="Don't automatically enter insert mode")
argument_parser.add_argument(
'--io-encoding', '-i', default='UTF-8',
help='Encoding used to communicate with subprocesses')
group = argument_parser.add_mutually_exclusive_group()
group.add_argument('--username-fill-only', '-e',
action='store_true', help='Only insert username')
group.add_argument('--password-fill-only', '-w',
action='store_true', help='Only insert password')
CMD_DELAY = 50
class ExitCodes(enum.IntEnum):
"""Stores various exit codes groups to use."""
SUCCESS = 0
FAILURE = 1
# 1 is automatically used if Python throws an exception
NO_CANDIDATES = 2
USER_QUIT = 3
DB_OPEN_FAIL = 4
INTERNAL_ERROR = 10
def qute_command(command):
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
fifo.write(command + '\n')
fifo.flush()
def stderr(to_print):
"""Extra functionality to echo out errors to qb ui."""
print(to_print, file=sys.stderr)
qute_command('message-error "{}"'.format(to_print))
def dmenu(items, invocation, encoding):
"""Runs dmenu with given arguments."""
command = shlex.split(invocation)
process = subprocess.run(command, input='\n'.join(items).encode(encoding),
stdout=subprocess.PIPE)
return process.stdout.decode(encoding).strip()
def get_password():
"""Get a keepass db password from user."""
_app = QApplication(sys.argv)
text, ok = QInputDialog.getText(
None, "KeePass DB Password",
"Please enter your KeePass Master Password",
QLineEdit.Password)
if not ok:
stderr('Password Prompt Rejected.')
sys.exit(ExitCodes.USER_QUIT)
return text
def find_candidates(args, host):
"""Finds candidates that match host"""
file_path = os.path.expanduser(args.path)
# TODO find a way to keep the db open, so we don't open (and query
# password) it every time
pw = None
if not args.no_password:
pw = get_password()
kf = args.keyfile_path
if kf:
kf = os.path.expanduser(kf)
try:
kp = pykeepass.PyKeePass(file_path, password=pw, keyfile=kf)
except Exception as e:
stderr("There was an error opening the DB: {}".format(str(e)))
return kp.find_entries(url="{}{}{}".format(".*", host, ".*"), regex=True)
def candidate_to_str(args, candidate):
"""Turns candidate into a human readable string for dmenu"""
return args.dmenu_format.format(title=candidate.title,
url=candidate.url,
username=candidate.username,
path=candidate.path,
uuid=candidate.uuid)
def candidate_to_secret(candidate):
"""Turns candidate into a generic (user, password) tuple"""
return (candidate.username, candidate.password)
def run(args):
"""Runs qute-keepass"""
if not args.url:
argument_parser.print_help()
return ExitCodes.FAILURE
url_host = QUrl(args.url).host()
if not url_host:
stderr('{} was not parsed as a valid URL!'.format(args.url))
return ExitCodes.INTERNAL_ERROR
# Find candidates matching the host of the given URL
candidates = find_candidates(args, url_host)
if not candidates:
stderr('No candidates for URL {!r} found!'.format(args.url))
return ExitCodes.NO_CANDIDATES
# Create a map so we can get turn the resulting string from dmenu back into
# a candidate
candidates_strs = list(map(functools.partial(candidate_to_str, args),
candidates))
candidates_map = dict(zip(candidates_strs, candidates))
if len(candidates) == 1:
selection = candidates.pop()
else:
selection = dmenu(candidates_strs,
args.dmenu_invocation,
args.io_encoding)
if selection not in candidates_map:
stderr("'{}' was not a valid entry!").format(selection)
return ExitCodes.USER_QUIT
selection = candidates_map[selection]
username, password = candidate_to_secret(selection)
insert_mode = ';; enter-mode insert' if args.insert_mode else ''
if args.username_fill_only:
qute_command('insert-text {}{}'.format(username, insert_mode))
elif args.password_fill_only:
qute_command('insert-text {}{}'.format(password, insert_mode))
else:
# Enter username and password using insert-key and fake-key <Tab>
# (which supports more passwords than fake-key only), then switch back
# into insert-mode, so the form can be directly submitted by hitting
# enter afterwards. It dosen't matter when we go into insert mode, but
# the other commands need to be be executed sequentially, so we add
# delays with later.
qute_command('insert-text {} ;;'
'later {} fake-key <Tab> ;;'
'later {} insert-text {}{}'
.format(username, CMD_DELAY,
CMD_DELAY * 2, password, insert_mode))
return ExitCodes.SUCCESS
if __name__ == '__main__':
arguments = argument_parser.parse_args()
sys.exit(run(arguments))

172
misc/userscripts/qute-lastpass Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env python3
# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com>
# Adapted for LastPass by Wayne Cheng (welps) <waynethecheng@gmail.com>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published bjy
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""
Insert login information using lastpass CLI and a dmenu-compatible application (e.g. dmenu, rofi -dmenu, ...).
A short demonstration can be seen here: https://i.imgur.com/zA61NrF.gifv.
"""
USAGE = """The domain of the site has to be in the name of the LastPass entry, for example: "github.com/cryzed" or
"websites/github.com". The login information is inserted by emulating key events using qutebrowser's fake-key command in this manner:
[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms.
You must log into LastPass CLI using `lpass login <email>` prior to use of this script. The LastPass CLI agent only holds your master password for an hour by default. If you wish to change this, please see `man lpass`.
To use in qutebrowser, run: `spawn --userscript qute-lastpass`
"""
EPILOG = """Dependencies: tldextract (Python 3 module), LastPass CLI (1.3 or newer)
WARNING: The login details are viewable as plaintext in qutebrowser's debug log (qute://log) and might be shared if
you decide to submit a crash report!"""
import argparse
import enum
import fnmatch
import functools
import os
import re
import shlex
import subprocess
import sys
import json
import tldextract
argument_parser = argparse.ArgumentParser(
description=__doc__, usage=USAGE, epilog=EPILOG)
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu',
help='Invocation used to execute a dmenu-provider')
argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false',
help="Don't automatically enter insert mode")
argument_parser.add_argument('--io-encoding', '-i', default='UTF-8',
help='Encoding used to communicate with subprocesses')
argument_parser.add_argument('--merge-candidates', '-m', action='store_true',
help='Merge pass candidates for fully-qualified and registered domain name')
group = argument_parser.add_mutually_exclusive_group()
group.add_argument('--username-only', '-e',
action='store_true', help='Only insert username')
group.add_argument('--password-only', '-w',
action='store_true', help='Only insert password')
stderr = functools.partial(print, file=sys.stderr)
class ExitCodes(enum.IntEnum):
SUCCESS = 0
FAILURE = 1
# 1 is automatically used if Python throws an exception
NO_PASS_CANDIDATES = 2
COULD_NOT_MATCH_USERNAME = 3
COULD_NOT_MATCH_PASSWORD = 4
def qute_command(command):
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
fifo.write(command + '\n')
fifo.flush()
def pass_(domain, encoding):
args = ['lpass', 'show', '-x', '-j', '-G', '.*{:s}.*'.format(domain)]
process = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
err = process.stderr.decode(encoding).strip()
if err:
msg = "LastPass CLI returned for {:s} - {:s}".format(domain, err)
stderr(msg)
return '[]'
out = process.stdout.decode(encoding).strip()
return out
def dmenu(items, invocation, encoding):
command = shlex.split(invocation)
process = subprocess.run(command, input='\n'.join(
items).encode(encoding), stdout=subprocess.PIPE)
return process.stdout.decode(encoding).strip()
def fake_key_raw(text):
for character in text:
# Escape all characters by default, space requires special handling
sequence = '" "' if character == ' ' else '\{}'.format(character)
qute_command('fake-key {}'.format(sequence))
def main(arguments):
if not arguments.url:
argument_parser.print_help()
return ExitCodes.FAILURE
extract_result = tldextract.extract(arguments.url)
# Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains),
# the registered domain name and finally: the IPv4 address if that's what
# the URL represents
candidates = []
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.subdomain + extract_result.domain, extract_result.domain, extract_result.ipv4]):
target_candidates = json.loads(pass_(target, arguments.io_encoding))
if not target_candidates:
continue
candidates = candidates + target_candidates
if not arguments.merge_candidates:
break
else:
if not candidates:
stderr('No pass candidates for URL {!r} found!'.format(
arguments.url))
return ExitCodes.NO_PASS_CANDIDATES
if len(candidates) == 1:
selection = candidates.pop()
else:
choices = ["{:s} | {:s} | {:s} | {:s}".format(c["id"], c["name"], c["url"], c["username"]) for c in candidates]
choice = dmenu(choices, arguments.dmenu_invocation, arguments.io_encoding)
choiceId = choice.split("|")[0].strip()
selection = next((c for (i, c) in enumerate(candidates) if c["id"] == choiceId), None)
# Nothing was selected, simply return
if not selection:
return ExitCodes.SUCCESS
username = selection["username"]
password = selection["password"]
if arguments.username_only:
fake_key_raw(username)
elif arguments.password_only:
fake_key_raw(password)
else:
# Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch
# back into insert-mode, so the form can be directly submitted by
# hitting enter afterwards
fake_key_raw(username)
qute_command('fake-key <Tab>')
fake_key_raw(password)
if arguments.insert_mode:
qute_command('enter-mode insert')
return ExitCodes.SUCCESS
if __name__ == '__main__':
arguments = argument_parser.parse_args()
sys.exit(main(arguments))

View File

@@ -109,6 +109,13 @@ def dmenu(items, invocation, encoding):
return process.stdout.decode(encoding).strip()
def fake_key_raw(text):
for character in text:
# Escape all characters by default, space requires special handling
sequence = '" "' if character == ' ' else '\{}'.format(character)
qute_command('fake-key {}'.format(sequence))
def main(arguments):
if not arguments.url:
argument_parser.print_help()
@@ -158,15 +165,19 @@ def main(arguments):
return ExitCodes.COULD_NOT_MATCH_PASSWORD
password = match.group(1)
insert_mode = ';; enter-mode insert' if arguments.insert_mode else ''
if arguments.username_only:
qute_command('fake-key {}{}'.format(username, insert_mode))
fake_key_raw(username)
elif arguments.password_only:
qute_command('fake-key {}{}'.format(password, insert_mode))
fake_key_raw(password)
else:
# Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch
# back into insert-mode, so the form can be directly submitted by hitting enter afterwards
qute_command('fake-key {} ;; fake-key <Tab> ;; fake-key {}{}'.format(username, password, insert_mode))
fake_key_raw(username)
qute_command('fake-key <Tab>')
fake_key_raw(password)
if arguments.insert_mode:
qute_command('enter-mode insert')
return ExitCodes.SUCCESS

View File

@@ -28,7 +28,7 @@
if msg="$(task add "$title" "$*" 2>&1)"; then
# annotate the new task with the url, send the output back to the browser
task +LATEST annotate "$QUTE_URL"
echo "message-info '$msg'" >> "$QUTE_FIFO"
echo "message-info '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO"
else
echo "message-error '$msg'" >> "$QUTE_FIFO"
echo "message-error '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO"
fi

View File

@@ -49,7 +49,7 @@ msg() {
MPV_COMMAND=${MPV_COMMAND:-mpv}
# Warning: spaces in single flags are not supported
MPV_FLAGS=${MPV_FLAGS:- --force-window --no-terminal --keep-open=yes --ytdl --ytdl-raw-options=yes-playlist=}
MPV_FLAGS=${MPV_FLAGS:- --force-window --no-terminal --keep-open=yes --ytdl}
IFS=" " read -r -a video_command <<< "$MPV_COMMAND $MPV_FLAGS"
js() {

View File

@@ -30,6 +30,7 @@ markers =
qtbug60673: Tests which are broken if the conversion from orange selection to real selection is flaky
fake_os: Fake utils.is_* to a fake operating system
unicode_locale: Tests which need an unicode locale to work
qtwebkit6021_skip: Tests which would fail on WebKit version 602.1
qt_log_level_fail = WARNING
qt_log_ignore =
^SpellCheck: .*
@@ -62,4 +63,8 @@ qt_log_ignore =
^inotify_add_watch\(".*"\) failed: "No space left on device"
^QSettings::value: Empty key passed
^Icon theme ".*" not found
^Error receiving trust for a CA certificate
xfail_strict = true
filterwarnings =
# This happens in many qutebrowser dependencies...
ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working:DeprecationWarning

View File

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

View File

@@ -340,7 +340,7 @@ def _open_startpage(win_id=None):
for cur_win_id in list(window_ids): # Copying as the dict could change
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=cur_win_id)
if tabbed_browser.count() == 0:
if tabbed_browser.widget.count() == 0:
log.init.debug("Opening start pages")
for url in config.val.url.start_pages:
tabbed_browser.tabopen(url)

View File

@@ -94,14 +94,8 @@ class HostBlocker:
_done_count: How many files have been read successfully.
_local_hosts_file: The path to the blocked-hosts file.
_config_hosts_file: The path to a blocked-hosts in ~/.config
Class attributes:
WHITELISTED: Hosts which never should be blocked.
"""
WHITELISTED = ('localhost', 'localhost.localdomain', 'broadcasthost',
'local')
def __init__(self):
self._blocked_hosts = set()
self._config_blocked_hosts = set()
@@ -234,16 +228,14 @@ class HostBlocker:
parts = line.split()
if len(parts) == 1:
# "one host per line" format
host = parts[0]
elif len(parts) == 2:
# /etc/hosts format
host = parts[1]
hosts = [parts[0]]
else:
log.misc.error("Failed to parse: {!r}".format(line))
return False
# /etc/hosts format
hosts = parts[1:]
if host not in self.WHITELISTED:
self._blocked_hosts.add(host)
for host in hosts:
if '.' in host and not host.endswith('.localdomain'):
self._blocked_hosts.add(host)
return True

View File

@@ -22,18 +22,22 @@
import enum
import itertools
import sip
import attr
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QApplication
import pygments
import pygments.lexers
import pygments.formatters
from qutebrowser.keyinput import modeman
from qutebrowser.config import config
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
urlutils, message)
from qutebrowser.misc import miscwidgets, objects
from qutebrowser.browser import mouse, hints
from qutebrowser.qt import sip
tab_id_gen = itertools.count(0)
@@ -95,6 +99,8 @@ class TabData:
keep_icon: Whether the (e.g. cloned) icon should not be cleared on page
load.
inspector: The QWebInspector used for this webview.
viewing_source: Set if we're currently showing a source view.
Only used when sources are shown via pygments.
open_target: Where to open the next link.
Only used for QtWebKit.
override_target: Override for open_target for fake clicks (like hints).
@@ -106,6 +112,7 @@ class TabData:
"""
keep_icon = attr.ib(False)
viewing_source = attr.ib(False)
inspector = attr.ib(None)
open_target = attr.ib(usertypes.ClickTarget.normal)
override_target = attr.ib(None)
@@ -114,6 +121,10 @@ class TabData:
netrc_used = attr.ib(False)
input_mode = attr.ib(usertypes.KeyMode.normal)
def should_show_icon(self):
return (config.val.tabs.favicons.show == 'always' or
config.val.tabs.favicons.show == 'pinned' and self.pinned)
class AbstractAction:
@@ -146,10 +157,31 @@ class AbstractAction:
raise WebTabError("{} is not a valid web action!".format(name))
self._widget.triggerPageAction(member)
def show_source(self):
def show_source(self,
pygments=False): # pylint: disable=redefined-outer-name
"""Show the source of the current page in a new tab."""
raise NotImplementedError
def _show_source_pygments(self):
def show_source_cb(source):
"""Show source as soon as it's ready."""
# WORKAROUND for https://github.com/PyCQA/pylint/issues/491
# pylint: disable=no-member
lexer = pygments.lexers.HtmlLexer()
formatter = pygments.formatters.HtmlFormatter(
full=True, linenos='table')
# pylint: enable=no-member
highlighted = pygments.highlight(source, lexer, formatter)
tb = objreg.get('tabbed-browser', scope='window',
window=self._tab.win_id)
new_tab = tb.tabopen(background=False, related=True)
new_tab.set_html(highlighted, self._tab.url())
new_tab.data.viewing_source = True
self._tab.dump_async(show_source_cb)
class AbstractPrinting:
@@ -333,7 +365,14 @@ class AbstractZoom(QObject):
class AbstractCaret(QObject):
"""Attribute of AbstractTab for caret browsing."""
"""Attribute of AbstractTab for caret browsing.
Signals:
selection_toggled: Emitted when the selection was toggled.
arg: Whether the selection is now active.
"""
selection_toggled = pyqtSignal(bool)
def __init__(self, tab, mode_manager, parent=None):
super().__init__(parent)
@@ -403,6 +442,13 @@ class AbstractCaret(QObject):
def selection(self, callback):
raise NotImplementedError
def _follow_enter(self, tab):
"""Follow a link by faking an enter press."""
if tab:
self._tab.key_press(Qt.Key_Enter, modifier=Qt.ControlModifier)
else:
self._tab.key_press(Qt.Key_Enter)
def follow_selected(self, *, tab=False):
raise NotImplementedError
@@ -439,6 +485,9 @@ class AbstractScroller(QObject):
def to_point(self, point):
raise NotImplementedError
def to_anchor(self, name):
raise NotImplementedError
def delta(self, x=0, y=0):
raise NotImplementedError
@@ -585,6 +634,33 @@ class AbstractElements:
raise NotImplementedError
class AbstractAudio(QObject):
"""Handling of audio/muting for this tab."""
muted_changed = pyqtSignal(bool)
recently_audible_changed = pyqtSignal(bool)
def __init__(self, parent=None):
super().__init__(parent)
self._widget = None
def set_muted(self, muted: bool):
"""Set this tab as muted or not."""
raise NotImplementedError
def is_muted(self):
"""Whether this tab is muted."""
raise NotImplementedError
def toggle_muted(self):
self.set_muted(not self.is_muted())
def is_recently_audible(self):
"""Whether this tab has had audio playing recently."""
raise NotImplementedError
class AbstractTab(QWidget):
"""A wrapper over the given widget to hide its API and expose another one.
@@ -665,8 +741,7 @@ class AbstractTab(QWidget):
objreg.register('hintmanager', hintmanager, scope='tab',
window=self.win_id, tab=self.tab_id)
self.predicted_navigation.connect(
lambda url: self.title_changed.emit(url.toDisplayString()))
self.predicted_navigation.connect(self._on_predicted_navigation)
def _set_widget(self, widget):
# pylint: disable=protected-access
@@ -680,6 +755,7 @@ class AbstractTab(QWidget):
self.printing._widget = widget
self.action._widget = widget
self.elements._widget = widget
self.audio._widget = widget
self.settings._settings = widget.settings()
self._install_event_filter()
@@ -711,10 +787,24 @@ class AbstractTab(QWidget):
if getattr(evt, 'posted', False):
raise utils.Unreachable("Can't re-use an event which was already "
"posted!")
recipient = self.event_target()
if recipient is None:
# https://github.com/qutebrowser/qutebrowser/issues/3888
log.webview.warning("Unable to find event target!")
return
evt.posted = True
QApplication.postEvent(recipient, evt)
@pyqtSlot(QUrl)
def _on_predicted_navigation(self, url):
"""Adjust the title if we are going to visit an URL soon."""
qtutils.ensure_valid(url)
url_string = url.toDisplayString()
log.webview.debug("Predicted navigation: {}".format(url_string))
self.title_changed.emit(url_string)
@pyqtSlot(QUrl)
def _on_url_changed(self, url):
"""Update title when URL has changed and no title is available."""
@@ -726,6 +816,7 @@ class AbstractTab(QWidget):
def _on_load_started(self):
self._progress = 0
self._has_ssl_errors = False
self.data.viewing_source = False
self._set_load_status(usertypes.LoadStatus.loading)
self.load_started.emit()
@@ -802,10 +893,6 @@ class AbstractTab(QWidget):
self._progress = perc
self.load_progress.emit(perc)
@pyqtSlot()
def _on_ssl_errors(self):
self._has_ssl_errors = True
def url(self, requested=False):
raise NotImplementedError
@@ -815,11 +902,12 @@ class AbstractTab(QWidget):
def load_status(self):
return self._load_status
def _openurl_prepare(self, url):
def _openurl_prepare(self, url, *, predict=True):
qtutils.ensure_valid(url)
self.predicted_navigation.emit(url)
if predict:
self.predicted_navigation.emit(url)
def openurl(self, url):
def openurl(self, url, *, predict=True):
raise NotImplementedError
def reload(self, *, force=False):

View File

@@ -53,7 +53,6 @@ class CommandDispatcher:
cmdutils.register() decorators are run, currentWidget() will return None.
Attributes:
_editor: The ExternalEditor object.
_win_id: The window ID the CommandDispatcher is associated with.
_tabbed_browser: The TabbedBrowser used.
"""
@@ -73,16 +72,16 @@ class CommandDispatcher:
def _count(self):
"""Convenience method to get the widget count."""
return self._tabbed_browser.count()
return self._tabbed_browser.widget.count()
def _set_current_index(self, idx):
"""Convenience method to set the current widget index."""
cmdutils.check_overflow(idx, 'int')
self._tabbed_browser.setCurrentIndex(idx)
self._tabbed_browser.widget.setCurrentIndex(idx)
def _current_index(self):
"""Convenience method to get the current widget index."""
return self._tabbed_browser.currentIndex()
return self._tabbed_browser.widget.currentIndex()
def _current_url(self):
"""Convenience method to get the current url."""
@@ -101,7 +100,7 @@ class CommandDispatcher:
def _current_widget(self):
"""Get the currently active widget from a command."""
widget = self._tabbed_browser.currentWidget()
widget = self._tabbed_browser.widget.currentWidget()
if widget is None:
raise cmdexc.CommandError("No WebView available yet!")
return widget
@@ -147,10 +146,10 @@ class CommandDispatcher:
None if no widget was found.
"""
if count is None:
return self._tabbed_browser.currentWidget()
return self._tabbed_browser.widget.currentWidget()
elif 1 <= count <= self._count():
cmdutils.check_overflow(count + 1, 'int')
return self._tabbed_browser.widget(count - 1)
return self._tabbed_browser.widget.widget(count - 1)
else:
return None
@@ -163,7 +162,7 @@ class CommandDispatcher:
if not show_error:
return
raise cmdexc.CommandError("No last focused tab!")
idx = self._tabbed_browser.indexOf(tab)
idx = self._tabbed_browser.widget.indexOf(tab)
if idx == -1:
raise cmdexc.CommandError("Last focused tab vanished!")
self._set_current_index(idx)
@@ -212,7 +211,7 @@ class CommandDispatcher:
what's configured in 'tabs.select_on_remove'.
count: The tab index to close, or None
"""
tabbar = self._tabbed_browser.tabBar()
tabbar = self._tabbed_browser.widget.tabBar()
selection_override = self._get_selection_override(prev, next_,
opposite)
@@ -264,7 +263,7 @@ class CommandDispatcher:
return
to_pin = not tab.data.pinned
self._tabbed_browser.set_tab_pinned(tab, to_pin)
self._tabbed_browser.widget.set_tab_pinned(tab, to_pin)
@cmdutils.register(instance='command-dispatcher', name='open',
maxsplit=0, scope='window')
@@ -483,7 +482,8 @@ class CommandDispatcher:
"""
cmdutils.check_exclusive((bg, window), 'bw')
curtab = self._current_widget()
cur_title = self._tabbed_browser.page_title(self._current_index())
cur_title = self._tabbed_browser.widget.page_title(
self._current_index())
try:
history = curtab.history.serialize()
except browsertab.WebTabError as e:
@@ -499,18 +499,18 @@ class CommandDispatcher:
newtab = new_tabbed_browser.tabopen(background=bg)
new_tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=newtab.win_id)
idx = new_tabbed_browser.indexOf(newtab)
idx = new_tabbed_browser.widget.indexOf(newtab)
new_tabbed_browser.set_page_title(idx, cur_title)
if config.val.tabs.favicons.show:
new_tabbed_browser.setTabIcon(idx, curtab.icon())
new_tabbed_browser.widget.set_page_title(idx, cur_title)
if curtab.data.should_show_icon():
new_tabbed_browser.widget.setTabIcon(idx, curtab.icon())
if config.val.tabs.tabs_are_windows:
new_tabbed_browser.window().setWindowIcon(curtab.icon())
new_tabbed_browser.widget.window().setWindowIcon(curtab.icon())
newtab.data.keep_icon = True
newtab.history.deserialize(history)
newtab.zoom.set_factor(curtab.zoom.factor())
new_tabbed_browser.set_tab_pinned(newtab, curtab.data.pinned)
new_tabbed_browser.widget.set_tab_pinned(newtab, curtab.data.pinned)
return newtab
@cmdutils.register(instance='command-dispatcher', scope='window')
@@ -566,12 +566,6 @@ class CommandDispatcher:
tabbed_browser.tabopen(self._current_url())
self._tabbed_browser.close_tab(self._current_widget(), add_undo=False)
@cmdutils.register(instance='command-dispatcher', scope='window',
deprecated='Use :tab-give instead!')
def tab_detach(self):
"""Deprecated way to detach a tab."""
self.tab_give()
def _back_forward(self, tab, bg, window, count, forward):
"""Helper function for :back/:forward."""
history = self._current_widget().history
@@ -768,6 +762,15 @@ class CommandDispatcher:
self._current_widget().scroller.to_perc(x, y)
@cmdutils.register(instance='command-dispatcher', scope='window')
def scroll_to_anchor(self, name):
"""Scroll to the given anchor in the document.
Args:
name: The anchor to scroll to.
"""
self._current_widget().scroller.to_anchor(name)
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('count', count=True)
@cmdutils.argument('top_navigate', metavar='ACTION',
@@ -846,7 +849,7 @@ class CommandDispatcher:
keep: Stay in visual mode after yanking the selection.
"""
if what == 'title':
s = self._tabbed_browser.page_title(self._current_index())
s = self._tabbed_browser.widget.page_title(self._current_index())
elif what == 'domain':
port = self._current_url().port()
s = '{}://{}{}'.format(self._current_url().scheme(),
@@ -958,7 +961,7 @@ class CommandDispatcher:
force: Avoid confirmation for pinned tabs.
"""
cmdutils.check_exclusive((prev, next_), 'pn')
cur_idx = self._tabbed_browser.currentIndex()
cur_idx = self._tabbed_browser.widget.currentIndex()
assert cur_idx != -1
def _to_close(i):
@@ -1013,7 +1016,7 @@ class CommandDispatcher:
elif config.val.tabs.wrap:
self._set_current_index(newidx % self._count())
else:
raise cmdexc.CommandError("First tab")
log.webview.debug("First tab")
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('count', count=True)
@@ -1033,7 +1036,7 @@ class CommandDispatcher:
elif config.val.tabs.wrap:
self._set_current_index(newidx % self._count())
else:
raise cmdexc.CommandError("Last tab")
log.webview.debug("Last tab")
def _resolve_buffer_index(self, index):
"""Resolve a buffer index to the tabbedbrowser and tab.
@@ -1075,11 +1078,11 @@ class CommandDispatcher:
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
if not 0 < idx <= tabbed_browser.count():
if not 0 < idx <= tabbed_browser.widget.count():
raise cmdexc.CommandError(
"There's no tab with index {}!".format(idx))
return (tabbed_browser, tabbed_browser.widget(idx-1))
return (tabbed_browser, tabbed_browser.widget.widget(idx-1))
@cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0)
@@ -1107,10 +1110,10 @@ class CommandDispatcher:
tabbed_browser, tab = self._resolve_buffer_index(index)
window = tabbed_browser.window()
window = tabbed_browser.widget.window()
window.activateWindow()
window.raise_()
tabbed_browser.setCurrentWidget(tab)
tabbed_browser.widget.setCurrentWidget(tab)
@cmdutils.register(instance='command-dispatcher', scope='window')
@cmdutils.argument('index', choices=['last'])
@@ -1194,7 +1197,7 @@ class CommandDispatcher:
cur_idx = self._current_index()
cmdutils.check_overflow(cur_idx, 'int')
cmdutils.check_overflow(new_idx, 'int')
self._tabbed_browser.tabBar().moveTab(cur_idx, new_idx)
self._tabbed_browser.widget.tabBar().moveTab(cur_idx, new_idx)
@cmdutils.register(instance='command-dispatcher', scope='window',
maxsplit=0, no_replace_variables=True)
@@ -1278,10 +1281,10 @@ class CommandDispatcher:
idx = self._current_index()
if idx != -1:
env['QUTE_TITLE'] = self._tabbed_browser.page_title(idx)
env['QUTE_TITLE'] = self._tabbed_browser.widget.page_title(idx)
# FIXME:qtwebengine: If tab is None, run_async will fail!
tab = self._tabbed_browser.currentWidget()
tab = self._tabbed_browser.widget.currentWidget()
try:
url = self._tabbed_browser.current_url()
@@ -1452,6 +1455,7 @@ class CommandDispatcher:
if tab.data.inspector is None:
tab.data.inspector = inspector.create()
tab.data.inspector.inspect(page)
tab.data.inspector.show()
else:
tab.data.inspector.toggle(page)
except inspector.WebInspectorError as e:
@@ -1512,11 +1516,15 @@ class CommandDispatcher:
)
@cmdutils.register(instance='command-dispatcher', scope='window')
def view_source(self, edit=False):
def view_source(self, edit=False, pygments=False):
"""Show the source of the current page in a new tab.
Args:
edit: Edit the source in the editor instead of opening a tab.
pygments: Use pygments to generate the view. This is always
the case for QtWebKit. For QtWebEngine it may display
slightly different source.
Some JavaScript processing may be applied.
"""
tab = self._current_widget()
try:
@@ -1524,14 +1532,15 @@ class CommandDispatcher:
except cmdexc.CommandError as e:
message.error(str(e))
return
if current_url.scheme() == 'view-source':
if current_url.scheme() == 'view-source' or tab.data.viewing_source:
raise cmdexc.CommandError("Already viewing source!")
if edit:
ed = editor.ExternalEditor(self._tabbed_browser)
tab.dump_async(ed.edit)
else:
tab.action.show_source()
tab.action.show_source(pygments)
@cmdutils.register(instance='command-dispatcher', scope='window',
debug=True)
@@ -1639,7 +1648,7 @@ class CommandDispatcher:
ed = editor.ExternalEditor(watch=True, parent=self._tabbed_browser)
ed.file_updated.connect(functools.partial(
self.on_file_updated, elem))
self.on_file_updated, ed, elem))
ed.editing_finished.connect(lambda: mainwindow.raise_window(
objreg.last_focused_window(), alert=False))
ed.edit(text, caret_position)
@@ -1654,7 +1663,7 @@ class CommandDispatcher:
tab = self._current_widget()
tab.elements.find_focused(self._open_editor_cb)
def on_file_updated(self, elem, text):
def on_file_updated(self, ed, elem, text):
"""Write the editor text into the form field and clean up tempfile.
Callback for GUIProcess when the edited text was updated.
@@ -1665,10 +1674,12 @@ class CommandDispatcher:
"""
try:
elem.set_value(text)
except webelem.OrphanedError as e:
except webelem.OrphanedError:
message.error('Edited element vanished')
ed.backup()
except webelem.Error as e:
raise cmdexc.CommandError(str(e))
message.error(str(e))
ed.backup()
@cmdutils.register(instance='command-dispatcher', maxsplit=0,
scope='window')
@@ -2217,5 +2228,22 @@ class CommandDispatcher:
pass
return
window = self._tabbed_browser.window()
window = self._tabbed_browser.widget.window()
window.setWindowState(window.windowState() ^ Qt.WindowFullScreen)
@cmdutils.register(instance='command-dispatcher', scope='window',
name='tab-mute')
@cmdutils.argument('count', count=True)
def tab_mute(self, count=None):
"""Mute/Unmute the current/[count]th tab.
Args:
count: The tab index to mute or unmute, or None
"""
tab = self._cntwidget(count)
if tab is None:
return
try:
tab.audio.toggle_muted()
except browsertab.WebTabError as e:
raise cmdexc.CommandError(e)

View File

@@ -29,7 +29,6 @@ import pathlib
import tempfile
import enum
import sip
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
QTimer, QAbstractListModel, QUrl)
@@ -37,6 +36,7 @@ from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.config import config
from qutebrowser.utils import (usertypes, standarddir, utils, message, log,
qtutils)
from qutebrowser.qt import sip
ModelRole = enum.IntEnum('ModelRole', ['item'], start=Qt.UserRole)

View File

@@ -21,13 +21,13 @@
import functools
import sip
from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu, QStyleFactory
from qutebrowser.browser import downloads
from qutebrowser.config import config
from qutebrowser.utils import qtutils, utils, objreg
from qutebrowser.qt import sip
def update_geometry(obj):

View File

@@ -30,9 +30,11 @@ import textwrap
import attr
from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from qutebrowser.utils import log, standarddir, jinja, objreg, utils
from qutebrowser.utils import (log, standarddir, jinja, objreg, utils,
javascript, urlmatch, version, usertypes)
from qutebrowser.commands import cmdutils
from qutebrowser.browser import downloads
from qutebrowser.misc import objects
def _scripts_dir():
@@ -47,6 +49,7 @@ class GreasemonkeyScript:
def __init__(self, properties, code):
self._code = code
self.includes = []
self.matches = []
self.excludes = []
self.requires = []
self.description = None
@@ -55,6 +58,7 @@ class GreasemonkeyScript:
self.run_at = None
self.script_meta = None
self.runs_on_sub_frames = True
self.jsworld = "main"
for name, value in properties:
if name == 'name':
self.name = value
@@ -62,8 +66,10 @@ class GreasemonkeyScript:
self.namespace = value
elif name == 'description':
self.description = value
elif name in ['include', 'match']:
elif name == 'include':
self.includes.append(value)
elif name == 'match':
self.matches.append(value)
elif name in ['exclude', 'exclude_match']:
self.excludes.append(value)
elif name == 'run-at':
@@ -72,6 +78,8 @@ class GreasemonkeyScript:
self.runs_on_sub_frames = False
elif name == 'require':
self.requires.append(value)
elif name == 'qute-js-world':
self.jsworld = value
HEADER_REGEX = r'// ==UserScript==|\n+// ==/UserScript==\n'
PROPS_REGEX = r'// @(?P<prop>[^\s]+)\s*(?P<val>.*)'
@@ -91,7 +99,7 @@ class GreasemonkeyScript:
props = ""
script = cls(re.findall(cls.PROPS_REGEX, props), source)
script.script_meta = props
if not props:
if not script.includes and not script.matches:
script.includes = ['*']
return script
@@ -104,18 +112,24 @@ class GreasemonkeyScript:
browser's debugger/inspector will not match up to the line
numbers in the source script directly.
"""
return jinja.js_environment.get_template(
'greasemonkey_wrapper.js').render(
scriptName="/".join([self.namespace or '', self.name]),
scriptInfo=self._meta_json(),
scriptMeta=self.script_meta,
scriptSource=self._code)
# Don't use Proxy on this webkit version, the support isn't there.
use_proxy = not (
objects.backend == usertypes.Backend.QtWebKit and
version.qWebKitVersion() == '602.1')
template = jinja.js_environment.get_template('greasemonkey_wrapper.js')
return template.render(
scriptName=javascript.string_escape(
"/".join([self.namespace or '', self.name])),
scriptInfo=self._meta_json(),
scriptMeta=javascript.string_escape(self.script_meta),
scriptSource=self._code,
use_proxy=use_proxy)
def _meta_json(self):
return json.dumps({
'name': self.name,
'description': self.description,
'matches': self.includes,
'matches': self.matches,
'includes': self.includes,
'excludes': self.excludes,
'run-at': self.run_at,
@@ -141,6 +155,42 @@ class MatchingScripts(object):
idle = attr.ib(default=attr.Factory(list))
class GreasemonkeyMatcher:
"""Check whether scripts should be loaded for a given URL."""
# https://wiki.greasespot.net/Include_and_exclude_rules#Greaseable_schemes
# Limit the schemes scripts can run on due to unreasonable levels of
# exploitability
GREASEABLE_SCHEMES = ['http', 'https', 'ftp', 'file']
def __init__(self, url):
self._url = url
self._url_string = url.toString(QUrl.FullyEncoded)
self.is_greaseable = url.scheme() in self.GREASEABLE_SCHEMES
def _match_pattern(self, pattern):
# For include and exclude rules if they start and end with '/' they
# should be treated as a (ecma syntax) regular expression.
if pattern.startswith('/') and pattern.endswith('/'):
matches = re.search(pattern[1:-1], self._url_string, flags=re.I)
return matches is not None
# Otherwise they are glob expressions.
return fnmatch.fnmatch(self._url_string, pattern)
def matches(self, script):
"""Check whether the URL matches filtering rules of the script."""
assert self.is_greaseable
matching_includes = any(self._match_pattern(pat)
for pat in script.includes)
matching_match = any(urlmatch.UrlPattern(pat).matches(self._url)
for pat in script.matches)
matching_excludes = any(self._match_pattern(pat)
for pat in script.excludes)
return (matching_includes or matching_match) and not matching_excludes
class GreasemonkeyManager(QObject):
"""Manager of userscripts and a Greasemonkey compatible environment.
@@ -152,10 +202,6 @@ class GreasemonkeyManager(QObject):
"""
scripts_reloaded = pyqtSignal()
# https://wiki.greasespot.net/Include_and_exclude_rules#Greaseable_schemes
# Limit the schemes scripts can run on due to unreasonable levels of
# exploitability
greaseable_schemes = ['http', 'https', 'ftp', 'file']
def __init__(self, parent=None):
super().__init__(parent)
@@ -307,30 +353,17 @@ class GreasemonkeyManager(QObject):
returns a tuple of lists of scripts meant to run at (document-start,
document-end, document-idle)
"""
if url.scheme() not in self.greaseable_schemes:
matcher = GreasemonkeyMatcher(url)
if not matcher.is_greaseable:
return MatchingScripts(url, [], [], [])
string_url = url.toString(QUrl.FullyEncoded)
def _match(pattern):
# For include and exclude rules if they start and end with '/' they
# should be treated as a (ecma syntax) regular expression.
if pattern.startswith('/') and pattern.endswith('/'):
matches = re.search(pattern[1:-1], string_url, flags=re.I)
return matches is not None
# Otherwise they are glob expressions.
return fnmatch.fnmatch(string_url, pattern)
tester = (lambda script:
any(_match(pat) for pat in script.includes) and
not any(_match(pat) for pat in script.excludes))
return MatchingScripts(
url,
[script for script in self._run_start if tester(script)],
[script for script in self._run_end if tester(script)],
[script for script in self._run_idle if tester(script)]
url=url,
start=[script for script in self._run_start
if matcher.matches(script)],
end=[script for script in self._run_end
if matcher.matches(script)],
idle=[script for script in self._run_idle
if matcher.matches(script)]
)
def all_scripts(self):

View File

@@ -22,6 +22,7 @@
import collections
import functools
import math
import os
import re
import html
import enum
@@ -154,6 +155,7 @@ class HintContext:
to_follow: The link to follow when enter is pressed.
args: Custom arguments for userscript/spawn
rapid: Whether to do rapid hinting.
first_run: Whether the action is run for the 1st time in rapid hinting.
add_history: Whether to add yanked or spawned link to the history.
filterstr: Used to save the filter string for restoring in rapid mode.
tab: The WebTab object we started hinting in.
@@ -166,12 +168,14 @@ class HintContext:
baseurl = attr.ib(None)
to_follow = attr.ib(None)
rapid = attr.ib(False)
first_run = attr.ib(True)
add_history = attr.ib(False)
filterstr = attr.ib(None)
args = attr.ib(attr.Factory(list))
tab = attr.ib(None)
group = attr.ib(None)
hint_mode = attr.ib(None)
first = attr.ib(False)
def get_args(self, urlstr):
"""Get the arguments, with {hint-url} replaced by the given URL."""
@@ -240,7 +244,18 @@ class HintActions:
if url.scheme() == 'mailto':
flags |= QUrl.RemoveScheme
urlstr = url.toString(flags)
utils.set_clipboard(urlstr, selection=sel)
new_content = urlstr
# only second and consecutive yanks are to append to the clipboard
if context.rapid and not context.first_run:
try:
old_content = utils.get_clipboard(selection=sel)
except utils.ClipboardEmptyError:
pass
else:
new_content = os.linesep.join([old_content, new_content])
utils.set_clipboard(new_content, selection=sel)
msg = "Yanked URL to {}: {}".format(
"primary selection" if sel else "clipboard",
@@ -612,6 +627,9 @@ class HintManager(QObject):
modeman.enter(self._win_id, usertypes.KeyMode.hint,
'HintManager.start')
if self._context.first:
self._fire(strings[0])
return
# to make auto_follow == 'always' work
self._handle_auto_follow()
@@ -620,7 +638,8 @@ class HintManager(QObject):
@cmdutils.argument('win_id', win_id=True)
def start(self, # pylint: disable=keyword-arg-before-vararg
group=webelem.Group.all, target=Target.normal,
*args, win_id, mode=None, add_history=False, rapid=False):
*args, win_id, mode=None, add_history=False, rapid=False,
first=False):
"""Start hinting.
Args:
@@ -631,6 +650,7 @@ class HintManager(QObject):
`window`, `run`, `hover`, `userscript` and `spawn`.
add_history: Whether to add the spawned or yanked link to the
browsing history.
first: Click the first hinted element without prompting.
group: The element types to hint.
- `all`: All clickable elements.
@@ -682,7 +702,7 @@ class HintManager(QObject):
"""
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=self._win_id)
tab = tabbed_browser.currentWidget()
tab = tabbed_browser.widget.currentWidget()
if tab is None:
raise cmdexc.CommandError("No WebView available yet!")
@@ -694,7 +714,8 @@ class HintManager(QObject):
if rapid:
if target in [Target.tab_bg, Target.window, Target.run,
Target.hover, Target.userscript, Target.spawn,
Target.download, Target.normal, Target.current]:
Target.download, Target.normal, Target.current,
Target.yank, Target.yank_primary]:
pass
elif target == Target.tab and config.val.tabs.background:
pass
@@ -713,6 +734,7 @@ class HintManager(QObject):
self._context.rapid = rapid
self._context.hint_mode = mode
self._context.add_history = add_history
self._context.first = first
try:
self._context.baseurl = tabbed_browser.current_url()
except qtutils.QtValueError:
@@ -907,6 +929,9 @@ class HintManager(QObject):
except HintingError as e:
message.error(str(e))
if self._context is not None:
self._context.first_run = False
@cmdutils.register(instance='hintmanager', scope='tab',
modes=[usertypes.KeyMode.hint])
def follow_hint(self, select=False, keystring=None):

View File

@@ -23,7 +23,7 @@ import os
import time
import contextlib
from PyQt5.QtCore import pyqtSlot, QUrl, QTimer
from PyQt5.QtCore import pyqtSlot, QUrl, QTimer, pyqtSignal
from qutebrowser.commands import cmdutils, cmdexc
from qutebrowser.utils import (utils, objreg, log, usertypes, message,
@@ -52,6 +52,11 @@ class WebHistory(sql.SqlTable):
"""The global history of visited pages."""
# All web history cleared
history_cleared = pyqtSignal()
# one url cleared
url_cleared = pyqtSignal(QUrl)
def __init__(self, parent=None):
super().__init__("History", ['url', 'title', 'atime', 'redirect'],
constraints={'url': 'NOT NULL',
@@ -157,6 +162,7 @@ class WebHistory(sql.SqlTable):
with self._handle_sql_errors():
self.delete_all()
self.completion.delete_all()
self.history_cleared.emit()
def delete_url(self, url):
"""Remove all history entries with the given url.
@@ -168,6 +174,7 @@ class WebHistory(sql.SqlTable):
qtutils.ensure_valid(qurl)
self.delete('url', self._format_url(qurl))
self.completion.delete('url', self._format_completion_url(qurl))
self.url_cleared.emit(qurl)
@pyqtSlot(QUrl, QUrl, str)
def add_from_tab(self, url, requested_url, title):

View File

@@ -87,6 +87,8 @@ class AbstractWebInspector(QWidget):
data = bytes(self.saveGeometry())
geom = base64.b64encode(data).decode('ASCII')
configfiles.state['geometry']['inspector'] = geom
self.inspect(None)
super().closeEvent(e)
def inspect(self, page):
@@ -99,3 +101,4 @@ class AbstractWebInspector(QWidget):
self.hide()
else:
self.inspect(page)
self.show()

View File

@@ -22,7 +22,7 @@
from PyQt5.QtCore import QObject, QEvent, Qt, QTimer
from qutebrowser.config import config
from qutebrowser.utils import message, log, usertypes
from qutebrowser.utils import message, log, usertypes, qtutils, objreg
from qutebrowser.keyinput import modeman
@@ -40,11 +40,12 @@ class ChildEventFilter(QObject):
_widget: The widget expected to send out childEvents.
"""
def __init__(self, eventfilter, widget, parent=None):
def __init__(self, eventfilter, widget, win_id, parent=None):
super().__init__(parent)
self._filter = eventfilter
assert widget is not None
self._widget = widget
self._win_id = win_id
def eventFilter(self, obj, event):
"""Act on ChildAdded events."""
@@ -54,6 +55,29 @@ class ChildEventFilter(QObject):
obj, child))
assert obj is self._widget
child.installEventFilter(self._filter)
if qtutils.version_check('5.11', compiled=False, exact=True):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-68076
pass_modes = [usertypes.KeyMode.command,
usertypes.KeyMode.prompt,
usertypes.KeyMode.yesno]
if modeman.instance(self._win_id).mode not in pass_modes:
tabbed_browser = objreg.get('tabbed-browser',
scope='window',
window=self._win_id)
current_index = tabbed_browser.widget.currentIndex()
try:
widget_index = tabbed_browser.widget.indexOf(
self._widget.parent())
except RuntimeError:
widget_index = -1
if current_index == widget_index:
QTimer.singleShot(0, self._widget.setFocus)
elif event.type() == QEvent.ChildRemoved:
child = event.child()
log.mouse.debug("{}: removed child {}".format(obj, child))
return False

View File

@@ -29,7 +29,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from qutebrowser.config import config
from qutebrowser.utils import message, usertypes, log, urlutils, utils
from qutebrowser.utils import message, usertypes, log, urlutils, utils, debug
from qutebrowser.browser import downloads
from qutebrowser.browser.webkit import http
from qutebrowser.browser.webkit.network import networkmanager
@@ -162,6 +162,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
QTimer.singleShot(0, lambda: self._die(reply.errorString()))
def _do_cancel(self):
self._read_timer.stop()
if self._reply is not None:
self._reply.finished.disconnect(self._on_reply_finished)
self._reply.abort()
@@ -306,7 +307,14 @@ class DownloadItem(downloads.AbstractDownloadItem):
"""Handle QNetworkReply errors."""
if code == QNetworkReply.OperationCanceledError:
return
self._die(self._reply.errorString())
if self._reply is None:
error = "Unknown error: {}".format(
debug.qenum_key(QNetworkReply, code))
else:
error = self._reply.errorString()
self._die(error)
@pyqtSlot()
def _on_read_timer_timeout(self):

View File

@@ -24,6 +24,7 @@ Module attributes:
_HANDLERS: The handlers registered via decorators.
"""
import html
import json
import os
import time
@@ -33,7 +34,6 @@ import urllib
import collections
import pkg_resources
import sip
from PyQt5.QtCore import QUrlQuery, QUrl
import qutebrowser
@@ -41,6 +41,7 @@ from qutebrowser.config import config, configdata, configexc, configdiff
from qutebrowser.utils import (version, utils, jinja, log, message, docutils,
objreg, urlutils)
from qutebrowser.misc import objects
from qutebrowser.qt import sip
pyeval_output = ":pyeval was never called"
@@ -123,12 +124,12 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
def wrong_backend_handler(self, url):
"""Show an error page about using the invalid backend."""
html = jinja.render('error.html',
title="Error while opening qute://url",
url=url.toDisplayString(),
error='{} is not available with this '
'backend'.format(url.toDisplayString()))
return 'text/html', html
src = jinja.render('error.html',
title="Error while opening qute://url",
url=url.toDisplayString(),
error='{} is not available with this '
'backend'.format(url.toDisplayString()))
return 'text/html', src
def data_for_url(url):
@@ -177,7 +178,7 @@ def data_for_url(url):
except OSError as e:
# FIXME:qtwebengine how to handle this?
raise QuteSchemeOSError(e)
except QuteSchemeError as e:
except QuteSchemeError:
raise
assert mimetype is not None, url
@@ -196,11 +197,11 @@ def qute_bookmarks(_url):
quickmarks = sorted(objreg.get('quickmark-manager').marks.items(),
key=lambda x: x[0]) # Sort by name
html = jinja.render('bookmarks.html',
title='Bookmarks',
bookmarks=bookmarks,
quickmarks=quickmarks)
return 'text/html', html
src = jinja.render('bookmarks.html',
title='Bookmarks',
bookmarks=bookmarks,
quickmarks=quickmarks)
return 'text/html', src
@add_handler('tabs')
@@ -218,10 +219,10 @@ def qute_tabs(_url):
urlstr = tab.url().toDisplayString()
tabs[str(win_id)].append((tab.title(), urlstr))
html = jinja.render('tabs.html',
title='Tabs',
tab_list_by_window=tabs)
return 'text/html', html
src = jinja.render('tabs.html',
title='Tabs',
tab_list_by_window=tabs)
return 'text/html', src
def history_data(start_time, offset=None):
@@ -241,8 +242,9 @@ def history_data(start_time, offset=None):
end_time = start_time - 24*60*60
entries = hist.entries_between(end_time, start_time)
return [{"url": e.url, "title": e.title or e.url, "time": e.atime}
for e in entries]
return [{"url": e.url,
"title": html.escape(e.title) or html.escape(e.url),
"time": e.atime} for e in entries]
@add_handler('history')
@@ -287,25 +289,25 @@ def qute_javascript(url):
@add_handler('pyeval')
def qute_pyeval(_url):
"""Handler for qute://pyeval."""
html = jinja.render('pre.html', title='pyeval', content=pyeval_output)
return 'text/html', html
src = jinja.render('pre.html', title='pyeval', content=pyeval_output)
return 'text/html', src
@add_handler('spawn-output')
def qute_spawn_output(_url):
"""Handler for qute://spawn-output."""
html = jinja.render('pre.html', title='spawn output', content=spawn_output)
return 'text/html', html
src = jinja.render('pre.html', title='spawn output', content=spawn_output)
return 'text/html', src
@add_handler('version')
@add_handler('verizon')
def qute_version(_url):
"""Handler for qute://version."""
html = jinja.render('version.html', title='Version info',
version=version.version(),
copyright=qutebrowser.__copyright__)
return 'text/html', html
src = jinja.render('version.html', title='Version info',
version=version.version(),
copyright=qutebrowser.__copyright__)
return 'text/html', src
@add_handler('plainlog')
@@ -323,8 +325,8 @@ def qute_plainlog(url):
if not level:
level = 'vdebug'
text = log.ram_handler.dump_log(html=False, level=level)
html = jinja.render('pre.html', title='log', content=text)
return 'text/html', html
src = jinja.render('pre.html', title='log', content=text)
return 'text/html', src
@add_handler('log')
@@ -343,8 +345,8 @@ def qute_log(url):
level = 'vdebug'
html_log = log.ram_handler.dump_log(html=True, level=level)
html = jinja.render('log.html', title='log', content=html_log)
return 'text/html', html
src = jinja.render('log.html', title='log', content=html_log)
return 'text/html', src
@add_handler('gpl')
@@ -415,12 +417,12 @@ def qute_help(url):
@add_handler('backend-warning')
def qute_backend_warning(_url):
"""Handler for qute://backend-warning."""
html = jinja.render('backend-warning.html',
distribution=version.distribution(),
Distribution=version.Distribution,
version=pkg_resources.parse_version,
title="Legacy backend warning")
return 'text/html', html
src = jinja.render('backend-warning.html',
distribution=version.distribution(),
Distribution=version.Distribution,
version=pkg_resources.parse_version,
title="Legacy backend warning")
return 'text/html', src
def _qute_settings_set(url):
@@ -450,10 +452,10 @@ def qute_settings(url):
if url.path() == '/set':
return _qute_settings_set(url)
html = jinja.render('settings.html', title='settings',
configdata=configdata,
confget=config.instance.get_str)
return 'text/html', html
src = jinja.render('settings.html', title='settings',
configdata=configdata,
confget=config.instance.get_str)
return 'text/html', src
@add_handler('bindings')
@@ -467,9 +469,9 @@ def qute_bindings(_url):
for mode in modes:
bindings[mode] = config.key_instance.get_bindings_for(mode)
html = jinja.render('bindings.html', title='Bindings',
bindings=bindings)
return 'text/html', html
src = jinja.render('bindings.html', title='Bindings',
bindings=bindings)
return 'text/html', src
@add_handler('back')
@@ -478,10 +480,10 @@ def qute_back(url):
Simple page to free ram / lazy load a site, goes back on focusing the tab.
"""
html = jinja.render(
src = jinja.render(
'back.html',
title='Suspended: ' + urllib.parse.unquote(url.fragment()))
return 'text/html', html
return 'text/html', src
@add_handler('configdiff')

View File

@@ -34,21 +34,22 @@ class CallSuper(Exception):
"""Raised when the caller should call the superclass instead."""
def custom_headers():
def custom_headers(url):
"""Get the combined custom headers."""
headers = {}
dnt_config = config.val.content.headers.do_not_track
dnt_config = config.instance.get('content.headers.do_not_track', url=url)
if dnt_config is not None:
dnt = b'1' if dnt_config else b'0'
headers[b'DNT'] = dnt
headers[b'X-Do-Not-Track'] = dnt
conf_headers = config.val.content.headers.custom
conf_headers = config.instance.get('content.headers.custom', url=url)
for header, value in conf_headers.items():
headers[header.encode('ascii')] = value.encode('ascii')
accept_language = config.val.content.headers.accept_language
accept_language = config.instance.get('content.headers.accept_language',
url=url)
if accept_language is not None:
headers[b'Accept-Language'] = accept_language.encode('ascii')
@@ -156,7 +157,7 @@ def ignore_certificate_errors(url, errors, abort_on):
Return:
True if the error should be ignored, False otherwise.
"""
ssl_strict = config.val.content.ssl_strict
ssl_strict = config.instance.get('content.ssl_strict', url=url)
log.webview.debug("Certificate errors {!r}, strict {}".format(
errors, ssl_strict))
@@ -196,7 +197,8 @@ def ignore_certificate_errors(url, errors, abort_on):
raise utils.Unreachable
def feature_permission(url, option, msg, yes_action, no_action, abort_on):
def feature_permission(url, option, msg, yes_action, no_action, abort_on,
blocking=False):
"""Handle a feature permission request.
Args:
@@ -206,11 +208,13 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on):
yes_action: A callable to call if the request was approved
no_action: A callable to call if the request was denied
abort_on: A list of signals which interrupt the question.
blocking: If True, ask a blocking question.
Return:
The Question object if a question was asked, None otherwise.
The Question object if a question was asked (and blocking=False),
None otherwise.
"""
config_val = config.instance.get(option)
config_val = config.instance.get(option, url=url)
if config_val == 'ask':
if url.isValid():
urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)
@@ -220,10 +224,20 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on):
urlstr = None
text = "Allow the website to {}?".format(msg)
return message.confirm_async(
yes_action=yes_action, no_action=no_action,
cancel_action=no_action, abort_on=abort_on,
title='Permission request', text=text, url=urlstr)
if blocking:
answer = message.ask(abort_on=abort_on, title='Permission request',
text=text, url=urlstr,
mode=usertypes.PromptMode.yesno)
if answer:
yes_action()
else:
no_action()
return None
else:
return message.confirm_async(
yes_action=yes_action, no_action=no_action,
cancel_action=no_action, abort_on=abort_on,
title='Permission request', text=text, url=urlstr)
elif config_val:
yes_action()
return None
@@ -299,10 +313,10 @@ def netrc_authentication(url, authenticator):
(user, _account, password) = authenticators
except FileNotFoundError:
log.misc.debug("No .netrc file found")
except OSError:
log.misc.exception("Unable to read the netrc file")
except netrc.NetrcParseError:
log.misc.exception("Error when parsing the netrc file")
except OSError as e:
log.misc.exception("Unable to read the netrc file: {}".format(e))
except netrc.NetrcParseError as e:
log.misc.exception("Error when parsing the netrc file: {}".format(e))
if user is None:
return False

View File

@@ -76,11 +76,11 @@ class SignalFilter(QObject):
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=self._win_id)
try:
tabidx = tabbed_browser.indexOf(tab)
tabidx = tabbed_browser.widget.indexOf(tab)
except RuntimeError:
# The tab has been deleted already
return
if tabidx == tabbed_browser.currentIndex():
if tabidx == tabbed_browser.widget.currentIndex():
if log_signal:
log.signals.debug("emitting: {} (tab {})".format(
debug.dbg_signal(signal, args), tabidx))

View File

@@ -307,6 +307,10 @@ class AbstractWebElement(collections.abc.MutableMapping):
href_tags = ['a', 'area', 'link']
return self.tag_name() in href_tags and 'href' in self
def _requires_user_interaction(self):
"""Return True if clicking this element needs user interaction."""
raise NotImplementedError
def _mouse_pos(self):
"""Get the position to click/hover."""
# Click the center of the largest square fitting into the top/left
@@ -405,7 +409,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
return
if click_target == usertypes.ClickTarget.normal:
if self.is_link():
if self.is_link() and not self._requires_user_interaction():
log.webelem.debug("Clicking via JS click()")
self._click_js(click_target)
elif self.is_editable(strict=True):

View File

@@ -28,6 +28,10 @@ class CertificateErrorWrapper(usertypes.AbstractCertificateErrorWrapper):
"""A wrapper over a QWebEngineCertificateError."""
def __init__(self, error):
super().__init__(error)
self.ignore = False
def __str__(self):
return self._error.errorDescription()
@@ -37,5 +41,8 @@ class CertificateErrorWrapper(usertypes.AbstractCertificateErrorWrapper):
self._error.error()),
string=str(self))
def url(self):
return self._error.url()
def is_overridable(self):
return self._error.isOverridable()

View File

@@ -0,0 +1,48 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Filter for QtWebEngine cookies."""
from qutebrowser.config import config
from qutebrowser.utils import utils
def _accept_cookie(request):
"""Check whether the given cookie should be accepted."""
accept = config.val.content.cookies.accept
if accept == 'all':
return True
elif accept in ['no-3rdparty', 'no-unknown-3rdparty']:
return not request.thirdParty
elif accept == 'never':
return False
else:
raise utils.Unreachable
def install_filter(profile):
"""Install the cookie filter on the given profile.
On Qt < 5.11, the filter isn't installed.
"""
store = profile.cookieStore()
try:
store.setCookieFilter(_accept_cookie)
except AttributeError:
pass

View File

@@ -19,20 +19,22 @@
"""A request interceptor taking care of adblocking and custom headers."""
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from PyQt5.QtWebEngineCore import (QWebEngineUrlRequestInterceptor,
QWebEngineUrlRequestInfo)
from qutebrowser.config import config
from qutebrowser.browser import shared
from qutebrowser.utils import utils, log
from qutebrowser.utils import utils, log, debug
class RequestInterceptor(QWebEngineUrlRequestInterceptor):
"""Handle ad blocking and custom headers."""
def __init__(self, host_blocker, parent=None):
def __init__(self, host_blocker, args, parent=None):
super().__init__(parent)
self._host_blocker = host_blocker
self._args = args
def install(self, profile):
"""Install the interceptor on the given QWebEngineProfile."""
@@ -54,15 +56,29 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
Args:
info: QWebEngineUrlRequestInfo &info
"""
if 'log-requests' in self._args.debug_flags:
resource_type = debug.qenum_key(QWebEngineUrlRequestInfo,
info.resourceType())
navigation_type = debug.qenum_key(QWebEngineUrlRequestInfo,
info.navigationType())
log.webview.debug("{} {}, first-party {}, resource {}, "
"navigation {}".format(
bytes(info.requestMethod()).decode('ascii'),
info.requestUrl().toDisplayString(),
info.firstPartyUrl().toDisplayString(),
resource_type, navigation_type))
url = info.requestUrl()
# FIXME:qtwebengine only block ads for NavigationTypeOther?
if self._host_blocker.is_blocked(info.requestUrl()):
if self._host_blocker.is_blocked(url):
log.webview.info("Request to {} blocked by host blocker.".format(
info.requestUrl().host()))
url.host()))
info.block(True)
for header, value in shared.custom_headers():
for header, value in shared.custom_headers(url=url):
info.setHttpHeader(header, value)
user_agent = config.val.content.headers.user_agent
user_agent = config.instance.get('content.headers.user_agent', url=url)
if user_agent is not None:
info.setHttpHeader(b'User-Agent', user_agent.encode('ascii'))

View File

@@ -21,38 +21,53 @@
import glob
import os
import os.path
import re
import shutil
from PyQt5.QtCore import QLibraryInfo
from qutebrowser.utils import log
from qutebrowser.utils import log, message, standarddir, qtutils
dict_version_re = re.compile(r".+-(?P<version>[0-9]+-[0-9]+?)\.bdic")
def version(filename):
"""Extract the version number from the dictionary file name."""
version_re = re.compile(r".+-(?P<version>[0-9]+-[0-9]+?)\.bdic")
match = version_re.fullmatch(filename)
match = dict_version_re.match(filename)
if match is None:
raise ValueError('the given dictionary file name is malformed: {}'
.format(filename))
message.warning(
"Found a dictionary with a malformed name: {}".format(filename))
return None
return tuple(int(n) for n in match.group('version').split('-'))
def dictionary_dir():
def dictionary_dir(old=False):
"""Return the path (str) to the QtWebEngine's dictionaries directory."""
datapath = QLibraryInfo.location(QLibraryInfo.DataPath)
if qtutils.version_check('5.10', compiled=False) and not old:
datapath = standarddir.data()
else:
datapath = QLibraryInfo.location(QLibraryInfo.DataPath)
return os.path.join(datapath, 'qtwebengine_dictionaries')
def local_files(code):
"""Return all installed dictionaries for the given code."""
"""Return all installed dictionaries for the given code.
The returned dictionaries are sorted by version, therefore the latest will
be the first element. The list will be empty if no dictionaries are found.
"""
pathname = os.path.join(dictionary_dir(), '{}*.bdic'.format(code))
matching_dicts = glob.glob(pathname)
files = []
for matching_dict in sorted(matching_dicts, key=version, reverse=True):
filename = os.path.basename(matching_dict)
log.config.debug('Found file for dict {}: {}'.format(code, filename))
files.append(filename)
return files
versioned_dicts = []
for matching_dict in matching_dicts:
parsed_version = version(matching_dict)
if parsed_version is not None:
filename = os.path.basename(matching_dict)
log.config.debug('Found file for dict {}: {}'
.format(code, filename))
versioned_dicts.append((parsed_version, filename))
return [filename for version, filename
in sorted(versioned_dicts, reverse=True)]
def local_filename(code):
@@ -63,3 +78,16 @@ def local_filename(code):
"""
all_installed = local_files(code)
return os.path.splitext(all_installed[0])[0] if all_installed else None
def init():
"""Initialize the dictionary path if supported."""
if qtutils.version_check('5.10', compiled=False):
new_dir = dictionary_dir()
old_dir = dictionary_dir(old=True)
os.environ['QTWEBENGINE_DICTIONARIES_PATH'] = new_dir
try:
if os.path.exists(old_dir) and not os.path.exists(new_dir):
shutil.copytree(old_dir, new_dir)
except OSError:
log.misc.exception("Failed to copy old dictionaries")

View File

@@ -101,7 +101,11 @@ class DownloadItem(downloads.AbstractDownloadItem):
def retry(self):
state = self._qt_item.state()
assert state == QWebEngineDownloadItem.DownloadInterrupted, state
if state != QWebEngineDownloadItem.DownloadInterrupted:
log.downloads.warning(
"Trying to retry download in state {}".format(
debug.qenum_key(QWebEngineDownloadItem, state)))
return
try:
self._qt_item.resume()

View File

@@ -27,7 +27,7 @@ from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineSettings
from qutebrowser.utils import log, javascript
from qutebrowser.utils import log, javascript, urlutils
from qutebrowser.browser import webelem
@@ -198,6 +198,13 @@ class WebEngineElement(webelem.AbstractWebElement):
if self.is_text_input() and self.is_editable():
self._js_call('move_cursor_to_end')
def _requires_user_interaction(self):
baseurl = self._tab.url()
url = self.resolve_url(baseurl)
if url is None:
return True
return url.scheme() not in urlutils.WEBENGINE_SCHEMES
def _click_editable(self, click_target):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-58515
ev = QMouseEvent(QMouseEvent.MouseButtonPress, QPoint(0, 0),

View File

@@ -22,7 +22,7 @@
import os
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
from qutebrowser.browser import inspector
@@ -35,10 +35,12 @@ class WebEngineInspector(inspector.AbstractWebInspector):
super().__init__(parent)
self.port = None
view = QWebEngineView()
settings = view.settings()
settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
self._set_widget(view)
def inspect(self, _page):
"""Set up the inspector."""
def _inspect_old(self, page):
"""Set up the inspector for Qt < 5.11."""
try:
port = int(os.environ['QTWEBENGINE_REMOTE_DEBUGGING'])
except KeyError:
@@ -46,5 +48,18 @@ class WebEngineInspector(inspector.AbstractWebInspector):
"QtWebEngine inspector is not enabled. See "
"'qutebrowser --help' for details.")
url = QUrl('http://localhost:{}/'.format(port))
self._widget.load(url)
self.show()
if page is None:
self._widget.load(QUrl('about:blank'))
else:
self._widget.load(url)
def _inspect_new(self, page):
"""Set up the inspector for Qt >= 5.11."""
self._widget.page().setInspectedPage(page)
def inspect(self, page):
try:
self._inspect_new(page)
except AttributeError:
self._inspect_old(page)

View File

@@ -34,6 +34,10 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
def install(self, profile):
"""Install the handler for qute:// URLs on the given profile."""
profile.installUrlSchemeHandler(b'qute', self)
if qtutils.version_check('5.11', compiled=False):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-63378
profile.installUrlSchemeHandler(b'chrome-error', self)
profile.installUrlSchemeHandler(b'chrome-extension', self)
def requestStarted(self, job):
"""Handle a request for a qute: scheme.
@@ -45,6 +49,12 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
job: QWebEngineUrlRequestJob
"""
url = job.requestUrl()
if url.scheme() in ['chrome-error', 'chrome-extension']:
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-63378
job.fail(QWebEngineUrlRequestJob.UrlInvalid)
return
assert job.requestMethod() == b'GET'
assert url.scheme() == 'qute'
log.misc.debug("Got request for {}".format(url.toDisplayString()))

View File

@@ -26,16 +26,14 @@ Module attributes:
import os
import sip
from PyQt5.QtGui import QFont
from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile,
QWebEngineScript)
QWebEnginePage)
from qutebrowser.browser import shared
from qutebrowser.browser.webengine import spell
from qutebrowser.config import config, websettings
from qutebrowser.utils import (utils, standarddir, javascript, qtutils,
message, log, objreg)
from qutebrowser.config.websettings import AttributeInfo as Attr
from qutebrowser.utils import utils, standarddir, qtutils, message, log
# The default QWebEngineProfile
default_profile = None
@@ -91,35 +89,40 @@ class WebEngineSettings(websettings.AbstractSettings):
_ATTRIBUTES = {
'content.xss_auditing':
[QWebEngineSettings.XSSAuditingEnabled],
Attr(QWebEngineSettings.XSSAuditingEnabled),
'content.images':
[QWebEngineSettings.AutoLoadImages],
Attr(QWebEngineSettings.AutoLoadImages),
'content.javascript.enabled':
[QWebEngineSettings.JavascriptEnabled],
Attr(QWebEngineSettings.JavascriptEnabled),
'content.javascript.can_open_tabs_automatically':
[QWebEngineSettings.JavascriptCanOpenWindows],
Attr(QWebEngineSettings.JavascriptCanOpenWindows),
'content.javascript.can_access_clipboard':
[QWebEngineSettings.JavascriptCanAccessClipboard],
Attr(QWebEngineSettings.JavascriptCanAccessClipboard),
'content.plugins':
[QWebEngineSettings.PluginsEnabled],
Attr(QWebEngineSettings.PluginsEnabled),
'content.hyperlink_auditing':
[QWebEngineSettings.HyperlinkAuditingEnabled],
Attr(QWebEngineSettings.HyperlinkAuditingEnabled),
'content.local_content_can_access_remote_urls':
[QWebEngineSettings.LocalContentCanAccessRemoteUrls],
Attr(QWebEngineSettings.LocalContentCanAccessRemoteUrls),
'content.local_content_can_access_file_urls':
[QWebEngineSettings.LocalContentCanAccessFileUrls],
Attr(QWebEngineSettings.LocalContentCanAccessFileUrls),
'content.webgl':
[QWebEngineSettings.WebGLEnabled],
Attr(QWebEngineSettings.WebGLEnabled),
'content.local_storage':
[QWebEngineSettings.LocalStorageEnabled],
Attr(QWebEngineSettings.LocalStorageEnabled),
'content.desktop_capture':
Attr(QWebEngineSettings.ScreenCaptureEnabled,
converter=lambda val: True if val == 'ask' else val),
# 'ask' is handled via the permission system,
# or a hardcoded dialog on Qt < 5.10
'input.spatial_navigation':
[QWebEngineSettings.SpatialNavigationEnabled],
Attr(QWebEngineSettings.SpatialNavigationEnabled),
'input.links_included_in_focus_chain':
[QWebEngineSettings.LinksIncludedInFocusChain],
Attr(QWebEngineSettings.LinksIncludedInFocusChain),
'scrolling.smooth':
[QWebEngineSettings.ScrollAnimatorEnabled],
Attr(QWebEngineSettings.ScrollAnimatorEnabled),
}
_FONT_SIZES = {
@@ -158,144 +161,116 @@ class WebEngineSettings(websettings.AbstractSettings):
# Attributes which don't exist in all Qt versions.
new_attributes = {
# Qt 5.8
'content.print_element_backgrounds': 'PrintElementBackgrounds',
'content.print_element_backgrounds':
('PrintElementBackgrounds', None),
# Qt 5.11
'content.autoplay':
('PlaybackRequiresUserGesture', lambda val: not val),
}
for name, attribute in new_attributes.items():
for name, (attribute, converter) in new_attributes.items():
try:
value = getattr(QWebEngineSettings, attribute)
except AttributeError:
continue
self._ATTRIBUTES[name] = [value]
self._ATTRIBUTES[name] = Attr(value, converter=converter)
def _init_stylesheet(profile):
"""Initialize custom stylesheets.
class ProfileSetter:
Partially inspired by QupZilla:
https://github.com/QupZilla/qupzilla/blob/v2.0/src/lib/app/mainapplication.cpp#L1063-L1101
"""
old_script = profile.scripts().findScript('_qute_stylesheet')
if not old_script.isNull():
profile.scripts().remove(old_script)
"""Helper to set various settings on a profile."""
css = shared.get_user_stylesheet()
source = '\n'.join([
'"use strict";',
'window._qutebrowser = window._qutebrowser || {};',
utils.read_file('javascript/stylesheet.js'),
javascript.assemble('stylesheet', 'set_css', css),
])
def __init__(self, profile):
self._profile = profile
script = QWebEngineScript()
script.setName('_qute_stylesheet')
script.setInjectionPoint(QWebEngineScript.DocumentCreation)
script.setWorldId(QWebEngineScript.ApplicationWorld)
script.setRunsOnSubFrames(True)
script.setSourceCode(source)
profile.scripts().insert(script)
def init_profile(self):
"""Initialize settings on the given profile."""
self.set_http_headers()
self.set_http_cache_size()
settings = self._profile.settings()
settings.setAttribute(
QWebEngineSettings.FullScreenSupportEnabled, True)
try:
settings.setAttribute(
QWebEngineSettings.FocusOnNavigationEnabled, False)
except AttributeError:
# Added in Qt 5.8
pass
def _update_stylesheet():
"""Update the custom stylesheet in existing tabs."""
css = shared.get_user_stylesheet()
code = javascript.assemble('stylesheet', 'set_css', css)
for win_id, window in objreg.window_registry.items():
# We could be in the middle of destroying a window here
if sip.isdeleted(window):
continue
tab_registry = objreg.get('tab-registry', scope='window',
window=win_id)
for tab in tab_registry.values():
tab.run_js_async(code)
if qtutils.version_check('5.8'):
self.set_dictionary_language()
def set_http_headers(self):
"""Set the user agent and accept-language for the given profile.
def _set_http_headers(profile):
"""Set the user agent and accept-language for the given profile.
We override those per request in the URL interceptor (to allow for
per-domain values), but this one still gets used for things like
window.navigator.userAgent/.languages in JS.
"""
self._profile.setHttpUserAgent(config.val.content.headers.user_agent)
accept_language = config.val.content.headers.accept_language
if accept_language is not None:
self._profile.setHttpAcceptLanguage(accept_language)
We override those per request in the URL interceptor (to allow for
per-domain values), but this one still gets used for things like
window.navigator.userAgent/.languages in JS.
"""
profile.setHttpUserAgent(config.val.content.headers.user_agent)
accept_language = config.val.content.headers.accept_language
if accept_language is not None:
profile.setHttpAcceptLanguage(accept_language)
def set_http_cache_size(self):
"""Initialize the HTTP cache size for the given profile."""
size = config.val.content.cache.size
if size is None:
size = 0
else:
size = qtutils.check_overflow(size, 'int', fatal=False)
# 0: automatically managed by QtWebEngine
self._profile.setHttpCacheMaximumSize(size)
def _set_http_cache_size(profile):
"""Initialize the HTTP cache size for the given profile."""
size = config.val.content.cache.size
if size is None:
size = 0
else:
size = qtutils.check_overflow(size, 'int', fatal=False)
def set_persistent_cookie_policy(self):
"""Set the HTTP Cookie size for the given profile."""
assert not self._profile.isOffTheRecord()
if config.val.content.cookies.store:
value = QWebEngineProfile.AllowPersistentCookies
else:
value = QWebEngineProfile.NoPersistentCookies
self._profile.setPersistentCookiesPolicy(value)
# 0: automatically managed by QtWebEngine
profile.setHttpCacheMaximumSize(size)
def set_dictionary_language(self, warn=True):
"""Load the given dictionaries."""
filenames = []
for code in config.val.spellcheck.languages or []:
local_filename = spell.local_filename(code)
if not local_filename:
if warn:
message.warning("Language {} is not installed - see "
"scripts/dictcli.py in qutebrowser's "
"sources".format(code))
continue
filenames.append(local_filename)
def _set_persistent_cookie_policy(profile):
"""Set the HTTP Cookie size for the given profile."""
if config.val.content.cookies.store:
value = QWebEngineProfile.AllowPersistentCookies
else:
value = QWebEngineProfile.NoPersistentCookies
profile.setPersistentCookiesPolicy(value)
def _set_dictionary_language(profile, warn=True):
filenames = []
for code in config.val.spellcheck.languages or []:
local_filename = spell.local_filename(code)
if not local_filename:
if warn:
message.warning(
"Language {} is not installed - see scripts/dictcli.py "
"in qutebrowser's sources".format(code))
continue
filenames.append(local_filename)
log.config.debug("Found dicts: {}".format(filenames))
profile.setSpellCheckLanguages(filenames)
log.config.debug("Found dicts: {}".format(filenames))
self._profile.setSpellCheckLanguages(filenames)
self._profile.setSpellCheckEnabled(bool(filenames))
def _update_settings(option):
"""Update global settings when qwebsettings changed."""
global_settings.update_setting(option)
if option in ['scrolling.bar', 'content.user_stylesheets']:
_init_stylesheet(default_profile)
_init_stylesheet(private_profile)
_update_stylesheet()
elif option in ['content.headers.user_agent',
'content.headers.accept_language']:
_set_http_headers(default_profile)
_set_http_headers(private_profile)
if option in ['content.headers.user_agent',
'content.headers.accept_language']:
default_profile.setter.set_http_headers()
private_profile.setter.set_http_headers()
elif option == 'content.cache.size':
_set_http_cache_size(default_profile)
_set_http_cache_size(private_profile)
default_profile.setter.set_http_cache_size()
private_profile.setter.set_http_cache_size()
elif (option == 'content.cookies.store' and
# https://bugreports.qt.io/browse/QTBUG-58650
qtutils.version_check('5.9', compiled=False)):
_set_persistent_cookie_policy(default_profile)
default_profile.setter.set_persistent_cookie_policy()
# We're not touching the private profile's cookie policy.
elif option == 'spellcheck.languages':
_set_dictionary_language(default_profile)
_set_dictionary_language(private_profile, warn=False)
def _init_profile(profile):
"""Init the given profile."""
_init_stylesheet(profile)
_set_http_headers(profile)
_set_http_cache_size(profile)
profile.settings().setAttribute(
QWebEngineSettings.FullScreenSupportEnabled, True)
if qtutils.version_check('5.8'):
profile.setSpellCheckEnabled(True)
_set_dictionary_language(profile)
default_profile.setter.set_dictionary_language()
private_profile.setter.set_dictionary_language(warn=False)
def _init_profiles():
@@ -303,60 +278,28 @@ def _init_profiles():
global default_profile, private_profile
default_profile = QWebEngineProfile.defaultProfile()
default_profile.setter = ProfileSetter(default_profile)
default_profile.setCachePath(
os.path.join(standarddir.cache(), 'webengine'))
default_profile.setPersistentStoragePath(
os.path.join(standarddir.data(), 'webengine'))
_init_profile(default_profile)
_set_persistent_cookie_policy(default_profile)
default_profile.setter.init_profile()
default_profile.setter.set_persistent_cookie_policy()
private_profile = QWebEngineProfile()
private_profile.setter = ProfileSetter(private_profile)
assert private_profile.isOffTheRecord()
_init_profile(private_profile)
def inject_userscripts():
"""Register user JavaScript files with the global profiles."""
# The Greasemonkey metadata block support in QtWebEngine only starts at
# Qt 5.8. With 5.7.1, we need to inject the scripts ourselves in response
# to urlChanged.
if not qtutils.version_check('5.8'):
return
# Since we are inserting scripts into profile.scripts they won't
# just get replaced by new gm scripts like if we were injecting them
# ourselves so we need to remove all gm scripts, while not removing
# any other stuff that might have been added. Like the one for
# stylesheets.
greasemonkey = objreg.get('greasemonkey')
for profile in [default_profile, private_profile]:
scripts = profile.scripts()
for script in scripts.toList():
if script.name().startswith("GM-"):
log.greasemonkey.debug('Removing script: {}'
.format(script.name()))
removed = scripts.remove(script)
assert removed, script.name()
# Then add the new scripts.
for script in greasemonkey.all_scripts():
# @run-at (and @include/@exclude/@match) is parsed by
# QWebEngineScript.
new_script = QWebEngineScript()
new_script.setWorldId(QWebEngineScript.MainWorld)
new_script.setSourceCode(script.code())
new_script.setName("GM-{}".format(script.name))
new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
log.greasemonkey.debug('adding script: {}'
.format(new_script.name()))
scripts.insert(new_script)
private_profile.setter.init_profile()
def init(args):
"""Initialize the global QWebSettings."""
if args.enable_webengine_inspector:
if (args.enable_webengine_inspector and
not hasattr(QWebEnginePage, 'setInspectedPage')): # only Qt < 5.11
os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = str(utils.random_port())
spell.init()
_init_profiles()
config.instance.changed.connect(_update_settings)

View File

@@ -25,23 +25,23 @@ import sys
import re
import html as html_utils
import sip
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QEvent, QPoint, QPointF,
QUrl, QTimer)
QUrl, QTimer, QObject, qVersion)
from PyQt5.QtGui import QKeyEvent, QIcon
from PyQt5.QtNetwork import QAuthenticator
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript
from qutebrowser.config import configdata
from qutebrowser.config import configdata, config
from qutebrowser.browser import browsertab, mouse, shared
from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
interceptor, webenginequtescheme,
webenginedownloads,
webenginesettings)
cookies, webenginedownloads,
webenginesettings, certificateerror)
from qutebrowser.misc import miscwidgets
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
message, objreg, jinja, debug)
from qutebrowser.qt import sip
_qute_scheme_handler = None
@@ -62,8 +62,9 @@ def init():
log.init.debug("Initializing request interceptor...")
host_blocker = objreg.get('host-blocker')
args = objreg.get('args')
req_interceptor = interceptor.RequestInterceptor(
host_blocker, parent=app)
host_blocker, args=args, parent=app)
req_interceptor.install(webenginesettings.default_profile)
req_interceptor.install(webenginesettings.private_profile)
@@ -73,9 +74,17 @@ def init():
download_manager.install(webenginesettings.private_profile)
objreg.register('webengine-download-manager', download_manager)
greasemonkey = objreg.get('greasemonkey')
greasemonkey.scripts_reloaded.connect(webenginesettings.inject_userscripts)
webenginesettings.inject_userscripts()
log.init.debug("Initializing cookie filter...")
cookies.install_filter(webenginesettings.default_profile)
cookies.install_filter(webenginesettings.private_profile)
# Clear visited links on web history clear
hist = objreg.get('web-history')
for p in [webenginesettings.default_profile,
webenginesettings.private_profile]:
hist.history_cleared.connect(p.clearAllVisitedLinks)
hist.url_cleared.connect(lambda url, profile=p:
profile.clearVisitedLinks([url]))
# Mapping worlds from usertypes.JsWorld to QWebEngineScript world IDs.
@@ -101,7 +110,11 @@ class WebEngineAction(browsertab.AbstractAction):
"""Save the current page."""
self._widget.triggerPageAction(QWebEnginePage.SavePage)
def show_source(self):
def show_source(self, pygments=False):
if pygments:
self._show_source_pygments()
return
try:
self._widget.triggerPageAction(QWebEnginePage.ViewSource)
except AttributeError:
@@ -233,8 +246,16 @@ class WebEngineCaret(browsertab.AbstractCaret):
self._tab.search.clear()
self._tab.run_js_async(
javascript.assemble('caret', 'setPlatform', sys.platform))
self._js_call('setInitialCursor')
javascript.assemble('caret',
'setPlatform', sys.platform, qVersion()))
self._js_call('setInitialCursor', self._selection_cb)
def _selection_cb(self, enabled):
"""Emit selection_toggled based on setInitialCursor."""
if enabled is None:
log.webview.debug("Ignoring selection status None")
return
self.selection_toggled.emit(enabled)
@pyqtSlot(usertypes.KeyMode)
def _on_mode_left(self, mode):
@@ -301,7 +322,7 @@ class WebEngineCaret(browsertab.AbstractCaret):
self._js_call('moveToEndOfDocument')
def toggle_selection(self):
self._js_call('toggleSelection')
self._js_call('toggleSelection', self.selection_toggled.emit)
def drop_selection(self):
self._js_call('dropSelection')
@@ -323,6 +344,11 @@ class WebEngineCaret(browsertab.AbstractCaret):
"""
if js_elem is None:
return
if js_elem == "focused":
# we had a focused element, not a selected one. Just send <enter>
self._follow_enter(tab)
return
assert isinstance(js_elem, dict), js_elem
elem = webengineelem.WebEngineElement(js_elem, tab=self._tab)
if tab:
@@ -345,20 +371,16 @@ class WebEngineCaret(browsertab.AbstractCaret):
log.webview.debug("Clicking a searched link via fake key press.")
# send a fake enter, clicking the orange selection box
if tab:
self._tab.key_press(Qt.Key_Enter, modifier=Qt.ControlModifier)
else:
self._tab.key_press(Qt.Key_Enter)
self._follow_enter(tab)
else:
# click an existing blue selection
js_code = javascript.assemble('webelem', 'find_selected_link')
js_code = javascript.assemble('webelem',
'find_selected_focused_link')
self._tab.run_js_async(js_code, lambda jsret:
self._follow_selected_cb(jsret, tab))
def _js_call(self, command):
self._tab.run_js_async(
javascript.assemble('caret', command))
def _js_call(self, command, callback=None):
self._tab.run_js_async(javascript.assemble('caret', command), callback)
class WebEngineScroller(browsertab.AbstractScroller):
@@ -379,7 +401,7 @@ class WebEngineScroller(browsertab.AbstractScroller):
def _repeated_key_press(self, key, count=1, modifier=Qt.NoModifier):
"""Send count fake key presses to this scroller's WebEngineTab."""
for _ in range(min(count, 5000)):
for _ in range(min(count, 1000)):
self._tab.key_press(key, modifier)
@pyqtSlot(QPointF)
@@ -432,6 +454,11 @@ class WebEngineScroller(browsertab.AbstractScroller):
js_code = javascript.assemble('window', 'scroll', point.x(), point.y())
self._tab.run_js_async(js_code)
def to_anchor(self, name):
url = self._tab.url()
url.setFragment(name)
self._tab.openurl(url)
def delta(self, x=0, y=0):
self._tab.run_js_async(javascript.assemble('window', 'scrollBy', x, y))
@@ -506,6 +533,9 @@ class WebEngineHistory(browsertab.AbstractHistory):
return qtutils.deserialize(data, self._history)
def load_items(self, items):
if items:
self._tab.predicted_navigation.emit(items[-1].url)
stream, _data, cur_data = tabhistory.serialize(items)
qtutils.deserialize_stream(stream, self._history)
@@ -596,6 +626,333 @@ class WebEngineElements(browsertab.AbstractElements):
self._tab.run_js_async(js_code, js_cb)
class WebEngineAudio(browsertab.AbstractAudio):
"""QtWebEngine implemementations related to audio/muting."""
def _connect_signals(self):
page = self._widget.page()
page.audioMutedChanged.connect(self.muted_changed)
page.recentlyAudibleChanged.connect(self.recently_audible_changed)
def set_muted(self, muted: bool):
page = self._widget.page()
page.setAudioMuted(muted)
def is_muted(self):
page = self._widget.page()
return page.isAudioMuted()
def is_recently_audible(self):
page = self._widget.page()
return page.recentlyAudible()
class _WebEnginePermissions(QObject):
"""Handling of various permission-related signals."""
_abort_questions = pyqtSignal()
def __init__(self, tab, parent=None):
super().__init__(parent)
self._tab = tab
self._widget = None
def connect_signals(self):
"""Connect related signals from the QWebEnginePage."""
page = self._widget.page()
page.fullScreenRequested.connect(
self._on_fullscreen_requested)
page.featurePermissionRequested.connect(
self._on_feature_permission_requested)
if qtutils.version_check('5.11'):
page.quotaRequested.connect(
self._on_quota_requested)
page.registerProtocolHandlerRequested.connect(
self._on_register_protocol_handler_requested)
self._tab.shutting_down.connect(self._abort_questions)
self._tab.load_started.connect(self._abort_questions)
@pyqtSlot('QWebEngineFullScreenRequest')
def _on_fullscreen_requested(self, request):
request.accept()
on = request.toggleOn()
self._tab.data.fullscreen = on
self._tab.fullscreen_requested.emit(on)
if on:
notification = miscwidgets.FullscreenNotification(self._widget)
notification.show()
notification.set_timeout(3000)
@pyqtSlot(QUrl, 'QWebEnginePage::Feature')
def _on_feature_permission_requested(self, url, feature):
"""Ask the user for approval for geolocation/media/etc.."""
options = {
QWebEnginePage.Geolocation: 'content.geolocation',
QWebEnginePage.MediaAudioCapture: 'content.media_capture',
QWebEnginePage.MediaVideoCapture: 'content.media_capture',
QWebEnginePage.MediaAudioVideoCapture: 'content.media_capture',
}
messages = {
QWebEnginePage.Geolocation: 'access your location',
QWebEnginePage.MediaAudioCapture: 'record audio',
QWebEnginePage.MediaVideoCapture: 'record video',
QWebEnginePage.MediaAudioVideoCapture: 'record audio/video',
}
try:
options.update({
QWebEnginePage.DesktopVideoCapture:
'content.desktop_capture',
QWebEnginePage.DesktopAudioVideoCapture:
'content.desktop_capture',
})
messages.update({
QWebEnginePage.DesktopVideoCapture:
'capture your desktop',
QWebEnginePage.DesktopAudioVideoCapture:
'capture your desktop and audio',
})
except AttributeError:
# Added in Qt 5.10
pass
assert options.keys() == messages.keys()
page = self._widget.page()
if feature not in options:
log.webview.error("Unhandled feature permission {}".format(
debug.qenum_key(QWebEnginePage, feature)))
page.setFeaturePermission(url, feature,
QWebEnginePage.PermissionDeniedByUser)
return
yes_action = functools.partial(
page.setFeaturePermission, url, feature,
QWebEnginePage.PermissionGrantedByUser)
no_action = functools.partial(
page.setFeaturePermission, url, feature,
QWebEnginePage.PermissionDeniedByUser)
question = shared.feature_permission(
url=url, option=options[feature], msg=messages[feature],
yes_action=yes_action, no_action=no_action,
abort_on=[self._abort_questions])
if question is not None:
page.featurePermissionRequestCanceled.connect(
functools.partial(self._on_feature_permission_cancelled,
question, url, feature))
def _on_feature_permission_cancelled(self, question, url, feature,
cancelled_url, cancelled_feature):
"""Slot invoked when a feature permission request was cancelled.
To be used with functools.partial.
"""
if url == cancelled_url and feature == cancelled_feature:
try:
question.abort()
except RuntimeError:
# The question could already be deleted, e.g. because it was
# aborted after a loadStarted signal.
pass
def _on_quota_requested(self, request):
size = utils.format_size(request.requestedSize())
shared.feature_permission(
url=request.origin(),
option='content.persistent_storage',
msg='use {} of persistent storage'.format(size),
yes_action=request.accept, no_action=request.reject,
abort_on=[self._abort_questions],
blocking=True)
def _on_register_protocol_handler_requested(self, request):
shared.feature_permission(
url=request.origin(),
option='content.register_protocol_handler',
msg='open all {} links'.format(request.scheme()),
yes_action=request.accept, no_action=request.reject,
abort_on=[self._abort_questions],
blocking=True)
class _WebEngineScripts(QObject):
def __init__(self, tab, parent=None):
super().__init__(parent)
self._tab = tab
self._widget = None
self._greasemonkey = objreg.get('greasemonkey')
def connect_signals(self):
config.instance.changed.connect(self._on_config_changed)
@pyqtSlot(str)
def _on_config_changed(self, option):
if option in ['scrolling.bar', 'content.user_stylesheets']:
self._init_stylesheet()
self._update_stylesheet()
def _update_stylesheet(self):
"""Update the custom stylesheet in existing tabs."""
css = shared.get_user_stylesheet()
code = javascript.assemble('stylesheet', 'set_css', css)
self._tab.run_js_async(code)
def _inject_early_js(self, name, js_code, *,
world=QWebEngineScript.ApplicationWorld,
subframes=False):
"""Inject the given script to run early on a page load.
This runs the script both on DocumentCreation and DocumentReady as on
some internal pages, DocumentCreation will not work.
That is a WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66011
"""
scripts = self._widget.page().scripts()
for injection in ['creation', 'ready']:
injection_points = {
'creation': QWebEngineScript.DocumentCreation,
'ready': QWebEngineScript.DocumentReady,
}
script = QWebEngineScript()
script.setInjectionPoint(injection_points[injection])
script.setSourceCode(js_code)
script.setWorldId(world)
script.setRunsOnSubFrames(subframes)
script.setName('_qute_{}_{}'.format(name, injection))
scripts.insert(script)
def _remove_early_js(self, name):
"""Remove an early QWebEngineScript."""
scripts = self._widget.page().scripts()
for injection in ['creation', 'ready']:
full_name = '_qute_{}_{}'.format(name, injection)
script = scripts.findScript(full_name)
if not script.isNull():
scripts.remove(script)
def init(self):
"""Initialize global qutebrowser JavaScript."""
js_code = javascript.wrap_global(
'scripts',
utils.read_file('javascript/scroll.js'),
utils.read_file('javascript/webelem.js'),
utils.read_file('javascript/caret.js'),
)
self._inject_early_js('js',
utils.read_file('javascript/print.js'),
subframes=True,
world=QWebEngineScript.MainWorld)
# FIXME:qtwebengine what about subframes=True?
self._inject_early_js('js', js_code, subframes=True)
self._init_stylesheet()
# The Greasemonkey metadata block support in QtWebEngine only starts at
# Qt 5.8. With 5.7.1, we need to inject the scripts ourselves in
# response to urlChanged.
if not qtutils.version_check('5.8'):
self._tab.url_changed.connect(
self._inject_greasemonkey_scripts_for_url)
else:
self._greasemonkey.scripts_reloaded.connect(
self._inject_all_greasemonkey_scripts)
self._inject_all_greasemonkey_scripts()
def _init_stylesheet(self):
"""Initialize custom stylesheets.
Partially inspired by QupZilla:
https://github.com/QupZilla/qupzilla/blob/v2.0/src/lib/app/mainapplication.cpp#L1063-L1101
"""
self._remove_early_js('stylesheet')
css = shared.get_user_stylesheet()
js_code = javascript.wrap_global(
'stylesheet',
utils.read_file('javascript/stylesheet.js'),
javascript.assemble('stylesheet', 'set_css', css),
)
self._inject_early_js('stylesheet', js_code, subframes=True)
@pyqtSlot(QUrl)
def _inject_greasemonkey_scripts_for_url(self, url):
matching_scripts = self._greasemonkey.scripts_for(url)
self._inject_greasemonkey_scripts(
matching_scripts.start, QWebEngineScript.DocumentCreation, True)
self._inject_greasemonkey_scripts(
matching_scripts.end, QWebEngineScript.DocumentReady, False)
self._inject_greasemonkey_scripts(
matching_scripts.idle, QWebEngineScript.Deferred, False)
@pyqtSlot()
def _inject_all_greasemonkey_scripts(self):
scripts = self._greasemonkey.all_scripts()
self._inject_greasemonkey_scripts(scripts)
def _inject_greasemonkey_scripts(self, scripts=None, injection_point=None,
remove_first=True):
"""Register user JavaScript files with the current tab.
Args:
scripts: A list of GreasemonkeyScripts, or None to add all
known by the Greasemonkey subsystem.
injection_point: The QWebEngineScript::InjectionPoint stage
to inject the script into, None to use
auto-detection.
remove_first: Whether to remove all previously injected
scripts before adding these ones.
"""
if sip.isdeleted(self._widget):
return
# Since we are inserting scripts into a per-tab collection,
# rather than just injecting scripts on page load, we need to
# make sure we replace existing scripts, not just add new ones.
# While, taking care not to remove any other scripts that might
# have been added elsewhere, like the one for stylesheets.
page_scripts = self._widget.page().scripts()
if remove_first:
for script in page_scripts.toList():
if script.name().startswith("GM-"):
log.greasemonkey.debug('Removing script: {}'
.format(script.name()))
removed = page_scripts.remove(script)
assert removed, script.name()
if not scripts:
return
for script in scripts:
new_script = QWebEngineScript()
try:
world = int(script.jsworld)
except ValueError:
try:
world = _JS_WORLD_MAP[usertypes.JsWorld[
script.jsworld.lower()]]
except KeyError:
log.greasemonkey.error(
"script {} has invalid value for '@qute-js-world'"
": {}".format(script.name, script.jsworld))
continue
new_script.setWorldId(world)
new_script.setSourceCode(script.code())
new_script.setName("GM-{}".format(script.name))
new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
# Override the @run-at value parsed by QWebEngineScript if desired.
if injection_point:
new_script.setInjectionPoint(injection_point)
log.greasemonkey.debug('adding script: {}'
.format(new_script.name()))
page_scripts.insert(new_script)
class WebEngineTab(browsertab.AbstractTab):
"""A QtWebEngine tab in the browser.
@@ -622,41 +979,32 @@ class WebEngineTab(browsertab.AbstractTab):
self.printing = WebEnginePrinting()
self.elements = WebEngineElements(tab=self)
self.action = WebEngineAction(tab=self)
self.audio = WebEngineAudio(parent=self)
self._permissions = _WebEnginePermissions(tab=self, parent=self)
self._scripts = _WebEngineScripts(tab=self, parent=self)
# We're assigning settings in _set_widget
self.settings = webenginesettings.WebEngineSettings(settings=None)
self._set_widget(widget)
self._connect_signals()
self.backend = usertypes.Backend.QtWebEngine
self._init_js()
self._child_event_filter = None
self._saved_zoom = None
self._reload_url = None
self._scripts.init()
def _init_js(self):
js_code = '\n'.join([
'"use strict";',
'window._qutebrowser = window._qutebrowser || {};',
utils.read_file('javascript/scroll.js'),
utils.read_file('javascript/webelem.js'),
utils.read_file('javascript/caret.js'),
])
script = QWebEngineScript()
# We can't use DocumentCreation here as WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-66011
script.setInjectionPoint(QWebEngineScript.DocumentReady)
script.setSourceCode(js_code)
page = self._widget.page()
script.setWorldId(QWebEngineScript.ApplicationWorld)
# FIXME:qtwebengine what about runsOnSubFrames?
page.scripts().insert(script)
def _set_widget(self, widget):
# pylint: disable=protected-access
super()._set_widget(widget)
self._permissions._widget = widget
self._scripts._widget = widget
def _install_event_filter(self):
self._widget.focusProxy().installEventFilter(self._mouse_event_filter)
fp = self._widget.focusProxy()
if fp is not None:
fp.installEventFilter(self._mouse_event_filter)
self._child_event_filter = mouse.ChildEventFilter(
eventfilter=self._mouse_event_filter, widget=self._widget,
parent=self)
win_id=self.win_id, parent=self)
self._widget.installEventFilter(self._child_event_filter)
@pyqtSlot()
@@ -669,9 +1017,18 @@ class WebEngineTab(browsertab.AbstractTab):
self.zoom.set_factor(self._saved_zoom)
self._saved_zoom = None
def openurl(self, url):
def openurl(self, url, *, predict=True):
"""Open the given URL in this tab.
Arguments:
url: The QUrl to open.
predict: If set to False, predicted_navigation is not emitted.
"""
if sip.isdeleted(self._widget):
# https://github.com/qutebrowser/qutebrowser/issues/3896
return
self._saved_zoom = self.zoom.factor()
self._openurl_prepare(url)
self._openurl_prepare(url, predict=predict)
self._widget.load(url)
def url(self, requested=False):
@@ -706,7 +1063,6 @@ class WebEngineTab(browsertab.AbstractTab):
self._widget.shutdown()
def reload(self, *, force=False):
self.predicted_navigation.emit(self.url())
if force:
action = QWebEnginePage.ReloadAndBypassCache
else:
@@ -826,26 +1182,13 @@ class WebEngineTab(browsertab.AbstractTab):
# https://www.riverbankcomputing.com/pipermail/pyqt/2016-December/038400.html
self._show_error_page(url, "Authentication required")
@pyqtSlot('QWebEngineFullScreenRequest')
def _on_fullscreen_requested(self, request):
request.accept()
on = request.toggleOn()
self.data.fullscreen = on
self.fullscreen_requested.emit(on)
if on:
notification = miscwidgets.FullscreenNotification(self)
notification.show()
notification.set_timeout(3000)
@pyqtSlot()
def _on_load_started(self):
"""Clear search when a new load is started if needed."""
if (qtutils.version_check('5.9', compiled=False) and
not qtutils.version_check('5.9.2', compiled=False)):
# WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-61506
self.search.clear()
# WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-61506
# (seems to be back in later Qt versions as well)
self.search.clear()
super()._on_load_started()
self.data.netrc_used = False
@@ -915,10 +1258,11 @@ class WebEngineTab(browsertab.AbstractTab):
if ok and self._reload_url is not None:
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66656
log.config.debug(
"Reloading {} because of config change".format(
"Loading {} again because of config change".format(
self._reload_url.toDisplayString()))
QTimer.singleShot(100, lambda url=self._reload_url:
self.openurl(url))
QTimer.singleShot(100, functools.partial(self.openurl,
self._reload_url,
predict=False))
self._reload_url = None
if not qtutils.version_check('5.10', compiled=False):
@@ -928,32 +1272,82 @@ class WebEngineTab(browsertab.AbstractTab):
# the old icon is still displayed.
self.icon_changed.emit(QIcon())
@pyqtSlot(certificateerror.CertificateErrorWrapper)
def _on_ssl_errors(self, error):
self._has_ssl_errors = True
url = error.url()
log.webview.debug("Certificate error: {}".format(error))
if error.is_overridable():
error.ignore = shared.ignore_certificate_errors(
url, [error], abort_on=[self.shutting_down, self.load_started])
else:
log.webview.error("Non-overridable certificate error: "
"{}".format(error))
log.webview.debug("ignore {}, URL {}, requested {}".format(
error.ignore, url, self.url(requested=True)))
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-56207
# We can't really know when to show an error page, as the error might
# have happened when loading some resource.
# However, self.url() is not available yet and the requested URL
# might not match the URL we get from the error - so we just apply a
# heuristic here.
if (not qtutils.version_check('5.9') and
not error.ignore and
url.matches(self.url(requested=True), QUrl.RemoveScheme)):
self._show_error_page(url, str(error))
@pyqtSlot(QUrl)
def _on_predicted_navigation(self, url):
"""If we know we're going to visit an URL soon, change the settings."""
self.settings.update_for_url(url)
"""If we know we're going to visit an URL soon, change the settings.
This is a WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66656
"""
super()._on_predicted_navigation(url)
if not qtutils.version_check('5.11.1', compiled=False):
self.settings.update_for_url(url)
@pyqtSlot(usertypes.NavigationRequest)
def _on_navigation_request(self, navigation):
super()._on_navigation_request(navigation)
if navigation.url == QUrl('qute://print'):
command_dispatcher = objreg.get('command-dispatcher',
scope='window',
window=self.win_id)
command_dispatcher.printpage()
navigation.accepted = False
if not navigation.accepted or not navigation.is_main_frame:
return
needs_reload = {
settings_needing_reload = {
'content.plugins',
'content.javascript.enabled',
'content.javascript.can_access_clipboard',
'content.javascript.can_access_clipboard',
'content.print_element_backgrounds',
'input.spatial_navigation',
'input.spatial_navigation',
}
assert needs_reload.issubset(configdata.DATA)
assert settings_needing_reload.issubset(configdata.DATA)
changed = self.settings.update_for_url(navigation.url)
if (changed & needs_reload and navigation.navigation_type !=
navigation.Type.link_clicked):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66656
reload_needed = changed & settings_needing_reload
# On Qt < 5.11, we don't don't need a reload when type == link_clicked.
# On Qt 5.11.0, we always need a reload.
# On Qt > 5.11.0, we never need a reload:
# https://codereview.qt-project.org/#/c/229525/1
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66656
if qtutils.version_check('5.11.1', compiled=False):
reload_needed = False
elif not qtutils.version_check('5.11.0', exact=True, compiled=False):
if navigation.navigation_type == navigation.Type.link_clicked:
reload_needed = False
if reload_needed:
self._reload_url = navigation.url
def _connect_signals(self):
@@ -968,7 +1362,6 @@ class WebEngineTab(browsertab.AbstractTab):
page.authenticationRequired.connect(self._on_authentication_required)
page.proxyAuthenticationRequired.connect(
self._on_proxy_authentication_required)
page.fullScreenRequested.connect(self._on_fullscreen_requested)
page.contentsSizeChanged.connect(self.contents_size_changed)
page.navigation_request.connect(self._on_navigation_request)
@@ -993,5 +1386,10 @@ class WebEngineTab(browsertab.AbstractTab):
self.predicted_navigation.connect(self._on_predicted_navigation)
# pylint: disable=protected-access
self.audio._connect_signals()
self._permissions.connect_signals()
self._scripts.connect_signals()
def event_target(self):
return self._widget.focusProxy()
return self._widget.render_widget()

View File

@@ -19,17 +19,17 @@
"""The main browser widget for QtWebEngine."""
import functools
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, PYQT_VERSION
from PyQt5.QtCore import pyqtSignal, QUrl, PYQT_VERSION
from PyQt5.QtGui import QPalette
from PyQt5.QtWebEngineWidgets import (QWebEngineView, QWebEnginePage,
QWebEngineScript)
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
from qutebrowser.browser import shared
from qutebrowser.browser.webengine import certificateerror, webenginesettings
from qutebrowser.browser.webengine import webenginesettings, certificateerror
from qutebrowser.config import config
from qutebrowser.utils import log, debug, usertypes, jinja, objreg, qtutils
from qutebrowser.utils import log, debug, usertypes, objreg, qtutils
from qutebrowser.misc import miscwidgets
from qutebrowser.qt import sip
class WebEngineView(QWebEngineView):
@@ -51,6 +51,35 @@ class WebEngineView(QWebEngineView):
parent=self)
self.setPage(page)
if qtutils.version_check('5.11', compiled=False):
# Set a PseudoLayout as a WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-68224
# and other related issues.
sip.delete(self.layout())
self._layout = miscwidgets.PseudoLayout(self)
def render_widget(self):
"""Get the RenderWidgetHostViewQt for this view.
Normally, this would always be the focusProxy().
However, it sometimes isn't, so we use this as a WORKAROUND for
https://bugreports.qt.io/browse/QTBUG-68727
"""
if 'lost-focusproxy' not in objreg.get('args').debug_flags:
proxy = self.focusProxy()
if proxy is not None:
return proxy
# We don't want e.g. a QMenu.
rwhv_class = 'QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget'
children = [c for c in self.findChildren(QWidget)
if c.isVisible() and c.inherits(rwhv_class)]
log.webview.debug("Found possibly lost focusProxy: {}"
.format(children))
return children[-1] if children else None
def shutdown(self):
self.page().shutdown()
@@ -122,23 +151,22 @@ class WebEnginePage(QWebEnginePage):
Signals:
certificate_error: Emitted on certificate errors.
Needs to be directly connected to a slot setting the
'ignore' attribute.
shutting_down: Emitted when the page is shutting down.
navigation_request: Emitted on acceptNavigationRequest.
"""
certificate_error = pyqtSignal()
certificate_error = pyqtSignal(certificateerror.CertificateErrorWrapper)
shutting_down = pyqtSignal()
navigation_request = pyqtSignal(usertypes.NavigationRequest)
def __init__(self, *, theme_color, profile, parent=None):
super().__init__(profile, parent)
self._is_shutting_down = False
self.featurePermissionRequested.connect(
self._on_feature_permission_requested)
self._theme_color = theme_color
self._set_bg_color()
config.instance.changed.connect(self._set_bg_color)
self.urlChanged.connect(self._inject_userjs)
@config.change_filter('colors.webpage.bg')
def _set_bg_color(self):
@@ -147,97 +175,15 @@ class WebEnginePage(QWebEnginePage):
col = self._theme_color
self.setBackgroundColor(col)
@pyqtSlot(QUrl, 'QWebEnginePage::Feature')
def _on_feature_permission_requested(self, url, feature):
"""Ask the user for approval for geolocation/media/etc.."""
options = {
QWebEnginePage.Geolocation: 'content.geolocation',
QWebEnginePage.MediaAudioCapture: 'content.media_capture',
QWebEnginePage.MediaVideoCapture: 'content.media_capture',
QWebEnginePage.MediaAudioVideoCapture: 'content.media_capture',
}
messages = {
QWebEnginePage.Geolocation: 'access your location',
QWebEnginePage.MediaAudioCapture: 'record audio',
QWebEnginePage.MediaVideoCapture: 'record video',
QWebEnginePage.MediaAudioVideoCapture: 'record audio/video',
}
assert options.keys() == messages.keys()
if feature not in options:
log.webview.error("Unhandled feature permission {}".format(
debug.qenum_key(QWebEnginePage, feature)))
self.setFeaturePermission(url, feature,
QWebEnginePage.PermissionDeniedByUser)
return
yes_action = functools.partial(
self.setFeaturePermission, url, feature,
QWebEnginePage.PermissionGrantedByUser)
no_action = functools.partial(
self.setFeaturePermission, url, feature,
QWebEnginePage.PermissionDeniedByUser)
question = shared.feature_permission(
url=url, option=options[feature], msg=messages[feature],
yes_action=yes_action, no_action=no_action,
abort_on=[self.shutting_down, self.loadStarted])
if question is not None:
self.featurePermissionRequestCanceled.connect(
functools.partial(self._on_feature_permission_cancelled,
question, url, feature))
def _on_feature_permission_cancelled(self, question, url, feature,
cancelled_url, cancelled_feature):
"""Slot invoked when a feature permission request was cancelled.
To be used with functools.partial.
"""
if url == cancelled_url and feature == cancelled_feature:
try:
question.abort()
except RuntimeError:
# The question could already be deleted, e.g. because it was
# aborted after a loadStarted signal.
pass
def shutdown(self):
self._is_shutting_down = True
self.shutting_down.emit()
def certificateError(self, error):
"""Handle certificate errors coming from Qt."""
self.certificate_error.emit()
url = error.url()
error = certificateerror.CertificateErrorWrapper(error)
log.webview.debug("Certificate error: {}".format(error))
url_string = url.toDisplayString()
error_page = jinja.render(
'error.html', title="Error loading page: {}".format(url_string),
url=url_string, error=str(error))
if error.is_overridable():
ignore = shared.ignore_certificate_errors(
url, [error], abort_on=[self.loadStarted, self.shutting_down])
else:
log.webview.error("Non-overridable certificate error: "
"{}".format(error))
ignore = False
# We can't really know when to show an error page, as the error might
# have happened when loading some resource.
# However, self.url() is not available yet and self.requestedUrl()
# might not match the URL we get from the error - so we just apply a
# heuristic here.
# See https://bugreports.qt.io/browse/QTBUG-56207
log.webview.debug("ignore {}, URL {}, requested {}".format(
ignore, url, self.requestedUrl()))
if not ignore and url.matches(self.requestedUrl(), QUrl.RemoveScheme):
self.setHtml(error_page)
return ignore
self.certificate_error.emit(error)
return error.ignore
def javaScriptConfirm(self, url, js_msg):
"""Override javaScriptConfirm to use qutebrowser prompts."""
@@ -315,43 +261,3 @@ class WebEnginePage(QWebEnginePage):
is_main_frame=is_main_frame)
self.navigation_request.emit(navigation)
return navigation.accepted
@pyqtSlot('QUrl')
def _inject_userjs(self, url):
"""Inject userscripts registered for `url` into the current page."""
if qtutils.version_check('5.8'):
# Handled in webenginetab with the builtin Greasemonkey
# support.
return
# Using QWebEnginePage.scripts() to hold the user scripts means
# we don't have to worry ourselves about where to inject the
# page but also means scripts hang around for the tab lifecycle.
# So clear them here.
scripts = self.scripts()
for script in scripts.toList():
if script.name().startswith("GM-"):
log.greasemonkey.debug("Removing script: {}"
.format(script.name()))
removed = scripts.remove(script)
assert removed, script.name()
def _add_script(script, injection_point):
new_script = QWebEngineScript()
new_script.setInjectionPoint(injection_point)
new_script.setWorldId(QWebEngineScript.MainWorld)
new_script.setSourceCode(script.code())
new_script.setName("GM-{}".format(script.name))
new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
log.greasemonkey.debug("Adding script: {}"
.format(new_script.name()))
scripts.insert(new_script)
greasemonkey = objreg.get('greasemonkey')
matching_scripts = greasemonkey.scripts_for(url)
for script in matching_scripts.start:
_add_script(script, QWebEngineScript.DocumentCreation)
for script in matching_scripts.end:
_add_script(script, QWebEngineScript.DocumentReady)
for script in matching_scripts.idle:
_add_script(script, QWebEngineScript.Deferred)

View File

@@ -28,7 +28,8 @@ from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QCoreApplication, QUrl,
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QSslSocket
from qutebrowser.config import config
from qutebrowser.utils import message, log, usertypes, utils, objreg, urlutils
from qutebrowser.utils import (message, log, usertypes, utils, objreg,
urlutils, debug)
from qutebrowser.browser import shared
from qutebrowser.browser.webkit import certificateerror
from qutebrowser.browser.webkit.network import (webkitqutescheme, networkreply,
@@ -147,6 +148,7 @@ class NetworkManager(QNetworkAccessManager):
super().__init__(parent)
log.init.debug("NetworkManager init done")
self.adopted_downloads = 0
self._args = objreg.get('args')
self._win_id = win_id
self._tab_id = tab_id
self._private = private
@@ -378,7 +380,7 @@ class NetworkManager(QNetworkAccessManager):
result.setParent(self)
return result
for header, value in shared.custom_headers():
for header, value in shared.custom_headers(url=req.url()):
req.setRawHeader(header, value)
host_blocker = objreg.get('host-blocker')
@@ -406,5 +408,13 @@ class NetworkManager(QNetworkAccessManager):
# the webpage shutdown here.
current_url = QUrl()
if 'log-requests' in self._args.debug_flags:
operation = debug.qenum_key(QNetworkAccessManager, op)
operation = operation.replace('Operation', '').upper()
log.webview.debug("{} {}, first-party {}".format(
operation,
req.url().toDisplayString(),
current_url.toDisplayString()))
self.set_referer(req, current_url)
return super().createRequest(op, req, outgoing_data)

View File

@@ -305,6 +305,9 @@ class WebKitElement(webelem.AbstractWebElement):
if self.is_text_input() and self.is_editable():
self._tab.caret.move_to_end_of_document()
def _requires_user_interaction(self):
return False
def _click_editable(self, click_target):
ok = self._elem.evaluateJavaScript('this.focus(); true;')
if ok:

View File

@@ -19,11 +19,10 @@
"""Customized QWebInspector for QtWebKit."""
from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebKitWidgets import QWebInspector
from qutebrowser.browser import inspector
from qutebrowser.config import config
class WebKitInspector(inspector.AbstractWebInspector):
@@ -36,9 +35,6 @@ class WebKitInspector(inspector.AbstractWebInspector):
self._set_widget(qwebinspector)
def inspect(self, page):
if not config.val.content.developer_extras:
raise inspector.WebInspectorError(
"Please enable content.developer_extras before using the "
"webinspector!")
settings = QWebSettings.globalSettings()
settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
self._widget.setPage(page)
self.show()

View File

@@ -30,6 +30,7 @@ from PyQt5.QtGui import QFont
from PyQt5.QtWebKit import QWebSettings
from qutebrowser.config import config, websettings
from qutebrowser.config.websettings import AttributeInfo as Attr
from qutebrowser.utils import standarddir, urlutils
from qutebrowser.browser import shared
@@ -44,50 +45,48 @@ class WebKitSettings(websettings.AbstractSettings):
_ATTRIBUTES = {
'content.images':
[QWebSettings.AutoLoadImages],
Attr(QWebSettings.AutoLoadImages),
'content.javascript.enabled':
[QWebSettings.JavascriptEnabled],
Attr(QWebSettings.JavascriptEnabled),
'content.javascript.can_open_tabs_automatically':
[QWebSettings.JavascriptCanOpenWindows],
Attr(QWebSettings.JavascriptCanOpenWindows),
'content.javascript.can_close_tabs':
[QWebSettings.JavascriptCanCloseWindows],
Attr(QWebSettings.JavascriptCanCloseWindows),
'content.javascript.can_access_clipboard':
[QWebSettings.JavascriptCanAccessClipboard],
Attr(QWebSettings.JavascriptCanAccessClipboard),
'content.plugins':
[QWebSettings.PluginsEnabled],
Attr(QWebSettings.PluginsEnabled),
'content.webgl':
[QWebSettings.WebGLEnabled],
Attr(QWebSettings.WebGLEnabled),
'content.hyperlink_auditing':
[QWebSettings.HyperlinkAuditingEnabled],
Attr(QWebSettings.HyperlinkAuditingEnabled),
'content.local_content_can_access_remote_urls':
[QWebSettings.LocalContentCanAccessRemoteUrls],
Attr(QWebSettings.LocalContentCanAccessRemoteUrls),
'content.local_content_can_access_file_urls':
[QWebSettings.LocalContentCanAccessFileUrls],
Attr(QWebSettings.LocalContentCanAccessFileUrls),
'content.dns_prefetch':
[QWebSettings.DnsPrefetchEnabled],
Attr(QWebSettings.DnsPrefetchEnabled),
'content.frame_flattening':
[QWebSettings.FrameFlatteningEnabled],
Attr(QWebSettings.FrameFlatteningEnabled),
'content.cache.appcache':
[QWebSettings.OfflineWebApplicationCacheEnabled],
Attr(QWebSettings.OfflineWebApplicationCacheEnabled),
'content.local_storage':
[QWebSettings.LocalStorageEnabled,
QWebSettings.OfflineStorageDatabaseEnabled],
'content.developer_extras':
[QWebSettings.DeveloperExtrasEnabled],
Attr(QWebSettings.LocalStorageEnabled,
QWebSettings.OfflineStorageDatabaseEnabled),
'content.print_element_backgrounds':
[QWebSettings.PrintElementBackgrounds],
Attr(QWebSettings.PrintElementBackgrounds),
'content.xss_auditing':
[QWebSettings.XSSAuditingEnabled],
Attr(QWebSettings.XSSAuditingEnabled),
'input.spatial_navigation':
[QWebSettings.SpatialNavigationEnabled],
Attr(QWebSettings.SpatialNavigationEnabled),
'input.links_included_in_focus_chain':
[QWebSettings.LinksIncludedInFocusChain],
Attr(QWebSettings.LinksIncludedInFocusChain),
'zoom.text_only':
[QWebSettings.ZoomTextOnly],
Attr(QWebSettings.ZoomTextOnly),
'scrolling.smooth':
[QWebSettings.ScrollAnimatorEnabled],
Attr(QWebSettings.ScrollAnimatorEnabled),
}
_FONT_SIZES = {

View File

@@ -23,11 +23,6 @@ import re
import functools
import xml.etree.ElementTree
import pygments
import pygments.lexers
import pygments.formatters
import sip
from PyQt5.QtCore import (pyqtSlot, Qt, QEvent, QUrl, QPoint, QTimer, QSizeF,
QSize)
from PyQt5.QtGui import QKeyEvent, QIcon
@@ -38,7 +33,8 @@ from PyQt5.QtPrintSupport import QPrinter
from qutebrowser.browser import browsertab, shared
from qutebrowser.browser.webkit import (webview, tabhistory, webkitelem,
webkitsettings)
from qutebrowser.utils import qtutils, objreg, usertypes, utils, log, debug
from qutebrowser.utils import qtutils, usertypes, utils, log, debug
from qutebrowser.qt import sip
class WebKitAction(browsertab.AbstractAction):
@@ -55,28 +51,8 @@ class WebKitAction(browsertab.AbstractAction):
"""Save the current page."""
raise browsertab.UnsupportedOperationError
def show_source(self):
def show_source_cb(source):
"""Show source as soon as it's ready."""
# WORKAROUND for https://github.com/PyCQA/pylint/issues/491
# pylint: disable=no-member
lexer = pygments.lexers.HtmlLexer()
formatter = pygments.formatters.HtmlFormatter(
full=True, linenos='table')
# pylint: enable=no-member
highlighted = pygments.highlight(source, lexer, formatter)
tb = objreg.get('tabbed-browser', scope='window',
window=self._tab.win_id)
new_tab = tb.tabopen(background=False, related=True)
# The original URL becomes the path of a view-source: URL
# (without a host), but query/fragment should stay.
url = QUrl('view-source:' + urlstr)
new_tab.set_html(highlighted, url)
urlstr = self._tab.url().toString(QUrl.RemoveUserInfo)
self._tab.dump_async(show_source_cb)
def show_source(self, pygments=False):
self._show_source_pygments()
class WebKitPrinting(browsertab.AbstractPrinting):
@@ -196,9 +172,10 @@ class WebKitCaret(browsertab.AbstractCaret):
if mode != usertypes.KeyMode.caret:
return
self.selection_enabled = self._widget.hasSelection()
self.selection_toggled.emit(self.selection_enabled)
settings = self._widget.settings()
settings.setAttribute(QWebSettings.CaretBrowsingEnabled, True)
self.selection_enabled = self._widget.hasSelection()
if self._widget.isVisible():
# Sometimes the caret isn't immediately visible, but unfocusing
@@ -363,9 +340,7 @@ class WebKitCaret(browsertab.AbstractCaret):
def toggle_selection(self):
self.selection_enabled = not self.selection_enabled
mainwindow = objreg.get('main-window', scope='window',
window=self._tab.win_id)
mainwindow.status.set_mode_active(usertypes.KeyMode.caret, True)
self.selection_toggled.emit(self.selection_enabled)
def drop_selection(self):
self._widget.triggerPageAction(QWebPage.MoveToNextChar)
@@ -378,11 +353,22 @@ class WebKitCaret(browsertab.AbstractCaret):
QWebSettings.JavascriptEnabled):
if tab:
self._tab.data.override_target = usertypes.ClickTarget.tab
self._tab.run_js_async(
'window.getSelection().anchorNode.parentNode.click()')
self._tab.run_js_async("""
const aElm = document.activeElement;
if (window.getSelection().anchorNode) {
window.getSelection().anchorNode.parentNode.click();
} else if (aElm && aElm !== document.body) {
aElm.click();
}
""")
else:
selection = self._widget.selectedHtml()
if not selection:
# Getting here may mean we crashed, but we can't do anything
# about that until this commit is released:
# https://github.com/annulen/webkit/commit/0e75f3272d149bc64899c161f150eb341a2417af
# TODO find a way to check if something is focused
self._follow_enter(tab)
return
try:
selected_element = xml.etree.ElementTree.fromstring(
@@ -427,6 +413,9 @@ class WebKitScroller(browsertab.AbstractScroller):
def to_point(self, point):
self._widget.page().mainFrame().setScrollPosition(point)
def to_anchor(self, name):
self._widget.page().mainFrame().scrollToAnchor(name)
def delta(self, x=0, y=0):
qtutils.check_overflow(x, 'int')
qtutils.check_overflow(y, 'int')
@@ -537,6 +526,9 @@ class WebKitHistory(browsertab.AbstractHistory):
return qtutils.deserialize(data, self._history)
def load_items(self, items):
if items:
self._tab.predicted_navigation.emit(items[-1].url)
stream, _data, user_data = tabhistory.serialize(items)
qtutils.deserialize_stream(stream, self._history)
for i, data in enumerate(user_data):
@@ -635,6 +627,20 @@ class WebKitElements(browsertab.AbstractElements):
callback(elem)
class WebKitAudio(browsertab.AbstractAudio):
"""Dummy handling of audio status for QtWebKit."""
def set_muted(self, muted: bool):
raise browsertab.WebTabError('Muting is not supported on QtWebKit!')
def is_muted(self):
return False
def is_recently_audible(self):
return False
class WebKitTab(browsertab.AbstractTab):
"""A QtWebKit tab in the browser."""
@@ -655,6 +661,7 @@ class WebKitTab(browsertab.AbstractTab):
self.printing = WebKitPrinting()
self.elements = WebKitElements(tab=self)
self.action = WebKitAction(tab=self)
self.audio = WebKitAudio(parent=self)
# We're assigning settings in _set_widget
self.settings = webkitsettings.WebKitSettings(settings=None)
self._set_widget(widget)
@@ -668,8 +675,8 @@ class WebKitTab(browsertab.AbstractTab):
settings = widget.settings()
settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True)
def openurl(self, url):
self._openurl_prepare(url)
def openurl(self, url, *, predict=True):
self._openurl_prepare(url, predict=predict)
self._widget.openurl(url)
def url(self, requested=False):
@@ -701,7 +708,6 @@ class WebKitTab(browsertab.AbstractTab):
self._widget.shutdown()
def reload(self, *, force=False):
self.predicted_navigation.emit(self.url())
if force:
action = QWebPage.ReloadAndBypassCache
else:
@@ -802,6 +808,10 @@ class WebKitTab(browsertab.AbstractTab):
if navigation.is_main_frame:
self.settings.update_for_url(navigation.url)
@pyqtSlot()
def _on_ssl_errors(self):
self._has_ssl_errors = True
def _connect_signals(self):
view = self._widget
page = view.page()

View File

@@ -22,7 +22,6 @@
import html
import functools
import sip
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QPoint
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
@@ -35,6 +34,7 @@ from qutebrowser.browser import pdfjs, shared
from qutebrowser.browser.webkit import http
from qutebrowser.browser.webkit.network import networkmanager
from qutebrowser.utils import message, usertypes, log, jinja, objreg
from qutebrowser.qt import sip
class BrowserPage(QWebPage):
@@ -212,7 +212,8 @@ class BrowserPage(QWebPage):
page = pdfjs.generate_pdfjs_page(reply.url())
except pdfjs.PDFJSNotFound:
page = jinja.render('no_pdfjs.html',
url=reply.url().toDisplayString())
url=reply.url().toDisplayString(),
title="PDF.js not found")
self.mainFrame().setContent(page.encode('utf-8'), 'text/html',
reply.url())
reply.deleteLater()
@@ -239,7 +240,6 @@ class BrowserPage(QWebPage):
printdiag.setAttribute(Qt.WA_DeleteOnClose)
printdiag.open(lambda: frame.print(printdiag.printer()))
@pyqtSlot('QNetworkRequest')
def on_download_requested(self, request):
"""Called when the user wants to download a link.
@@ -416,7 +416,7 @@ class BrowserPage(QWebPage):
def userAgentForUrl(self, url):
"""Override QWebPage::userAgentForUrl to customize the user agent."""
ua = config.val.content.headers.user_agent
ua = config.instance.get('content.headers.user_agent', url=url)
if ua is None:
return super().userAgentForUrl(url)
else:

View File

@@ -19,7 +19,7 @@
"""The main browser widgets."""
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl
from PyQt5.QtCore import pyqtSignal, Qt, QUrl
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QStyleFactory
from PyQt5.QtWebKit import QWebSettings
@@ -78,10 +78,6 @@ class WebView(QWebView):
self.setPage(page)
mode_manager = objreg.get('mode-manager', scope='window',
window=win_id)
mode_manager.entered.connect(self.on_mode_entered)
mode_manager.left.connect(self.on_mode_left)
config.instance.changed.connect(self._set_bg_color)
def __repr__(self):
@@ -130,32 +126,6 @@ class WebView(QWebView):
"""
self.load(url)
@pyqtSlot(usertypes.KeyMode)
def on_mode_entered(self, mode):
"""Ignore attempts to focus the widget if in any status-input mode.
FIXME:qtwebengine
For QtWebEngine, doing the same has no effect, so we do it in here.
"""
if mode in [usertypes.KeyMode.command, usertypes.KeyMode.prompt,
usertypes.KeyMode.yesno]:
log.webview.debug("Ignoring focus because mode {} was "
"entered.".format(mode))
self.setFocusPolicy(Qt.NoFocus)
@pyqtSlot(usertypes.KeyMode)
def on_mode_left(self, mode):
"""Restore focus policy if status-input modes were left.
FIXME:qtwebengine
For QtWebEngine, doing the same has no effect, so we do it in here.
"""
if mode in [usertypes.KeyMode.command, usertypes.KeyMode.prompt,
usertypes.KeyMode.yesno]:
log.webview.debug("Restoring focus policy because mode {} was "
"left.".format(mode))
self.setFocusPolicy(Qt.WheelFocus)
def createWindow(self, wintype):
"""Called by Qt when a page wants to create a new window.

View File

@@ -123,6 +123,7 @@ class Command:
self.pos_args = []
self.desc = None
self.flags_with_args = []
self._has_vararg = False
# This is checked by future @cmdutils.argument calls so they fail
# (as they'd be silently ignored otherwise)
@@ -170,6 +171,8 @@ class Command:
def get_pos_arg_info(self, pos):
"""Get an ArgInfo tuple for the given positional parameter."""
if pos >= len(self.pos_args) and self._has_vararg:
pos = len(self.pos_args) - 1
name = self.pos_args[pos][0]
return self._qute_args.get(name, ArgInfo())
@@ -233,6 +236,8 @@ class Command:
log.commands.vdebug('Adding arg {} of type {} -> {}'.format(
param.name, typ, callsig))
self.parser.add_argument(*args, **kwargs)
if param.kind == inspect.Parameter.VAR_POSITIONAL:
self._has_vararg = True
return signature.parameters.values()
def _param_to_argparse_kwargs(self, param, is_bool):

View File

@@ -49,7 +49,7 @@ class Completer(QObject):
_last_cursor_pos: The old cursor position so we avoid double completion
updates.
_last_text: The old command text so we avoid double completion updates.
_last_completion_func: The completion function used for the last text.
_last_before_cursor: The prior value of before_cursor.
"""
def __init__(self, *, cmd, win_id, parent=None):
@@ -62,7 +62,7 @@ class Completer(QObject):
self._timer.timeout.connect(self._update_completion)
self._last_cursor_pos = -1
self._last_text = None
self._last_completion_func = None
self._last_before_cursor = None
self._cmd.update_completion.connect(self.schedule_completion_update)
def __repr__(self):
@@ -228,7 +228,7 @@ class Completer(QObject):
# FIXME complete searches
# https://github.com/qutebrowser/qutebrowser/issues/32
completion.set_model(None)
self._last_completion_func = None
self._last_before_cursor = None
return
before_cursor, pattern, after_cursor = self._partition()
@@ -242,11 +242,11 @@ class Completer(QObject):
if func is None:
log.completion.debug('Clearing completion')
completion.set_model(None)
self._last_completion_func = None
self._last_before_cursor = None
return
if func != self._last_completion_func:
self._last_completion_func = func
if before_cursor != self._last_before_cursor:
self._last_before_cursor = before_cursor
args = (x for x in before_cursor[1:] if not x.startswith('-'))
with debug.log_time(log.completion, 'Starting {} completion'
.format(func.__name__)):

View File

@@ -47,12 +47,12 @@ def customized_option(*, info):
return model
def value(optname, *_values, info):
def value(optname, *values, info):
"""A CompletionModel filled with setting values.
Args:
optname: The name of the config option this model shows.
_values: The values already provided on the command line.
values: The values already provided on the command line.
info: A CompletionInfo instance.
"""
model = completionmodel.CompletionModel(column_widths=(30, 70, 0))
@@ -64,13 +64,18 @@ def value(optname, *_values, info):
opt = info.config.get_opt(optname)
default = opt.typ.to_str(opt.default)
cur_cat = listcategory.ListCategory(
"Current/Default",
[(current, "Current value"), (default, "Default value")])
model.add_category(cur_cat)
cur_def = []
if current not in values:
cur_def.append((current, "Current value"))
if default not in values:
cur_def.append((default, "Default value"))
if cur_def:
cur_cat = listcategory.ListCategory("Current/Default", cur_def)
model.add_category(cur_cat)
vals = opt.typ.complete()
if vals is not None:
vals = opt.typ.complete() or []
vals = [x for x in vals if x[0] not in values]
if vals:
model.add_category(listcategory.ListCategory("Completions", vals))
return model

View File

@@ -110,18 +110,18 @@ def _buffer(skip_win_id=None):
model = completionmodel.CompletionModel(column_widths=(6, 40, 54))
for win_id in objreg.window_registry:
if skip_win_id and win_id == skip_win_id:
if skip_win_id is not None and win_id == skip_win_id:
continue
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
if tabbed_browser.shutting_down:
continue
tabs = []
for idx in range(tabbed_browser.count()):
tab = tabbed_browser.widget(idx)
for idx in range(tabbed_browser.widget.count()):
tab = tabbed_browser.widget.widget(idx)
tabs.append(("{}/{}".format(win_id, idx + 1),
tab.url().toDisplayString(),
tabbed_browser.page_title(idx)))
tabbed_browser.widget.page_title(idx)))
cat = listcategory.ListCategory("{}".format(win_id), tabs,
delete_func=delete_buffer)
model.add_category(cat)

View File

@@ -276,7 +276,8 @@ class Config(QObject):
"""Set the given option to the given value."""
if not isinstance(objects.backend, objects.NoBackend):
if objects.backend not in opt.backends:
raise configexc.BackendError(opt.name, objects.backend)
raise configexc.BackendError(opt.name, objects.backend,
opt.raw_backends)
opt.typ.to_py(value) # for validation

View File

@@ -102,7 +102,7 @@ def _parse_yaml_type(name, node):
try:
typ = getattr(configtypes, type_name)
except AttributeError as e:
except AttributeError:
raise AttributeError("Did not find type {} for {}".format(
type_name, name))
@@ -149,6 +149,9 @@ def _parse_yaml_backends_dict(name, node):
False: False,
'Qt 5.8': qtutils.version_check('5.8'),
'Qt 5.9': qtutils.version_check('5.9'),
'Qt 5.9.2': qtutils.version_check('5.9.2'),
'Qt 5.10': qtutils.version_check('5.10'),
'Qt 5.11': qtutils.version_check('5.11'),
}
for key in sorted(node.keys()):
if conditionals[node[key]]:

View File

@@ -150,14 +150,24 @@ force_software_rendering:
renamed: qt.force_software_rendering
qt.force_software_rendering:
type: Bool
default: false
type:
name: String
valid_values:
- software-opengl: Tell LibGL to use a software implementation of GL
(`LIBGL_ALWAYS_SOFTWARE` / `QT_XCB_FORCE_SOFTWARE_OPENGL`)
- qt-quick: Tell Qt Quick to use a software renderer instead of OpenGL.
(`QT_QUICK_BACKEND=software`)
- chromium: Tell Chromium to disable GPU support and use Skia software
rendering instead. (`--disable-gpu`)
- none: Don't force software rendering.
default: none
backend: QtWebEngine
restart: true
desc: >-
Force software rendering for QtWebEngine.
This is needed for QtWebEngine to work with Nouveau drivers.
This is needed for QtWebEngine to work with Nouveau drivers and can be
useful in other scenarios related to graphic issues.
qt.force_platform:
type:
@@ -204,6 +214,17 @@ auto_save.session:
## content
content.autoplay:
default: true
type: Bool
backend:
QtWebEngine: Qt 5.10
QtWebKit: false
desc: >-
Automatically start playing `<video>` elements.
Note this option needs a restart with QtWebEngine on Qt < 5.11.
content.cache.size:
default: null
type:
@@ -216,6 +237,16 @@ content.cache.size:
With QtWebEngine, the maximum supported value is 2147483647 (~2 GB).
content.canvas_reading:
default: true
type: Bool
backend: QtWebEngine
restart: true
desc: >-
Allow websites to read canvas elements.
Note this is needed for some websites to work properly.
# Defaults from QWebSettings::QWebSettings() in
# qtwebkit/Source/WebKit/qt/Api/qwebsettings.cpp
@@ -250,14 +281,17 @@ content.cache.appcache:
content.cookies.accept:
default: no-3rdparty
backend: QtWebKit
backend:
QtWebKit: true
QtWebEngine: Qt 5.11
type:
name: String
valid_values:
- all: "Accept all cookies."
- no-3rdparty: "Accept cookies from the same origin only."
- no-unknown-3rdparty: "Accept cookies from the same origin only, unless
a cookie is already set for the domain."
a cookie is already set for the domain. On QtWebEngine, this is the
same as no-3rdparty."
- never: "Don't accept cookies at all."
desc: Which cookies to accept.
@@ -284,16 +318,18 @@ content.windowed_fullscreen:
desc: >-
Limit fullscreen to the browser window (does not expand to fill the screen).
content.developer_extras:
type: Bool
default: false
backend: QtWebKit
content.desktop_capture:
type: BoolAsk
default: ask
supports_pattern: true
desc: >-
Enable extra tools for Web developers.
Allow websites to share screen content.
This needs to be enabled for `:inspector` to work and also adds an
_Inspect_ entry to the context menu. For QtWebEngine, see
`--enable-webengine-inspector` in `qutebrowser --help` instead.
On Qt < 5.10, a dialog box is always displayed, even if this is set to
"true".
content.developer_extras:
deleted: true
content.dns_prefetch:
default: true
@@ -315,14 +351,19 @@ content.frame_flattening:
content.geolocation:
default: ask
type: BoolAsk
supports_pattern: true
desc: Allow websites to request geolocations.
content.headers.accept_language:
type:
name: String
none_ok: true
supports_pattern: true
default: en-US,en
desc: Value to send in the `Accept-Language` header.
desc: >-
Value to send in the `Accept-Language` header.
Note that the value read from JavaScript is always the global value.
content.headers.custom:
default: {}
@@ -335,6 +376,7 @@ content.headers.custom:
name: String
encoding: ascii
none_ok: true
supports_pattern: true
desc: Custom headers for qutebrowser HTTP requests.
content.headers.do_not_track:
@@ -342,6 +384,7 @@ content.headers.do_not_track:
name: Bool
none_ok: true
default: true
supports_pattern: true
desc: >-
Value to send in the `DNT` header.
@@ -416,7 +459,11 @@ content.headers.user_agent:
Gecko"
- IE 11.0 for Desktop Win7 64-bit
desc: User agent to send. Unset to send the default.
supports_pattern: true
desc: >-
User agent to send. Unset to send the default.
Note that the value read from JavaScript is always the global value.
content.host_blocking.enabled:
default: true
@@ -425,11 +472,7 @@ content.host_blocking.enabled:
content.host_blocking.lists:
default:
- "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"
- "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
type:
name: List
valtype: Url
@@ -563,6 +606,7 @@ content.local_storage:
content.media_capture:
default: ask
type: BoolAsk
supports_pattern: true
backend: QtWebEngine
desc: Allow websites to record audio/video.
@@ -579,6 +623,7 @@ content.netrc_file:
content.notifications:
default: ask
type: BoolAsk
supports_pattern: true
backend: QtWebKit
desc: Allow websites to show notifications.
@@ -592,6 +637,16 @@ content.pdfjs:
Note that the files can still be downloaded by clicking the download button
in the pdf.js viewer.
content.persistent_storage:
default: ask
type: BoolAsk
supports_pattern: true
backend:
QtWebKit: false
QtWebEngine: Qt 5.11
desc: Allow websites to request persistent storage quota via
`navigator.webkitPersistentStorage.requestQuota`.
content.plugins:
default: false
type: Bool
@@ -629,9 +684,20 @@ content.proxy_dns_requests:
backend: QtWebKit
desc: Send DNS requests over the configured proxy.
content.register_protocol_handler:
default: ask
type: BoolAsk
supports_pattern: true
backend:
QtWebKit: false
QtWebEngine: Qt 5.11
desc: Allow websites to register protocol handlers via
`navigator.registerProtocolHandler`.
content.ssl_strict:
default: ask
type: BoolAsk
supports_pattern: true
desc: Validate SSL handshakes.
content.user_stylesheets:
@@ -648,6 +714,19 @@ content.webgl:
supports_pattern: true
desc: Enable WebGL.
content.webrtc_public_interfaces_only:
default: false
type: Bool
backend:
QtWebKit: false
QtWebEngine: Qt 5.9.2
desc: >-
Only expose public interfaces via WebRTC.
On Qt 5.9, this option requires a restart.
On Qt 5.10, this option doesn't work at all because of a Qt bug.
On Qt >= 5.11, no restart is required.
content.xss_auditing:
type: Bool
default: false
@@ -965,6 +1044,11 @@ hints.uppercase:
## input
input.escape_quits_reporter:
type: Bool
default: True
desc: Allow Escape to quit the crash reporter.
input.forward_unbound_keys:
default: auto
type:
@@ -1252,9 +1336,14 @@ tabs.favicons.scale:
`tabs.padding`.
tabs.favicons.show:
default: true
type: Bool
desc: Show favicons in the tab bar.
default: always
type:
name: String
valid_values:
- always: Always show favicons.
- never: Always hide favicons.
- pinned: Show favicons only on pinned tabs.
desc: When to show favicons in the tab bar.
tabs.last_close:
default: ignore
@@ -1325,7 +1414,10 @@ tabs.show:
tabs.show_switching_delay:
default: 800
type: Int
type:
name: Int
minval: 0
maxval: maxint
desc: "Duration (in milliseconds) to show the tab bar before hiding it when
tabs.show is set to 'switching'."
@@ -1340,7 +1432,7 @@ tabs.title.alignment:
desc: Alignment of the text inside of tabs.
tabs.title.format:
default: '{index}: {title}'
default: '{audio}{index}: {title}'
type:
name: FormatString
fields:
@@ -1355,6 +1447,7 @@ tabs.title.format:
- private
- current_url
- protocol
- audio
none_ok: true
desc: |
Format to use for the tab title.
@@ -1372,6 +1465,7 @@ tabs.title.format:
* `{private}`: Indicates when private mode is enabled.
* `{current_url}`: URL of the current web page.
* `{protocol}`: Protocol (http/https/...) of the current web page.
* `{audio}`: Indicator for audio/mute status.
tabs.title.format_pinned:
default: '{index}'
@@ -1389,6 +1483,7 @@ tabs.title.format_pinned:
- private
- current_url
- protocol
- audio
none_ok: true
desc: Format to use for the tab title for pinned tabs. The same placeholders
like for `tabs.title.format` are defined.
@@ -1406,6 +1501,19 @@ tabs.width:
desc: "Width (in pixels or as percentage of the window) of the tab bar if
it's vertical."
tabs.min_width:
default: -1
type:
name: Int
minval: -1
maxval: maxint
desc: >-
Minimum width (in pixels) of tabs (-1 for the default minimum size behavior).
This setting only applies when tabs are horizontal.
This setting does not apply to pinned tabs, unless `tabs.pinned.shrink` is False.
tabs.width.indicator:
renamed: tabs.indicator.width
@@ -1469,6 +1577,11 @@ url.incdec_segments:
desc: URL segments where `:navigate increment/decrement` will search for
a number.
url.open_base_url:
type: Bool
default: false
desc: Open base URL of the searchengine if a searchengine shortcut is invoked without parameters.
url.searchengines:
default:
DEFAULT: https://duckduckgo.com/?q={}
@@ -1513,10 +1626,15 @@ url.yank_ignored_parameters:
## window
window.hide_wayland_decoration:
renamed: window.hide_decoration
window.hide_decoration:
type: Bool
default: false
restart: true
desc: Hide the window decoration when using wayland.
desc: |
Hide the window decoration.
This setting requires a restart on Wayland.
window.title_format:
type:
@@ -1533,6 +1651,7 @@ window.title_format:
- private
- current_url
- protocol
- audio
default: '{perc}{title}{title_sep}qutebrowser'
desc: |
Format to use for the window title. The same placeholders like for
@@ -2244,6 +2363,7 @@ bindings.default:
;R: hint --rapid links window
;d: hint links download
;t: hint inputs
gi: hint inputs --first
h: scroll left
j: scroll down
k: scroll up
@@ -2302,6 +2422,7 @@ bindings.default:
gf: view-source
gt: set-cmd-text -s :buffer
<Ctrl-Tab>: tab-focus last
<Ctrl-Shift-Tab>: nop
<Ctrl-^>: tab-focus last
<Ctrl-V>: enter-mode passthrough
<Ctrl-Q>: quit
@@ -2334,6 +2455,7 @@ bindings.default:
<Ctrl-Return>: follow-selected -t
.: repeat-command
<Ctrl-p>: tab-pin
<Alt-m>: tab-mute
q: record-macro
"@": run-macro
tsh: config-cycle -p -t -u *://{url:host}/* content.javascript.enabled ;; reload

View File

@@ -44,9 +44,15 @@ class BackendError(Error):
"""Raised when this setting is unavailable with the current backend."""
def __init__(self, name, backend):
super().__init__("The {} setting is not available with the {} "
"backend!".format(name, backend.name))
def __init__(self, name, backend, raw_backends):
if raw_backends is None or not raw_backends[backend.name]:
msg = ("The {} setting is not available with the {} backend!"
.format(name, backend.name))
else:
msg = ("The {} setting needs {} with the {} backend!"
.format(name, raw_backends[backend.name], backend.name))
super().__init__(msg)
class NoPatternError(Error):

View File

@@ -233,6 +233,14 @@ class YamlConfig(QObject):
if errors:
raise configexc.ConfigFileErrors('autoconfig.yml', errors)
def _migrate_bool(self, settings, name, true_value, false_value):
"""Migrate a boolean in the settings."""
if name in settings:
for scope, val in settings[name].items():
if isinstance(val, bool):
settings[name][scope] = true_value if val else false_value
self._mark_changed()
def _handle_migrations(self, settings):
"""Migrate older configs to the newest format."""
# Simple renamed/deleted options
@@ -268,6 +276,10 @@ class YamlConfig(QObject):
del settings['bindings.default']
self._mark_changed()
self._migrate_bool(settings, 'tabs.favicons.show', 'always', 'never')
self._migrate_bool(settings, 'qt.force_software_rendering',
'software-opengl', 'none')
return settings
def _validate(self, settings):
@@ -564,7 +576,7 @@ def read_autoconfig():
"""Read the autoconfig.yml file."""
try:
config.instance.read_yaml()
except configexc.ConfigFileErrors as e:
except configexc.ConfigFileErrors:
raise # caught in outer block
except configexc.Error as e:
desc = configexc.ConfigErrorDesc("Error", e)

View File

@@ -26,7 +26,8 @@ from PyQt5.QtWidgets import QMessageBox
from qutebrowser.config import (config, configdata, configfiles, configtypes,
configexc, configcommands)
from qutebrowser.utils import objreg, usertypes, log, standarddir, message
from qutebrowser.utils import (objreg, usertypes, log, standarddir, message,
qtutils)
from qutebrowser.misc import msgbox, objects
@@ -82,14 +83,17 @@ def early_init(args):
def _init_envvars():
"""Initialize environment variables which need to be set early."""
if (objects.backend == usertypes.Backend.QtWebEngine and
config.val.qt.force_software_rendering):
os.environ['QT_XCB_FORCE_SOFTWARE_OPENGL'] = '1'
if objects.backend == usertypes.Backend.QtWebEngine:
software_rendering = config.val.qt.force_software_rendering
if software_rendering == 'software-opengl':
os.environ['QT_XCB_FORCE_SOFTWARE_OPENGL'] = '1'
elif software_rendering == 'qt-quick':
os.environ['QT_QUICK_BACKEND'] = 'software'
if config.val.qt.force_platform is not None:
os.environ['QT_QPA_PLATFORM'] = config.val.qt.force_platform
if config.val.window.hide_wayland_decoration:
if config.val.window.hide_decoration:
os.environ['QT_WAYLAND_DISABLE_WINDOWDECORATION'] = '1'
if config.val.qt.highdpi:
@@ -161,4 +165,26 @@ def qt_args(namespace):
argv += ['--' + name, value]
argv += ['--' + arg for arg in config.val.qt.args]
if objects.backend == usertypes.Backend.QtWebEngine:
if not qtutils.version_check('5.11', compiled=False):
# WORKAROUND equivalent to
# https://codereview.qt-project.org/#/c/217932/
# Needed for Qt < 5.9.5 and < 5.10.1
argv.append('--disable-shared-workers')
if config.val.qt.force_software_rendering == 'chromium':
argv.append('--disable-gpu')
if not config.val.content.canvas_reading:
argv.append('--disable-reading-from-canvas')
if not qtutils.version_check('5.11'):
# On Qt 5.11, we can control this via QWebEngineSettings
if not config.val.content.autoplay:
argv.append('--autoplay-policy=user-gesture-required')
if config.val.content.webrtc_public_interfaces_only:
argv.append('--force-webrtc-ip-handling-policy='
'default_public_interface_only')
return argv

View File

@@ -451,7 +451,7 @@ class List(BaseType):
def from_obj(self, value):
if value is None:
return []
return value
return [self.valtype.from_obj(v) for v in value]
def to_py(self, value):
self._basic_py_validation(value, list)
@@ -506,6 +506,16 @@ class ListOrValue(BaseType):
self.listtype = List(valtype, none_ok=none_ok, *args, **kwargs)
self.valtype = valtype
def _val_and_type(self, value):
"""Get the value and type to use for to_str/to_doc/from_str."""
if isinstance(value, list):
if len(value) == 1:
return value[0], self.valtype
else:
return value, self.listtype
else:
return value, self.valtype
def get_name(self):
return self.listtype.get_name() + ', or ' + self.valtype.get_name()
@@ -533,25 +543,15 @@ class ListOrValue(BaseType):
if value is None:
return ''
if isinstance(value, list):
if len(value) == 1:
return self.valtype.to_str(value[0])
else:
return self.listtype.to_str(value)
else:
return self.valtype.to_str(value)
val, typ = self._val_and_type(value)
return typ.to_str(val)
def to_doc(self, value, indent=0):
if value is None:
return 'empty'
if isinstance(value, list):
if len(value) == 1:
return self.valtype.to_doc(value[0], indent)
else:
return self.listtype.to_doc(value, indent)
else:
return self.valtype.to_doc(value, indent)
val, typ = self._val_and_type(value)
return typ.to_doc(val)
class FlagList(List):
@@ -1199,7 +1199,9 @@ class Dict(BaseType):
def from_obj(self, value):
if value is None:
return {}
return value
return {self.keytype.from_obj(key): self.valtype.from_obj(val)
for key, val in value.items()}
def _fill_fixed_keys(self, value):
"""Fill missing fixed keys with a None-value."""
@@ -1407,7 +1409,7 @@ class SearchEngineUrl(BaseType):
try:
value.format("")
except (KeyError, IndexError) as e:
except (KeyError, IndexError):
raise configexc.ValidationError(
value, "may not contain {...} (use {{ and }} for literal {/})")
except ValueError as e:
@@ -1623,9 +1625,7 @@ class TimestampTemplate(BaseType):
"""An strftime-like template for timestamps.
See
https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
for reference.
See https://sqlite.org/lang_datefunc.html for reference.
"""
def to_py(self, value):
@@ -1648,6 +1648,10 @@ class Key(BaseType):
"""A name of a key."""
def from_obj(self, value):
"""Make sure key sequences are always normalized."""
return str(keyutils.KeySequence.parse(value))
def to_py(self, value):
self._basic_py_validation(value, str)
if not value:

View File

@@ -22,12 +22,24 @@
from PyQt5.QtGui import QFont
from qutebrowser.config import config, configutils
from qutebrowser.utils import log, usertypes, urlmatch
from qutebrowser.utils import log, usertypes, urlmatch, qtutils
from qutebrowser.misc import objects
UNSET = object()
class AttributeInfo:
"""Info about a settings attribute."""
def __init__(self, *attributes, converter=None):
self.attributes = attributes
if converter is None:
self.converter = lambda val: val
else:
self.converter = converter
class AbstractSettings:
"""Abstract base class for settings set via QWeb(Engine)Settings."""
@@ -50,12 +62,13 @@ class AbstractSettings:
"""
old_value = self.test_attribute(name)
for attribute in self._ATTRIBUTES[name]:
info = self._ATTRIBUTES[name]
for attribute in info.attributes:
if value is configutils.UNSET:
self._settings.resetAttribute(attribute)
new_value = self.test_attribute(name)
else:
self._settings.setAttribute(attribute, value)
self._settings.setAttribute(attribute, info.converter(value))
new_value = value
return old_value != new_value
@@ -66,7 +79,8 @@ class AbstractSettings:
If the setting resolves to a list of attributes, only the first
attribute is tested.
"""
return self._settings.testAttribute(self._ATTRIBUTES[name][0])
info = self._ATTRIBUTES[name]
return self._settings.testAttribute(info.attributes[0])
def set_font_size(self, name, value):
"""Set the given QWebSettings/QWebEngineSettings font size.
@@ -141,6 +155,7 @@ class AbstractSettings:
Return:
A set of settings which actually changed.
"""
qtutils.ensure_valid(url)
changed_settings = set()
for values in config.instance:
if not values.opt.supports_pattern:

View File

@@ -74,7 +74,7 @@ function tryagain()
</td>
<td style="padding-left: 40px;">
<h1>Unable to load page</h1>
Error while opening {{ url }}<br>
Error while opening {{ url | default('page', true) }}<br>
<p id="error-message-text" style="color: #a31a1a;">{{ error }}</p><br><br>
<form name="bl">

View File

@@ -2,3 +2,4 @@
pac_utils.js
# Actually a jinja template so eslint chokes on the {{}} syntax.
greasemonkey_wrapper.js
global_wrapper.js

View File

@@ -56,3 +56,5 @@ rules:
no-bitwise: "off"
no-ternary: "off"
max-lines: "off"
multiline-ternary: ["error", "always-multiline"]
max-lines-per-function: "off"

View File

@@ -324,9 +324,8 @@ window._qutebrowser.caret = (function() {
const color = axs.color.parseColor(style.backgroundColor);
if (color &&
(style.opacity < 1 &&
(color.alpha *= style.opacity),
color.alpha !== 0 &&
(el.push(color), color.alpha === 1))) {
(color.alpha *= style.opacity), color.alpha !== 0 &&
(el.push(color), color.alpha === 1))) {
iter = !0;
break;
}
@@ -781,6 +780,18 @@ window._qutebrowser.caret = (function() {
*/
CaretBrowsing.blinkFlag = true;
/**
* Whether we're running on Windows.
* @type {boolean}
*/
CaretBrowsing.isWindows = null;
/**
* Whether we're running on on old Qt 5.7.1.
* @type {boolean}
*/
CaretBrowsing.isOldQt = null;
/**
* Check if a node is a control that normally allows the user to interact
* with it using arrow keys. We won't override the arrow keys when such a
@@ -857,12 +868,24 @@ window._qutebrowser.caret = (function() {
};
CaretBrowsing.injectCaretStyles = function() {
const style = ".CaretBrowsing_Caret {" +
" position: absolute;" +
" z-index: 2147483647;" +
" min-height: 10px;" +
" background-color: #000;" +
"}";
const prefix = CaretBrowsing.isOldQt ? "-webkit-" : "";
const style = `
.CaretBrowsing_Caret {
position: absolute;
z-index: 2147483647;
min-height: 1em;
min-width: 0.2em;
animation: blink 1s step-end infinite;
--inherited-color: inherit;
background-color: var(--inherited-color, #000);
color: var(--inherited-color, #000);
mix-blend-mode: difference;
${prefix}filter: invert(85%);
}
@keyframes blink {
50% { visibility: hidden; }
}
`;
const node = document.createElement("style");
node.innerHTML = style;
document.body.appendChild(node);
@@ -1159,6 +1182,8 @@ window._qutebrowser.caret = (function() {
CaretBrowsing.updateCaretOrSelection(true);
}, 0);
}
CaretBrowsing.stopAnimation();
};
CaretBrowsing.moveToBlock = function(paragraph, boundary) {
@@ -1177,6 +1202,8 @@ window._qutebrowser.caret = (function() {
window.setTimeout(() => {
CaretBrowsing.updateCaretOrSelection(true);
}, 0);
CaretBrowsing.stopAnimation();
};
CaretBrowsing.toggle = function(value) {
@@ -1243,6 +1270,17 @@ window._qutebrowser.caret = (function() {
CaretBrowsing.updateIsCaretVisible();
};
CaretBrowsing.startAnimation = function() {
CaretBrowsing.caretElement.style.animationIterationCount = "infinite";
};
CaretBrowsing.stopAnimation = function() {
CaretBrowsing.caretElement.style.animationIterationCount = 0;
window.setTimeout(() => {
CaretBrowsing.startAnimation();
}, 1000);
};
CaretBrowsing.init = function() {
CaretBrowsing.isWindowFocused = document.hasFocus();
@@ -1270,17 +1308,19 @@ window._qutebrowser.caret = (function() {
funcs.setInitialCursor = () => {
if (!CaretBrowsing.initiated) {
CaretBrowsing.setInitialCursor();
return;
return CaretBrowsing.selectionEnabled;
}
if (window.getSelection().toString().length === 0) {
positionCaret();
}
CaretBrowsing.toggle();
return CaretBrowsing.selectionEnabled;
};
funcs.setPlatform = (platform) => {
funcs.setPlatform = (platform, qtVersion) => {
CaretBrowsing.isWindows = platform.startsWith("win");
CaretBrowsing.isOldQt = qtVersion === "5.7.1";
};
funcs.disableCaret = () => {
@@ -1362,6 +1402,7 @@ window._qutebrowser.caret = (function() {
funcs.toggleSelection = () => {
CaretBrowsing.selectionEnabled = !CaretBrowsing.selectionEnabled;
return CaretBrowsing.selectionEnabled;
};
return funcs;

Some files were not shown because too many files have changed in this diff Show More