Merge remote-tracking branch 'upstream/master' into HEAD
This commit is contained in:
commit
be7d8bbbd5
|
|
@ -5,15 +5,15 @@ cache:
|
|||
build: off
|
||||
environment:
|
||||
PYTHONUNBUFFERED: 1
|
||||
PYTHON: C:\Python36-x64\python.exe
|
||||
PYTHON: C:\Python37-x64\python.exe
|
||||
matrix:
|
||||
- TESTENV: py36-pyqt511
|
||||
- TESTENV: py37-pyqt512
|
||||
- TESTENV: pylint
|
||||
|
||||
install:
|
||||
- '%PYTHON% -m pip install -U pip'
|
||||
- '%PYTHON% -m pip install -r misc\requirements\requirements-tox.txt'
|
||||
- 'set PATH=C:\Python36-x64;%PATH'
|
||||
- 'set PATH=C:\Python37-x64;%PATH'
|
||||
|
||||
test_script:
|
||||
- '%PYTHON% -m tox -e %TESTENV%'
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ exclude_lines =
|
|||
raise NotImplementedError
|
||||
raise utils\.Unreachable
|
||||
if __name__ == ["']__main__["']:
|
||||
if MYPY:
|
||||
|
||||
[xml]
|
||||
output=coverage.xml
|
||||
|
|
|
|||
10
.flake8
10
.flake8
|
|
@ -46,11 +46,11 @@ ignore =
|
|||
min-version = 3.4.0
|
||||
max-complexity = 12
|
||||
per-file-ignores =
|
||||
/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
|
||||
qutebrowser/api/hook.py : N801
|
||||
tests/* : D100,D101
|
||||
tests/unit/browser/test_history.py : D100,D101,N806
|
||||
tests/helpers/fixtures.py : D100,D101,N806
|
||||
tests/unit/browser/webkit/http/test_content_disposition.py : D100,D101,D400
|
||||
copyright-check = True
|
||||
copyright-regexp = # Copyright [\d-]+ .*
|
||||
copyright-min-file-size = 110
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
IMPORTANT: *Currently, bigger changes are going on in qutebrowser, as
|
||||
part of a
|
||||
https://lists.schokokeks.org/pipermail/qutebrowser-announce/2018-September/000051.html[student research project]
|
||||
about adding a plugin API to qutebrowser and moving a lot of code from the code
|
||||
into plugins.* Due to that, bandwidth for pull request review is currently
|
||||
very limited, and contributions might lead to merge conflicts due to
|
||||
ongoing refactorings.
|
||||
IMPORTANT: Bandwidth for pull request review is currently quite limited. If you
|
||||
want to contribute where it's most needed, please consider reviewing or testing
|
||||
open pull requests.
|
||||
|
||||
- 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.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ about: Report errors and problems
|
|||
|
||||
**Version info (see `:version`)**:
|
||||
|
||||
**Does the bug happen if you start with `--temp-basedir`?** (if applicable):
|
||||
**Does the bug happen if you start with `--temp-basedir`?**:
|
||||
|
||||
**Description**
|
||||
|
||||
|
|
|
|||
|
|
@ -41,3 +41,4 @@ TODO
|
|||
/scripts/testbrowser/cpp/webengine/.qmake.stash
|
||||
/scripts/dev/pylint_checkers/qute_pylint.egg-info
|
||||
/misc/file_version_info.txt
|
||||
/doc/extapi/_build
|
||||
|
|
|
|||
110
.travis.yml
110
.travis.yml
|
|
@ -1,74 +1,87 @@
|
|||
dist: xenial
|
||||
language: python
|
||||
group: edge
|
||||
python: 3.6
|
||||
python: 3.7
|
||||
os: linux
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
env: DOCKER=archlinux
|
||||
### Archlinux QtWebKit
|
||||
- env: DOCKER=archlinux
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=archlinux-webengine QUTE_BDD_WEBENGINE=true
|
||||
|
||||
### Archlinux QtWebEngine
|
||||
- env: DOCKER=archlinux-webengine QUTE_BDD_WEBENGINE=true
|
||||
services: docker
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt571
|
||||
- os: linux
|
||||
python: 3.5
|
||||
|
||||
### PyQt 5.7.1 (Python 3.5)
|
||||
- python: 3.5
|
||||
env: TESTENV=py35-pyqt571
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt59
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt510
|
||||
### PyQt 5.7.1 (Python 3.6)
|
||||
- python: 3.6
|
||||
env: TESTENV=py36-pyqt571
|
||||
|
||||
### PyQt 5.9
|
||||
- env: TESTENV=py37-pyqt59
|
||||
|
||||
### PyQt 5.10
|
||||
- env: TESTENV=py37-pyqt510
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- xfonts-base
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt511-cov
|
||||
- os: linux
|
||||
python: 3.7
|
||||
env: TESTENV=py37-pyqt511
|
||||
|
||||
### PyQt 5.11
|
||||
- env: TESTENV=py37-pyqt511
|
||||
|
||||
### PyQt 5.12 (Python 3.7, with coverage)
|
||||
- env: TESTENV=py37-pyqt512-cov
|
||||
# http://code.qt.io/cgit/qt/qtbase.git/commit/?id=c3a963da1f9e7b1d37e63eedded61da4fbdaaf9a
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libxkbcommon-x11-0
|
||||
|
||||
### macOS sierra
|
||||
- os: osx
|
||||
env: TESTENV=py37 OSX=sierra
|
||||
env: TESTENV=py37-pyqt512 OSX=sierra
|
||||
osx_image: xcode9.2
|
||||
language: generic
|
||||
### macOS yosemite
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/2013
|
||||
# - os: osx
|
||||
# env: TESTENV=py35 OSX=yosemite
|
||||
# osx_image: xcode6.4
|
||||
- os: linux
|
||||
env: TESTENV=pylint PYTHON=python3.6
|
||||
- os: linux
|
||||
env: TESTENV=flake8
|
||||
- os: linux
|
||||
env: TESTENV=docs
|
||||
|
||||
### pylint/flake8/mypy
|
||||
- env: TESTENV=pylint
|
||||
- env: TESTENV=flake8
|
||||
- env: TESTENV=mypy
|
||||
|
||||
### docs
|
||||
- env: TESTENV=docs
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- asciidoc
|
||||
- os: linux
|
||||
env: TESTENV=vulture
|
||||
- os: linux
|
||||
env: TESTENV=misc
|
||||
- os: linux
|
||||
env: TESTENV=pyroma
|
||||
- os: linux
|
||||
env: TESTENV=check-manifest
|
||||
- os: linux
|
||||
env: TESTENV=eslint
|
||||
|
||||
### vulture/misc/pyroma/check-manifest
|
||||
- env: TESTENV=vulture
|
||||
- env: TESTENV=misc
|
||||
- env: TESTENV=pyroma
|
||||
- env: TESTENV=check-manifest
|
||||
|
||||
### eslint
|
||||
- env: TESTENV=eslint
|
||||
language: node_js
|
||||
python: null
|
||||
node_js: "lts/*"
|
||||
- os: linux
|
||||
language: generic
|
||||
|
||||
### shellcheck
|
||||
- language: generic
|
||||
env: TESTENV=shellcheck
|
||||
services: docker
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/4055
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt510
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
|
@ -87,16 +100,3 @@ after_success:
|
|||
|
||||
after_failure:
|
||||
- bash scripts/dev/ci/travis_backtrace.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#qutebrowser-dev"
|
||||
on_success: always
|
||||
on_failure: always
|
||||
skip_join: true
|
||||
template:
|
||||
- "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}"
|
||||
- "%{compare_url} - %{build_url}"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ recursive-include scripts *.py *.sh *.js
|
|||
include qutebrowser/utils/testfile
|
||||
include qutebrowser/git-commit-id
|
||||
include LICENSE doc/* README.asciidoc
|
||||
include misc/qutebrowser.desktop
|
||||
include misc/qutebrowser.appdata.xml
|
||||
include misc/org.qutebrowser.qutebrowser.desktop
|
||||
include misc/org.qutebrowser.qutebrowser.appdata.xml
|
||||
include misc/Makefile
|
||||
include requirements.txt
|
||||
include tox.ini
|
||||
|
|
@ -32,6 +32,7 @@ include doc/changelog.asciidoc
|
|||
prune tests
|
||||
prune qutebrowser/3rdparty
|
||||
exclude pytest.ini
|
||||
exclude mypy.ini
|
||||
exclude qutebrowser/javascript/.eslintrc.yaml
|
||||
exclude qutebrowser/javascript/.eslintignore
|
||||
exclude doc/help
|
||||
|
|
@ -39,5 +40,6 @@ exclude .*
|
|||
exclude misc/qutebrowser.spec
|
||||
exclude misc/qutebrowser.nsi
|
||||
exclude misc/qutebrowser.rcc
|
||||
prune doc/extapi
|
||||
|
||||
global-exclude __pycache__ *.pyc *.pyo
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ Requirements
|
|||
The following software and libraries are required to run qutebrowser:
|
||||
|
||||
* https://www.python.org/[Python] 3.5 or newer (3.6 recommended)
|
||||
* https://www.qt.io/[Qt] 5.7.1 or newer (5.11 recommended, support for < 5.9
|
||||
* https://www.qt.io/[Qt] 5.7.1 or newer (5.12 recommended, support for < 5.9
|
||||
will be dropped soon) with the following modules:
|
||||
- QtCore / qtbase
|
||||
- QtQuick (part of qtbase in some distributions)
|
||||
|
|
@ -108,7 +108,7 @@ The following software and libraries are required to run qutebrowser:
|
|||
only the link:https://github.com/annulen/webkit/wiki[updated fork] (5.212)
|
||||
is supported
|
||||
* https://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.7.0 or newer
|
||||
(5.11 recommended, support for < 5.9 will be dropped soon) for Python 3
|
||||
(5.12 recommended, support for < 5.9 will be dropped soon) for Python 3
|
||||
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
||||
* https://fdik.org/pyPEG/[pyPEG2]
|
||||
* http://jinja.pocoo.org/[jinja2]
|
||||
|
|
|
|||
|
|
@ -15,32 +15,91 @@ 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.6.0 (unreleased)
|
||||
v1.7.0 (unreleased)
|
||||
-------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New `tabs.new_position.stacking` setting which controls whether new tabs
|
||||
opened from a page should stack on each other or not.
|
||||
- New `completion.open_categories` setting which allows to configure which
|
||||
categories are shown in the `:open` completion, and how they are ordered.
|
||||
- New settings:
|
||||
* `colors.tabs.pinned.*` to control colors of pinned tabs.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- The desktop file `qutebrowser.desktop` is now renamed to
|
||||
`org.qutebrowser.qutebrowser.desktop`.
|
||||
- Pinned tabs now always show a favicon (even if the site doesn't provide one)
|
||||
when shrinking.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
v1.6.1
|
||||
------
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Windows/macOS releases now ship with Qt 5.12.2, which includes
|
||||
security fixes up to Chromium 72.0.3626.121 (including CVE-2019-5786
|
||||
which is known to be exploited in the wild).
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Crash when using `:config-{dict,list}-{add,remove}` with an invalid setting.
|
||||
- Functionality like hinting on pages with an element with ID `_qutebrowser` (such as qutebrowser.org) on Qt 5.12.
|
||||
- The .desktop file in v1.6.0 was missing the "Actions" key, which is now fixed.
|
||||
- The SVG icon now has a size of 256x256px set to comply with freedesktop standards.
|
||||
- Setting `colors.statusbar.*.bg` to a gradient now has the expected effect of
|
||||
the gradient spanning the entire statusbar.
|
||||
|
||||
v1.6.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New settings:
|
||||
* `tabs.new_position.stacking` which controls whether new tabs opened from a
|
||||
page should stack on each other or not.
|
||||
* `completion.open_categories` which allows to configure which categories are
|
||||
shown in the `:open` completion, and how they are ordered.
|
||||
* `tabs.pinned.frozen` to allow/deny navigating in pinned tabs.
|
||||
* `hints.selectors` which allows to configure what CSS selectors are used for
|
||||
hints, and also allows adding custom hint groups.
|
||||
* `input.insert_mode.leave_on_load` to turn off leaving insert mode when a
|
||||
new page is loaded.
|
||||
- New config manipulation commands:
|
||||
* `:config-dict-add` and `:config-list-add` to a new element to a dict/list
|
||||
setting.
|
||||
* `:config-dict-remove` and `:config-list-remove` to remove an element from a
|
||||
dict/list setting.
|
||||
- New `hints.selectors` setting which allows to configure what CSS selectors
|
||||
are used for hints, and also allows adding custom hint groups.
|
||||
- New `:yank markdown` feature which yanks the current URL and title in
|
||||
markdown format.
|
||||
- New command `:navigate strip` which removes queries and parameters from the
|
||||
current URL (bound to `gs`/`gS` by default).
|
||||
- Support for new QtWebEngine features in Qt 5.12:
|
||||
* Basic support for client certificates. Selecting the certificate to use
|
||||
when there are multiple matching certificates isn't implemented yet.
|
||||
* Support for DNS prefetching (plus new `content.dns_prefetch` setting).
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- `:q` now closes current window instead of quitting qutebrowser completely
|
||||
- Various changes to the Windows and macOS builds:
|
||||
* Bundling Qt 5.12.1, based on Chromium 69.0.3497.128 with security fixes up
|
||||
to 71.0.3578.94.
|
||||
* Windows: A 32-bit build is available again.
|
||||
* Windows: The builds now bundle the Universal CRT DLLs, causing them to work
|
||||
on earlier versions of Windows 10.
|
||||
* macOS: Support for OS X 10.11 El Capitan was dropped, requiring macOS 10.12
|
||||
Sierra or newer.
|
||||
* macOS: The IPC socket path used to communicate with existing instances
|
||||
changed due to changes in Qt 5.12. Please make sure to quit qutebrowser
|
||||
before upgrading.
|
||||
- `:q` now closes the current window instead of quitting qutebrowser completely
|
||||
(`:close`), while `:qa` quits (`:quit`). The behavior of `:wq` remains
|
||||
unchanged (`:quit --save`), as closing a window while saving the session
|
||||
doesn't make sense.
|
||||
|
|
@ -53,6 +112,20 @@ Changed
|
|||
adblocker can be disabled on a given page.
|
||||
- Elements with a `tabindex` attribute now also get hints by default.
|
||||
- Various small performance improvements for hints and the completion.
|
||||
- The Wayland check for QtWebEngine is now disabled on Qt >= 5.11.2, as those
|
||||
versions should work without any issues.
|
||||
- The JavaScript `console` object is now available in PAC files.
|
||||
- PAC proxies currently don't work properly on QtWebEngine (and never did), so
|
||||
an error is now shown when trying to configure a PAC proxy.
|
||||
- The metainfo file `qutebrowser.appdata.xml` is now renamed to
|
||||
`org.qutebrowser.qutebrowser.appdata.xml`.
|
||||
- The `qute-pass` userscript now understands domains in gpg filenames
|
||||
in addition to directory names.
|
||||
- The autocompletion for `content.headers.user_agent` got updated to only
|
||||
include the default and Chrome, as setting the UA to Firefox has various
|
||||
bad side-effects.
|
||||
- Combining Qt 5.12 with an older PyQt can lead to issues, so a warning is
|
||||
now shown when starting qutebrowser with that combination.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
|
@ -67,7 +140,28 @@ Fixed
|
|||
`content.cookies.accept = no-3rdparty` from working properly on some pages
|
||||
like GMail. However, the default for `content.cookies.accept` is still `all`
|
||||
to be in line with what other browsers do.
|
||||
- `:navigate` not incrementing in anchors or queries or anchors.
|
||||
- `:navigate` not incrementing in anchors or queries.
|
||||
- Crash when trying to use a proxy requiring authentication with QtWebKit.
|
||||
- Slashes in search terms are now percent-escaped.
|
||||
- When `scrolling.bar = True` was set in versions before v1.5.0, this now
|
||||
correctly gets migrated to `always` instead of `when-searching`.
|
||||
- Completion highlighting now works again on Qt 5.11.3 and 5.12.1.
|
||||
- The non-standard header `X-Do-Not-Track` is no longer sent.
|
||||
- PAC proxies were never correctly supported with QtWebEngine, but are now
|
||||
explicitly disallowed.
|
||||
- macOS: Context menus for download items now show in the correct macOS style.
|
||||
- Issues with fullscreen handling when exiting a video player.
|
||||
- Various fixes for Qt 5.12 issues:
|
||||
* A javascript error on page load was fixed.
|
||||
* `window.print()` works with Qt 5.12 now.
|
||||
* Fixed handling of duplicate download filenames.
|
||||
* Fixed broken `qute://history` page.
|
||||
* Fixed PDF.js not working properly.
|
||||
* The download button in PDF.js now works (it's not possible to make
|
||||
it work with earlier Qt versions).
|
||||
* Since Greasemonkey scripts modifying the DOM fail when being run at
|
||||
document-start, some known-broken scripts (Iridium, userstyles.org) are now
|
||||
forced to run at document-end.
|
||||
|
||||
v1.5.2
|
||||
------
|
||||
|
|
@ -367,11 +461,11 @@ 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.
|
||||
- CVE-2018-1000559: 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`. See the related GitHub issue for details:
|
||||
https://github.com/qutebrowser/qutebrowser/issues/4011.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
|
|
|||
|
|
@ -5,13 +5,9 @@ The Compiler <mail@qutebrowser.org>
|
|||
:data-uri:
|
||||
:toc:
|
||||
|
||||
IMPORTANT: *Currently, bigger changes are going on in qutebrowser, as
|
||||
part of a
|
||||
https://lists.schokokeks.org/pipermail/qutebrowser-announce/2018-September/000051.html[student research project]
|
||||
about adding a plugin API to qutebrowser and moving a lot of code from the code
|
||||
into plugins.* Due to that, bandwidth for pull request review is currently
|
||||
very limited, and contributions might lead to merge conflicts due to
|
||||
ongoing refactorings.
|
||||
IMPORTANT: Bandwidth for pull request review is currently quite limited. If you
|
||||
want to contribute where it's most needed, please consider reviewing or testing
|
||||
open pull requests.
|
||||
|
||||
I `<3` footnote:[Of course, that says `<3` in HTML.] contributors!
|
||||
|
||||
|
|
@ -222,7 +218,7 @@ Some resources which might be handy:
|
|||
* http://doc.qt.io/qt-5/classes.html[The Qt5 reference]
|
||||
* https://docs.python.org/3/library/index.html[The Python reference]
|
||||
* http://httpbin.org/[httpbin, a test service for HTTP requests/responses]
|
||||
* http://requestb.in/[RequestBin, a service to inspect HTTP requests]
|
||||
* https://requestbin.com/[RequestBin, a service to inspect HTTP requests]
|
||||
|
||||
Documentation of used Python libraries:
|
||||
|
||||
|
|
@ -407,7 +403,7 @@ Creating a new command is straightforward:
|
|||
|
||||
[source,python]
|
||||
----
|
||||
import qutebrowser.commands.cmdutils
|
||||
from qutebrowser.api import cmdutils
|
||||
|
||||
...
|
||||
|
||||
|
|
@ -429,7 +425,7 @@ selects which object registry (global, per-tab, etc.) to use. See the
|
|||
|
||||
There are also other arguments to customize the way the command is
|
||||
registered; see the class documentation for `register` in
|
||||
`qutebrowser.commands.cmdutils` for details.
|
||||
`qutebrowser.api.cmdutils` for details.
|
||||
|
||||
The types of the function arguments are inferred based on their default values,
|
||||
e.g., an argument `foo=True` will be converted to a flag `-f`/`--foo` in
|
||||
|
|
@ -480,8 +476,10 @@ For `typing.Union` types, the given `choices` are only checked if other types
|
|||
The following arguments are supported for `@cmdutils.argument`:
|
||||
|
||||
- `flag`: Customize the short flag (`-x`) the argument will get.
|
||||
- `win_id=True`: Mark the argument as special window ID argument.
|
||||
- `count=True`: Mark the argument as special count argument.
|
||||
- `value`: Tell qutebrowser to fill the argument with special values:
|
||||
- `value=cmdutils.Value.count`: The `count` given by the user to the command.
|
||||
- `value=cmdutils.Value.win_id`: The window ID of the current window.
|
||||
- `value=cmdutils.Value.cur_tab`: The tab object which is currently focused.
|
||||
- `completion`: A completion function (see `qutebrowser.completions.models.*`)
|
||||
to use when completing arguments for the given command.
|
||||
- `choices`: The allowed string choices for the argument.
|
||||
|
|
@ -606,7 +604,7 @@ Style conventions
|
|||
-----------------
|
||||
|
||||
qutebrowser's coding conventions are based on
|
||||
http://legacy.python.org/dev/peps/pep-0008/[PEP8] and the https://google-styleguide.googlecode.com/svn/trunk/pyguide.html[Google Python style guidelines] with some additions:
|
||||
http://legacy.python.org/dev/peps/pep-0008/[PEP8] and the https://google.github.io/styleguide/pyguide.html[Google Python style guidelines] with some additions:
|
||||
|
||||
* The _Raise:_ section is not added to the docstring.
|
||||
* Methods overriding Qt methods (obviously!) don't follow the naming schemes.
|
||||
|
|
@ -708,6 +706,7 @@ qutebrowser release
|
|||
|
||||
* Update changelog (remove *(unreleased)*).
|
||||
* Adjust `__version_info__` in `qutebrowser/__init__.py`.
|
||||
* Consider updating the completions for `content.headers.user_agent` in `configdata.yml`.
|
||||
* Commit.
|
||||
|
||||
* Create annotated git tag (`git tag -s "v1.$x.$y" -m "Release v1.$x.$y"`).
|
||||
|
|
@ -721,7 +720,7 @@ as closed.
|
|||
* Windows: Run `git checkout v1.X.Y; py -3 scripts\dev\build_release.py --asciidoc C:\Python27\python %userprofile%\bin\asciidoc-8.6.10\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).
|
||||
- Run `bash download_release.sh 1.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.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
API modules
|
||||
===========
|
||||
|
||||
cmdutils module
|
||||
---------------
|
||||
|
||||
.. automodule:: qutebrowser.api.cmdutils
|
||||
:members:
|
||||
:imported-members:
|
||||
|
||||
apitypes module
|
||||
---------------
|
||||
|
||||
.. automodule:: qutebrowser.api.apitypes
|
||||
:members:
|
||||
:imported-members:
|
||||
|
||||
config module
|
||||
-------------
|
||||
|
||||
.. automodule:: qutebrowser.api.config
|
||||
:members:
|
||||
|
||||
downloads module
|
||||
----------------
|
||||
|
||||
.. automodule:: qutebrowser.api.downloads
|
||||
:members:
|
||||
|
||||
hook module
|
||||
-----------
|
||||
|
||||
.. automodule:: qutebrowser.api.hook
|
||||
:members:
|
||||
|
||||
interceptor module
|
||||
------------------
|
||||
|
||||
.. automodule:: qutebrowser.api.interceptor
|
||||
:members:
|
||||
:imported-members:
|
||||
|
||||
message module
|
||||
--------------
|
||||
|
||||
.. automodule:: qutebrowser.api.message
|
||||
:members:
|
||||
:imported-members:
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file does only contain a selection of the most common options. For a
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'qutebrowser extensions'
|
||||
copyright = '2018-2019, Florian Bruhin'
|
||||
author = 'Florian Bruhin'
|
||||
|
||||
# The short X.Y version
|
||||
version = ''
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = ''
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.napoleon',
|
||||
]
|
||||
autodoc_member_order = 'bysource'
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = None
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# The default sidebars (for documents that don't match any pattern) are
|
||||
# defined by theme itself. Builtin themes are using these templates by
|
||||
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
|
||||
# 'searchbox.html']``.
|
||||
#
|
||||
# html_sidebars = {}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'qutebrowserextensionsdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'qutebrowserextensions.tex', 'qutebrowser extensions Documentation',
|
||||
'Florian Bruhin', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'qutebrowserextensions', 'qutebrowser extensions Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'qutebrowserextensions', 'qutebrowser extensions Documentation',
|
||||
author, 'qutebrowserextensions', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Epub output -------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = project
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#
|
||||
# epub_uid = ''
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
|
||||
|
||||
# -- Extension configuration -------------------------------------------------
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
.. qutebrowser extensions documentation master file, created by
|
||||
sphinx-quickstart on Tue Dec 11 18:59:44 2018.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to qutebrowser extensions's documentation!
|
||||
==================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
api
|
||||
tab
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
Tab API
|
||||
=======
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractTab()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractAction()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractPrinting()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractSearch()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractZoom()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractCaret()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractScroller()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractHistory()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractElements()
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.browsertab.AbstractAudio()
|
||||
:members:
|
||||
|
||||
Web element API
|
||||
===============
|
||||
|
||||
.. autoclass:: qutebrowser.browser.webelem.AbstractWebElement
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.webelem.Error
|
||||
:members:
|
||||
|
||||
.. autoclass:: qutebrowser.browser.webelem.OrphanedError
|
||||
:members:
|
||||
|
|
@ -211,9 +211,10 @@ Why does J move to the next (right) tab, and K to the previous (left) one?::
|
|||
|
||||
What's the difference between insert and passthrough mode?::
|
||||
They are quite similar, but insert mode has some bindings (like `Ctrl-e` to
|
||||
open an editor) while passthrough mode only has escape bound. It might also
|
||||
be useful to rebind escape to something else in passthrough mode only, to be
|
||||
able to send an escape keypress to the website.
|
||||
open an editor) while passthrough mode only has shift+escape bound. This is
|
||||
because shift+escape is unlikely to be a useful binding to be passed to a
|
||||
webpage. However, any other keys may be assigned to leaving passthrough mode
|
||||
instead of shift+escape should this be desired.
|
||||
|
||||
Why does it take longer to open a URL in qutebrowser than in chromium?::
|
||||
When opening a URL in an existing instance, the normal qutebrowser
|
||||
|
|
@ -260,6 +261,12 @@ Note that there are some missing features which you may run into:
|
|||
. Any greasemonkey API function to do with adding UI elements is not currently
|
||||
supported. That means context menu extentensions and background pages.
|
||||
|
||||
How do I change the `WM_CLASS` used by qutebrowser windows?::
|
||||
Qt only supports setting `WM_CLASS` globally, which you can do by starting
|
||||
with `--qt-arg name foo`. Note that all windows are part of the same
|
||||
qutebrowser instance (unless you use `--temp-basedir` or `--basedir`), so
|
||||
they all will share the same `WM_CLASS`.
|
||||
|
||||
== Troubleshooting
|
||||
|
||||
Unable to view flash content.::
|
||||
|
|
|
|||
|
|
@ -165,8 +165,7 @@ If no command is given, show the current binding for the given key. Using :bind
|
|||
* +'command'+: The command to execute, with optional args.
|
||||
|
||||
==== optional arguments
|
||||
* +*-m*+, +*--mode*+: A comma-separated list of modes to bind the key in (default: `normal`). See `:help bindings.commands` for the
|
||||
available modes.
|
||||
* +*-m*+, +*--mode*+: The mode to bind the key in (default: `normal`). See `:help bindings.commands` for the available modes.
|
||||
|
||||
* +*-d*+, +*--default*+: If given, restore a default binding.
|
||||
|
||||
|
|
@ -1426,7 +1425,7 @@ Unbind a keychain.
|
|||
|
||||
|
||||
==== optional arguments
|
||||
* +*-m*+, +*--mode*+: A mode to unbind the key in (default: `normal`). See `:help bindings.commands` for the available modes.
|
||||
* +*-m*+, +*--mode*+: The mode to unbind the key in (default: `normal`). See `:help bindings.commands` for the available modes.
|
||||
|
||||
|
||||
[[undo]]
|
||||
|
|
@ -1485,14 +1484,14 @@ Yank something to the clipboard or primary selection.
|
|||
|
||||
[[zoom]]
|
||||
=== zoom
|
||||
Syntax: +:zoom [*--quiet*] ['zoom']+
|
||||
Syntax: +:zoom [*--quiet*] ['level']+
|
||||
|
||||
Set the zoom level for the current tab.
|
||||
|
||||
The zoom can be given as argument or as [count]. If neither is given, the zoom is set to the default zoom. If both are given, use [count].
|
||||
|
||||
==== positional arguments
|
||||
* +'zoom'+: The zoom percentage to set.
|
||||
* +'level'+: The zoom percentage to set.
|
||||
|
||||
==== optional arguments
|
||||
* +*-q*+, +*--quiet*+: Don't show a zoom level message.
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ hand, you can simply use those - see
|
|||
<<autoconfig,"Configuring qutebrowser via the user interface">> for details.
|
||||
|
||||
For more advanced configuration, you can write a `config.py` file - see
|
||||
<<configpy,"Configuring qutebrowser via config.py">>. As soon as a `config.py`
|
||||
<<configpy,"Configuring qutebrowser via config.py">>. When 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.
|
||||
to <<configpy-autoconfig,load it from `config.py`>> if you want settings changed via
|
||||
`:set`/`:bind` to persist between restarts.
|
||||
|
||||
[[autoconfig]]
|
||||
Configuring qutebrowser via the user interface
|
||||
|
|
@ -89,7 +89,7 @@ You can run `:config-edit` inside qutebrowser to open the file in your editor,
|
|||
The file should be located in the "config" location listed on
|
||||
link:qute://version[], which is typically `~/.config/qutebrowser/config.py` on
|
||||
Linux, `~/.qutebrowser/config.py` on macOS, and
|
||||
`%APPDATA%/qutebrowser/config.py` on Windows.
|
||||
`%APPDATA%/qutebrowser/config/config.py` on Windows.
|
||||
|
||||
Two global objects are pre-defined when running `config.py`: `c` and `config`.
|
||||
|
||||
|
|
@ -229,18 +229,18 @@ Loading `autoconfig.yml`
|
|||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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:
|
||||
stored in the `autoconfig.yml` file. When a `config.py` file exists, `autoconfig.yml`
|
||||
is not loaded automatically. To load `autoconfig.yml` automatically, add the
|
||||
following snippet to `config.py`:
|
||||
|
||||
.config.py:
|
||||
[source,python]
|
||||
----
|
||||
config.load_autoconfig()
|
||||
----
|
||||
|
||||
If you do so at the top of your file, your `config.py` settings will take
|
||||
precedence as they overwrite the settings done in `autoconfig.yml`.
|
||||
You can configure which file overrides the other by the location of the above code snippet.
|
||||
Place the snippet at the top to allow `config.py` to override `autoconfig.yml`.
|
||||
Place the snippet at the bottom for the opposite effect.
|
||||
|
||||
Importing other modules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
@ -309,7 +309,7 @@ You can use:
|
|||
import yaml
|
||||
|
||||
with (config.configdir / 'config.yml').open() as f:
|
||||
yaml_data = yaml.load(f)
|
||||
yaml_data = yaml.safe_load(f)
|
||||
|
||||
for k, v in yaml_data.items():
|
||||
config.set(k, v)
|
||||
|
|
@ -339,7 +339,7 @@ You can use:
|
|||
import yaml
|
||||
|
||||
with (config.configdir / 'colors.yml').open() as f:
|
||||
yaml_data = yaml.load(f)
|
||||
yaml_data = yaml.safe_load(f)
|
||||
|
||||
def dict_attrs(obj, path=''):
|
||||
if isinstance(obj, dict):
|
||||
|
|
@ -396,6 +396,7 @@ 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]
|
||||
- https://github.com/evannagle/qutebrowser-dracula-theme[Dracula]
|
||||
- https://github.com/jjzmajic/qutewal[Pywal theme]
|
||||
|
||||
Avoiding flake8 errors
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -91,6 +91,14 @@
|
|||
|<<colors.tabs.indicator.system,colors.tabs.indicator.system>>|Color gradient interpolation system for the tab indicator.
|
||||
|<<colors.tabs.odd.bg,colors.tabs.odd.bg>>|Background color of unselected odd tabs.
|
||||
|<<colors.tabs.odd.fg,colors.tabs.odd.fg>>|Foreground color of unselected odd tabs.
|
||||
|<<colors.tabs.pinned.even.bg,colors.tabs.pinned.even.bg>>|Background color of pinned unselected even tabs.
|
||||
|<<colors.tabs.pinned.even.fg,colors.tabs.pinned.even.fg>>|Foreground color of pinned unselected even tabs.
|
||||
|<<colors.tabs.pinned.odd.bg,colors.tabs.pinned.odd.bg>>|Background color of pinned unselected odd tabs.
|
||||
|<<colors.tabs.pinned.odd.fg,colors.tabs.pinned.odd.fg>>|Foreground color of pinned unselected odd tabs.
|
||||
|<<colors.tabs.pinned.selected.even.bg,colors.tabs.pinned.selected.even.bg>>|Background color of pinned selected even tabs.
|
||||
|<<colors.tabs.pinned.selected.even.fg,colors.tabs.pinned.selected.even.fg>>|Foreground color of pinned selected even tabs.
|
||||
|<<colors.tabs.pinned.selected.odd.bg,colors.tabs.pinned.selected.odd.bg>>|Background color of pinned selected odd tabs.
|
||||
|<<colors.tabs.pinned.selected.odd.fg,colors.tabs.pinned.selected.odd.fg>>|Foreground color of pinned selected odd tabs.
|
||||
|<<colors.tabs.selected.even.bg,colors.tabs.selected.even.bg>>|Background color of selected even tabs.
|
||||
|<<colors.tabs.selected.even.fg,colors.tabs.selected.even.fg>>|Foreground color of selected even tabs.
|
||||
|<<colors.tabs.selected.odd.bg,colors.tabs.selected.odd.bg>>|Background color of selected odd tabs.
|
||||
|
|
@ -215,6 +223,7 @@
|
|||
|<<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.
|
||||
|<<input.insert_mode.auto_load,input.insert_mode.auto_load>>|Automatically enter insert mode if an editable element is focused after loading the page.
|
||||
|<<input.insert_mode.leave_on_load,input.insert_mode.leave_on_load>>|Leave insert mode when starting a new page load.
|
||||
|<<input.insert_mode.plugins,input.insert_mode.plugins>>|Switch to insert mode when clicking flash and other plugins.
|
||||
|<<input.links_included_in_focus_chain,input.links_included_in_focus_chain>>|Include hyperlinks in the keyboard focus chain when tabbing.
|
||||
|<<input.partial_timeout,input.partial_timeout>>|Timeout (in milliseconds) for partially typed key bindings.
|
||||
|
|
@ -261,6 +270,7 @@
|
|||
|<<tabs.new_position.stacking,tabs.new_position.stacking>>|Stack related tabs on top of each other when opened consecutively.
|
||||
|<<tabs.new_position.unrelated,tabs.new_position.unrelated>>|Position of new tabs which are not opened from another tab.
|
||||
|<<tabs.padding,tabs.padding>>|Padding (in pixels) around text for tabs.
|
||||
|<<tabs.pinned.frozen,tabs.pinned.frozen>>|Force pinned tabs to stay at fixed URL.
|
||||
|<<tabs.pinned.shrink,tabs.pinned.shrink>>|Shrink pinned tabs down to their contents.
|
||||
|<<tabs.position,tabs.position>>|Position of the tab bar.
|
||||
|<<tabs.select_on_remove,tabs.select_on_remove>>|Which tab to select when the focused tab is removed.
|
||||
|
|
@ -1333,6 +1343,70 @@ Type: <<types,QtColor>>
|
|||
|
||||
Default: +pass:[white]+
|
||||
|
||||
[[colors.tabs.pinned.even.bg]]
|
||||
=== colors.tabs.pinned.even.bg
|
||||
Background color of pinned unselected even tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[darkseagreen]+
|
||||
|
||||
[[colors.tabs.pinned.even.fg]]
|
||||
=== colors.tabs.pinned.even.fg
|
||||
Foreground color of pinned unselected even tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[white]+
|
||||
|
||||
[[colors.tabs.pinned.odd.bg]]
|
||||
=== colors.tabs.pinned.odd.bg
|
||||
Background color of pinned unselected odd tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[seagreen]+
|
||||
|
||||
[[colors.tabs.pinned.odd.fg]]
|
||||
=== colors.tabs.pinned.odd.fg
|
||||
Foreground color of pinned unselected odd tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[white]+
|
||||
|
||||
[[colors.tabs.pinned.selected.even.bg]]
|
||||
=== colors.tabs.pinned.selected.even.bg
|
||||
Background color of pinned selected even tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[black]+
|
||||
|
||||
[[colors.tabs.pinned.selected.even.fg]]
|
||||
=== colors.tabs.pinned.selected.even.fg
|
||||
Foreground color of pinned selected even tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[white]+
|
||||
|
||||
[[colors.tabs.pinned.selected.odd.bg]]
|
||||
=== colors.tabs.pinned.selected.odd.bg
|
||||
Background color of pinned selected odd tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[black]+
|
||||
|
||||
[[colors.tabs.pinned.selected.odd.fg]]
|
||||
=== colors.tabs.pinned.selected.odd.fg
|
||||
Foreground color of pinned selected odd tabs.
|
||||
|
||||
Type: <<types,QtColor>>
|
||||
|
||||
Default: +pass:[white]+
|
||||
|
||||
[[colors.tabs.selected.even.bg]]
|
||||
=== colors.tabs.selected.even.bg
|
||||
Background color of selected even tabs.
|
||||
|
|
@ -1648,7 +1722,7 @@ Type: <<types,Bool>>
|
|||
|
||||
Default: +pass:[true]+
|
||||
|
||||
This setting is only available with the QtWebKit backend.
|
||||
On QtWebEngine, this setting requires Qt 5.12 or newer.
|
||||
|
||||
[[content.frame_flattening]]
|
||||
=== content.frame_flattening
|
||||
|
|
@ -2712,6 +2786,17 @@ Type: <<types,Bool>>
|
|||
|
||||
Default: +pass:[false]+
|
||||
|
||||
[[input.insert_mode.leave_on_load]]
|
||||
=== input.insert_mode.leave_on_load
|
||||
Leave insert mode when starting a new page load.
|
||||
Patterns may be unreliable on this setting, and they may match the url you are navigating to, or the URL you are navigating from.
|
||||
|
||||
This setting supports URL patterns.
|
||||
|
||||
Type: <<types,Bool>>
|
||||
|
||||
Default: +pass:[true]+
|
||||
|
||||
[[input.insert_mode.plugins]]
|
||||
=== input.insert_mode.plugins
|
||||
Switch to insert mode when clicking flash and other plugins.
|
||||
|
|
@ -2962,7 +3047,7 @@ Default: +pass:[false]+
|
|||
=== search.ignore_case
|
||||
When to find text on a page case-insensitively.
|
||||
|
||||
Type: <<types,String>>
|
||||
Type: <<types,IgnoreCase>>
|
||||
|
||||
Valid values:
|
||||
|
||||
|
|
@ -3309,6 +3394,14 @@ Default:
|
|||
- +pass:[right]+: +pass:[5]+
|
||||
- +pass:[top]+: +pass:[0]+
|
||||
|
||||
[[tabs.pinned.frozen]]
|
||||
=== tabs.pinned.frozen
|
||||
Force pinned tabs to stay at fixed URL.
|
||||
|
||||
Type: <<types,Bool>>
|
||||
|
||||
Default: +pass:[true]+
|
||||
|
||||
[[tabs.pinned.shrink]]
|
||||
=== tabs.pinned.shrink
|
||||
Shrink pinned tabs down to their contents.
|
||||
|
|
@ -3626,6 +3719,7 @@ Lists with duplicate flags are invalid. Each item is checked against the valid v
|
|||
|FontFamily|A Qt font family.
|
||||
|FormatString|A string with placeholders.
|
||||
|FuzzyUrl|A URL which gets interpreted as search if needed.
|
||||
|IgnoreCase|Whether to search case insensitively.
|
||||
|Int|Base class for an integer setting.
|
||||
|Key|A name of a key.
|
||||
|List|A list of values.
|
||||
|
|
|
|||
|
|
@ -102,18 +102,12 @@ $ python3 scripts/asciidoc2html.py
|
|||
On Fedora
|
||||
---------
|
||||
|
||||
NOTE: Fedora's packages used to be outdated for a long time, but are
|
||||
now (November 2017) maintained and up-to-date again.
|
||||
|
||||
qutebrowser is available in the official repositories:
|
||||
|
||||
-----
|
||||
# dnf install qutebrowser
|
||||
-----
|
||||
|
||||
However, note that Fedora 25/26 won't be updated to qutebrowser v1.0, so you
|
||||
might want to <<tox,install qutebrowser via tox>> instead there.
|
||||
|
||||
Additional hints
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
|
@ -17,12 +17,12 @@ doc/qutebrowser.1.html:
|
|||
|
||||
install: doc/qutebrowser.1.html
|
||||
$(PYTHON) setup.py install --prefix="$(PREFIX)" --optimize=1 $(SETUPTOOLSOPTS)
|
||||
install -Dm644 misc/qutebrowser.appdata.xml \
|
||||
"$(DESTDIR)$(DATADIR)/metainfo/qutebrowser.appdata.xml"
|
||||
install -Dm644 misc/org.qutebrowser.qutebrowser.appdata.xml \
|
||||
"$(DESTDIR)$(DATADIR)/metainfo/org.qutebrowser.qutebrowser.appdata.xml"
|
||||
install -Dm644 doc/qutebrowser.1 \
|
||||
"$(DESTDIR)$(MANDIR)/man1/qutebrowser.1"
|
||||
install -Dm644 misc/qutebrowser.desktop \
|
||||
"$(DESTDIR)$(DATADIR)/applications/qutebrowser.desktop"
|
||||
install -Dm644 misc/org.qutebrowser.qutebrowser.desktop \
|
||||
"$(DESTDIR)$(DATADIR)/applications/org.qutebrowser.qutebrowser.desktop"
|
||||
$(foreach i,$(ICONSIZES),install -Dm644 "icons/qutebrowser-$(i)x$(i).png" \
|
||||
"$(DESTDIR)$(DATADIR)/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";)
|
||||
install -Dm644 icons/qutebrowser.svg \
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<provides>
|
||||
<binary>qutebrowser</binary>
|
||||
</provides>
|
||||
<launchable type="desktop-id">qutebrowser.desktop</launchable>
|
||||
<launchable type="desktop-id">org.qutebrowser.qutebrowser.desktop</launchable>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/main.png</image>
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
[Desktop Entry]
|
||||
Name=qutebrowser
|
||||
GenericName=Web Browser
|
||||
GenericName[ar]=ﻢﺘﺼﻔﺣ ﺎﻠﺸﺒﻛﺓ
|
||||
GenericName[bg]=Уеб браузър
|
||||
GenericName[ca]=Navegador web
|
||||
GenericName[cs]=WWW prohlížeč
|
||||
GenericName[da]=Browser
|
||||
GenericName[de]=Web-Browser
|
||||
GenericName[el]=Περιηγητής ιστού
|
||||
GenericName[en_GB]=Web Browser
|
||||
GenericName[es]=Navegador web
|
||||
GenericName[et]=Veebibrauser
|
||||
GenericName[fi]=WWW-selain
|
||||
GenericName[fr]=Navigateur Web
|
||||
GenericName[gu]=વેબ બ્રાઉઝર
|
||||
GenericName[he]=דפדפן אינטרנט
|
||||
GenericName[hi]=वेब ब्राउज़र
|
||||
GenericName[hu]=Webböngésző
|
||||
GenericName[it]=Browser Web
|
||||
GenericName[ja]=ウェブブラウザ
|
||||
GenericName[kn]=ಜಾಲ ವೀಕ್ಷಕ
|
||||
GenericName[ko]=웹 브라우저
|
||||
GenericName[lt]=Žiniatinklio naršyklė
|
||||
GenericName[lv]=Tīmekļa pārlūks
|
||||
GenericName[ml]=വെബ് ബ്രൌസര്<200d>
|
||||
GenericName[mr]=वेब ब्राऊजर
|
||||
GenericName[nb]=Nettleser
|
||||
GenericName[nl]=Webbrowser
|
||||
GenericName[pl]=Przeglądarka WWW
|
||||
GenericName[pt]=Navegador Web
|
||||
GenericName[pt_BR]=Navegador da Internet
|
||||
GenericName[ro]=Navigator de Internet
|
||||
GenericName[ru]=Веб-браузер
|
||||
GenericName[sl]=Spletni brskalnik
|
||||
GenericName[sv]=Webbläsare
|
||||
GenericName[ta]=இணைய உலாவி
|
||||
GenericName[th]=เว็บเบราว์เซอร์
|
||||
GenericName[tr]=Web Tarayıcı
|
||||
GenericName[uk]=Навігатор Тенет瀏覽器
|
||||
Comment=A keyboard-driven, vim-like browser based on PyQt5
|
||||
Comment[it]= Un browser web vim-like utilizzabile da tastiera basato su PyQt5
|
||||
Icon=qutebrowser
|
||||
Type=Application
|
||||
Categories=Network;WebBrowser;
|
||||
Exec=qutebrowser %u
|
||||
Terminal=false
|
||||
StartupNotify=false
|
||||
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute;
|
||||
Keywords=Browser
|
||||
Actions=new-window;preferences;
|
||||
|
||||
[Desktop Action new-window]
|
||||
Name=New Window
|
||||
Name[am]=አዲስ መስኮት
|
||||
Name[ar]=ﻥﺎﻓﺫﺓ ﺝﺪﻳﺩﺓ
|
||||
Name[bg]=Нов прозорец
|
||||
Name[bn]=নতুন উইন্ডো
|
||||
Name[ca]=Finestra nova
|
||||
Name[cs]=Nové okno
|
||||
Name[da]=Nyt vindue
|
||||
Name[de]=Neues Fenster
|
||||
Name[el]=Νέο Παράθυρο
|
||||
Name[en_GB]=New Window
|
||||
Name[es]=Nueva ventana
|
||||
Name[et]=Uus aken
|
||||
Name[fa]=پﻦﺟﺮﻫ ﺝﺩیﺩ
|
||||
Name[fi]=Uusi ikkuna
|
||||
Name[fil]=New Window
|
||||
Name[fr]=Nouvelle fenêtre
|
||||
Name[gu]=નવી વિંડો
|
||||
Name[hi]=नई विंडो
|
||||
Name[hr]=Novi prozor
|
||||
Name[hu]=Új ablak
|
||||
Name[id]=Jendela Baru
|
||||
Name[it]=Nuova finestra
|
||||
Name[iw]=חלון חדש
|
||||
Name[ja]=新規ウインドウ
|
||||
Name[kn]=ಹೊಸ ವಿಂಡೊ
|
||||
Name[ko]=새 창
|
||||
Name[lt]=Naujas langas
|
||||
Name[lv]=Jauns logs
|
||||
Name[ml]=പുതിയ വിന്<200d>ഡോ
|
||||
Name[mr]=नवीन विंडो
|
||||
Name[nl]=Nieuw venster
|
||||
Name[no]=Nytt vindu
|
||||
Name[pl]=Nowe okno
|
||||
Name[pt]=Nova janela
|
||||
Name[pt_BR]=Nova janela
|
||||
Name[ro]=Fereastră nouă
|
||||
Name[ru]=Новое окно
|
||||
Name[sk]=Nové okno
|
||||
Name[sl]=Novo okno
|
||||
Name[sr]=Нови прозор
|
||||
Name[sv]=Nytt fönster
|
||||
Name[sw]=Dirisha Jipya
|
||||
Name[ta]=புதிய சாளரம்
|
||||
Name[te]=క్రొత్త విండో
|
||||
Name[th]=หน้าต่างใหม่
|
||||
Name[tr]=Yeni Pencere
|
||||
Name[uk]=Нове вікно
|
||||
Name[vi]=Cửa sổ Mới
|
||||
Exec=qutebrowser
|
||||
|
||||
[Desktop Action preferences]
|
||||
Name=Preferences
|
||||
Name[an]=Preferencias
|
||||
Name[ar]=ﺎﻠﺘﻔﻀﻳﻼﺗ
|
||||
Name[as]=পছন্দসমূহ
|
||||
Name[be]=Настройкі
|
||||
Name[bg]=Настройки
|
||||
Name[bn_IN]=পছন্দ
|
||||
Name[bs]=Postavke
|
||||
Name[ca]=Preferències
|
||||
Name[ca@valencia]=Preferències
|
||||
Name[cs]=Předvolby
|
||||
Name[da]=Indstillinger
|
||||
Name[de]=Einstellungen
|
||||
Name[el]=Προτιμήσεις
|
||||
Name[en_GB]=Preferences
|
||||
Name[eo]=Agordoj
|
||||
Name[es]=Preferencias
|
||||
Name[et]=Eelistused
|
||||
Name[eu]=Hobespenak
|
||||
Name[fa]=ﺕﺮﺟیﺡﺎﺗ
|
||||
Name[fi]=Asetukset
|
||||
Name[fr]=Préférences
|
||||
Name[fur]=Preferencis
|
||||
Name[ga]=Sainroghanna
|
||||
Name[gd]=Roghainnean
|
||||
Name[gl]=Preferencias
|
||||
Name[gu]=પસંદગીઓ
|
||||
Name[he]=העדפות
|
||||
Name[hi]=वरीयताएँ
|
||||
Name[hr]=Osobitosti
|
||||
Name[hu]=Beállítások
|
||||
Name[id]=Preferensi
|
||||
Name[is]=Kjörstillingar
|
||||
Name[it]=Preferenze
|
||||
Name[ja]=設定
|
||||
Name[kk]=Баптаулар
|
||||
Name[km]=ចំណូលចិត្ត
|
||||
Name[kn]=ಆದ್ಯತೆಗಳು
|
||||
Name[ko]=기본 설정
|
||||
Name[lt]=Nuostatos
|
||||
Name[lv]=Iestatījumi
|
||||
Name[ml]=മുന്<200d>ഗണനകള്<200d>
|
||||
Name[mr]=पसंती
|
||||
Name[nb]=Brukervalg
|
||||
Name[ne]=प्राथमिकताहरू
|
||||
Name[nl]=Voorkeuren
|
||||
Name[oc]=Preferéncias
|
||||
Name[or]=ପସନ୍ଦ
|
||||
Name[pa]=ਮੇਰੀ ਪਸੰਦ
|
||||
Name[pl]=Preferencje
|
||||
Name[pt]=Preferências
|
||||
Name[pt_BR]=Preferências
|
||||
Name[ro]=Preferințe
|
||||
Name[ru]=Параметры
|
||||
Name[sk]=Nastavenia
|
||||
Name[sl]=Možnosti
|
||||
Name[sr]=Поставке
|
||||
Name[sr@latin]=Postavke
|
||||
Name[sv]=Inställningar
|
||||
Name[ta]=விருப்பங்கள்
|
||||
Name[te]=అభీష్టాలు
|
||||
Name[tg]=Хусусиятҳо
|
||||
Name[th]=ปรับแต่ง
|
||||
Name[tr]=Tercihler
|
||||
Name[ug]=ﻡﺎﻳﻰﻠﻟﻰﻗ
|
||||
Name[uk]=Параметри
|
||||
Name[vi]=Tùy thích
|
||||
Name[zh_CN]=首选项
|
||||
Name[zh_HK]=偏好設定
|
||||
Name[zh_TW]=偏好設定
|
||||
Exec=qutebrowser "qute://settings"
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Name=qutebrowser
|
||||
GenericName=Web Browser
|
||||
Comment=A keyboard-driven, vim-like browser based on PyQt5
|
||||
Icon=qutebrowser
|
||||
Type=Application
|
||||
Categories=Network;WebBrowser;
|
||||
Exec=qutebrowser %u
|
||||
Terminal=false
|
||||
StartupNotify=false
|
||||
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute;
|
||||
Keywords=Browser
|
||||
|
|
@ -6,6 +6,8 @@ import os
|
|||
sys.path.insert(0, os.getcwd())
|
||||
from scripts import setupcommon
|
||||
|
||||
from qutebrowser.extensions import loader
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
|
|
@ -27,13 +29,20 @@ def get_data_files():
|
|||
return data_files
|
||||
|
||||
|
||||
def get_hidden_imports():
|
||||
imports = ['PyQt5.QtOpenGL', 'PyQt5._QOpenGLFunctions_2_0']
|
||||
for info in loader.walk_components():
|
||||
imports.append(info.name)
|
||||
return imports
|
||||
|
||||
|
||||
setupcommon.write_git_file()
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
icon = 'icons/qutebrowser.ico'
|
||||
icon = '../icons/qutebrowser.ico'
|
||||
elif sys.platform == 'darwin':
|
||||
icon = 'icons/qutebrowser.icns'
|
||||
icon = '../icons/qutebrowser.icns'
|
||||
else:
|
||||
icon = None
|
||||
|
||||
|
|
@ -42,7 +51,7 @@ a = Analysis(['../qutebrowser/__main__.py'],
|
|||
pathex=['misc'],
|
||||
binaries=None,
|
||||
datas=get_data_files(),
|
||||
hiddenimports=['PyQt5.QtOpenGL', 'PyQt5._QOpenGLFunctions_2_0'],
|
||||
hiddenimports=get_hidden_imports(),
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=['tkinter'],
|
||||
|
|
@ -60,7 +69,7 @@ exe = EXE(pyz,
|
|||
strip=False,
|
||||
upx=False,
|
||||
console=False,
|
||||
version='misc/file_version_info.txt')
|
||||
version='../misc/file_version_info.txt')
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
certifi==2018.10.15
|
||||
certifi==2019.3.9
|
||||
chardet==3.0.4
|
||||
codecov==2.0.15
|
||||
coverage==4.5.2
|
||||
idna==2.7
|
||||
requests==2.20.1
|
||||
coverage==4.5.3
|
||||
idna==2.8
|
||||
requests==2.21.0
|
||||
urllib3==1.24.1
|
||||
|
|
|
|||
|
|
@ -1,27 +1,25 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
attrs==18.2.0
|
||||
flake8==3.6.0
|
||||
attrs==19.1.0
|
||||
entrypoints==0.3
|
||||
flake8==3.7.7
|
||||
flake8-bugbear==18.8.0
|
||||
flake8-builtins==1.4.1
|
||||
flake8-comprehensions==1.4.1
|
||||
flake8-comprehensions==2.1.0
|
||||
flake8-copyright==0.2.2
|
||||
flake8-debugger==3.1.0
|
||||
flake8-deprecated==1.3
|
||||
flake8-docstrings==1.3.0
|
||||
flake8-future-import==0.4.5
|
||||
flake8-mock==0.3
|
||||
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
|
||||
flake8-tidy-imports==2.0.0
|
||||
flake8-tuple==0.2.14
|
||||
mccabe==0.6.1
|
||||
pathmatch==0.2.1
|
||||
pep8-naming==0.7.0
|
||||
pycodestyle==2.4.0
|
||||
pep8-naming==0.8.2
|
||||
pycodestyle==2.5.0
|
||||
pydocstyle==3.0.0
|
||||
pyflakes==2.0.0
|
||||
six==1.11.0
|
||||
pyflakes==2.1.1
|
||||
six==1.12.0
|
||||
snowballstemmer==1.2.1
|
||||
typing==3.6.6
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ flake8-deprecated
|
|||
flake8-docstrings
|
||||
flake8-future-import
|
||||
flake8-mock
|
||||
flake8-per-file-ignores
|
||||
flake8-string-format
|
||||
flake8-tidy-imports
|
||||
flake8-tuple
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
mypy==0.670
|
||||
mypy-extensions==0.4.1
|
||||
# PyQt5==5.11.3
|
||||
# PyQt5-sip==4.19.14
|
||||
-e git+https://github.com/qutebrowser/PyQt5-stubs.git@wip#egg=PyQt5_stubs
|
||||
typed-ast==1.3.1
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
mypy
|
||||
-e git+https://github.com/qutebrowser/PyQt5-stubs.git@wip#egg=PyQt5-stubs
|
||||
|
||||
# remove @commit-id for scm installs
|
||||
#@ replace: @.*# @wip#
|
||||
#@ ignore: PyQt5, PyQt5-sip
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
colorama==0.4.1
|
||||
cssutils==1.0.2
|
||||
hunter==2.2.1
|
||||
Pympler==0.6
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
hunter
|
||||
cssutils
|
||||
pympler
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
appdirs==1.4.3
|
||||
packaging==18.0
|
||||
pyparsing==2.3.0
|
||||
setuptools==40.5.0
|
||||
six==1.11.0
|
||||
wheel==0.32.2
|
||||
packaging==19.0
|
||||
pyparsing==2.3.1
|
||||
setuptools==40.8.0
|
||||
six==1.12.0
|
||||
wheel==0.33.1
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
asn1crypto==0.24.0
|
||||
astroid==2.0.4
|
||||
certifi==2018.10.15
|
||||
cffi==1.11.5
|
||||
astroid==2.2.5
|
||||
certifi==2019.3.9
|
||||
cffi==1.12.2
|
||||
chardet==3.0.4
|
||||
cryptography==2.4.1
|
||||
github3.py==1.2.0
|
||||
idna==2.7
|
||||
isort==4.3.4
|
||||
cryptography==2.6.1
|
||||
github3.py==1.3.0
|
||||
idna==2.8
|
||||
isort==4.3.15
|
||||
jwcrypto==0.6.0
|
||||
lazy-object-proxy==1.3.1
|
||||
mccabe==0.6.1
|
||||
pycparser==2.19
|
||||
pylint==2.1.1
|
||||
python-dateutil==2.7.5
|
||||
pylint==2.3.1
|
||||
python-dateutil==2.8.0
|
||||
./scripts/dev/pylint_checkers
|
||||
requests==2.20.1
|
||||
six==1.11.0
|
||||
requests==2.21.0
|
||||
six==1.12.0
|
||||
uritemplate==3.0.0
|
||||
urllib3==1.24.1
|
||||
wrapt==1.10.11
|
||||
wrapt==1.11.1
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt5==5.11.3
|
||||
PyQt5-sip==4.19.13
|
||||
PyQt5==5.12.1
|
||||
PyQt5-sip==4.19.15
|
||||
PyQtWebEngine==5.12.1
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
PyQt5
|
||||
PyQt5
|
||||
PyQtWebEngine
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
alabaster==0.7.12
|
||||
Babel==2.6.0
|
||||
certifi==2019.3.9
|
||||
chardet==3.0.4
|
||||
docutils==0.14
|
||||
idna==2.8
|
||||
imagesize==1.1.0
|
||||
Jinja2==2.10
|
||||
MarkupSafe==1.1.1
|
||||
packaging==19.0
|
||||
Pygments==2.3.1
|
||||
pyparsing==2.3.1
|
||||
pytz==2018.9
|
||||
requests==2.21.0
|
||||
six==1.12.0
|
||||
snowballstemmer==1.2.1
|
||||
Sphinx==1.8.5
|
||||
sphinxcontrib-websupport==1.1.0
|
||||
urllib3==1.24.1
|
||||
|
|
@ -0,0 +1 @@
|
|||
sphinx
|
||||
|
|
@ -1,42 +1,42 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
atomicwrites==1.2.1
|
||||
attrs==18.2.0
|
||||
atomicwrites==1.3.0
|
||||
attrs==19.1.0
|
||||
backports.functools-lru-cache==1.5
|
||||
beautifulsoup4==4.6.3
|
||||
cheroot==6.5.2
|
||||
beautifulsoup4==4.7.1
|
||||
cheroot==6.5.4
|
||||
Click==7.0
|
||||
# colorama==0.4.0
|
||||
coverage==4.5.2
|
||||
EasyProcess==0.2.3
|
||||
fields==5.0.0
|
||||
# colorama==0.4.1
|
||||
coverage==4.5.3
|
||||
EasyProcess==0.2.5
|
||||
Flask==1.0.2
|
||||
glob2==0.6
|
||||
hunter==2.0.2
|
||||
hypothesis==3.82.1
|
||||
hunter==2.2.1
|
||||
hypothesis==4.12.0
|
||||
itsdangerous==1.1.0
|
||||
# Jinja2==2.10
|
||||
Mako==1.0.7
|
||||
# MarkupSafe==1.1.0
|
||||
more-itertools==4.3.0
|
||||
parse==1.9.0
|
||||
more-itertools==6.0.0
|
||||
parse==1.11.1
|
||||
parse-type==0.4.2
|
||||
pluggy==0.8.0
|
||||
py==1.7.0
|
||||
pluggy==0.9.0
|
||||
py==1.8.0
|
||||
py-cpuinfo==4.0.0
|
||||
pytest==4.0.0
|
||||
pytest-bdd==3.0.0
|
||||
pytest-benchmark==3.1.1
|
||||
pytest-cov==2.6.0
|
||||
pytest==4.3.1
|
||||
pytest-bdd==3.1.0
|
||||
pytest-benchmark==3.2.2
|
||||
pytest-cov==2.6.1
|
||||
pytest-faulthandler==1.5.0
|
||||
pytest-instafail==0.4.0
|
||||
pytest-mock==1.10.0
|
||||
pytest-qt==3.2.1
|
||||
pytest-repeat==0.7.0
|
||||
pytest-rerunfailures==5.0
|
||||
pytest-instafail==0.4.1
|
||||
pytest-mock==1.10.1
|
||||
pytest-qt==3.2.2
|
||||
pytest-repeat==0.8.0
|
||||
pytest-rerunfailures==6.0
|
||||
pytest-travis-fold==1.3.0
|
||||
pytest-xvfb==1.1.0
|
||||
pytest-xvfb==1.2.0
|
||||
PyVirtualDisplay==0.2.1
|
||||
six==1.11.0
|
||||
six==1.12.0
|
||||
soupsieve==1.8
|
||||
vulture==1.0
|
||||
Werkzeug==0.14.1
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
filelock==3.0.10
|
||||
pluggy==0.8.0
|
||||
py==1.7.0
|
||||
six==1.11.0
|
||||
pluggy==0.9.0
|
||||
py==1.8.0
|
||||
six==1.12.0
|
||||
toml==0.10.0
|
||||
tox==3.5.3
|
||||
virtualenv==16.1.0
|
||||
tox==3.7.0
|
||||
virtualenv==16.4.3
|
||||
|
|
|
|||
|
|
@ -53,9 +53,10 @@ The following userscripts can be found on their own repositories.
|
|||
- [qtb.us](https://github.com/Chinggis6/qtb.us): small pack of userscripts.
|
||||
- [pinboard.zsh](https://github.com/dmix/pinboard.zsh): Add URL to your
|
||||
[Pinboard][] bookmark manager.
|
||||
- [qute-capture](https://github.com/alcah/qute-capture): Capture links with
|
||||
Emacs's org-mode to a read-later file.
|
||||
|
||||
[Zotero]: https://www.zotero.org/
|
||||
[Pocket]: https://getpocket.com/
|
||||
[Instapaper]: https://www.instapaper.com/
|
||||
[Pinboard]: https://pinboard.in/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2015-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
|
||||
# Copyright 2015-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
#
|
||||
# Behavior:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015 jnphilipp <me@jnphilipp.org>
|
||||
# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ die() {
|
|||
javascript_escape() {
|
||||
# print the first argument in an escaped way, such that it can safely
|
||||
# be used within javascripts double quotes
|
||||
# shellcheck disable=SC2001
|
||||
sed "s,[\\\\'\"],\\\\&,g" <<< "$1"
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +112,7 @@ simplify_url() {
|
|||
# are found:
|
||||
no_entries_found() {
|
||||
while [ 0 -eq "${#files[@]}" ] && [ -n "$simple_url" ]; do
|
||||
# shellcheck disable=SC2001
|
||||
shorter_simple_url=$(sed 's,^[^.]*\.,,' <<< "$simple_url")
|
||||
if [ "$shorter_simple_url" = "$simple_url" ] ; then
|
||||
# if no dot, then even remove the top level domain
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2018 Jay Kamat <jaygkamat@gmail.com>
|
||||
# Copyright 2018-2019 Jay Kamat <jaygkamat@gmail.com>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -148,7 +148,7 @@ def dmenu(items, invocation, encoding):
|
|||
|
||||
def get_password():
|
||||
"""Get a keepass db password from user."""
|
||||
_app = QApplication(sys.argv)
|
||||
_app = QApplication(sys.argv) # don't remove this local variable
|
||||
text, ok = QInputDialog.getText(
|
||||
None, "KeePass DB Password",
|
||||
"Please enter your KeePass Master Password",
|
||||
|
|
|
|||
|
|
@ -97,13 +97,19 @@ def qute_command(command):
|
|||
def find_pass_candidates(domain, password_store_path):
|
||||
candidates = []
|
||||
for path, directories, file_names in os.walk(password_store_path, followlinks=True):
|
||||
if directories or domain not in path.split(os.path.sep):
|
||||
secrets = fnmatch.filter(file_names, '*.gpg')
|
||||
if not secrets:
|
||||
continue
|
||||
|
||||
# Strip password store path prefix to get the relative pass path
|
||||
pass_path = path[len(password_store_path) + 1:]
|
||||
secrets = fnmatch.filter(file_names, '*.gpg')
|
||||
candidates.extend(os.path.join(pass_path, os.path.splitext(secret)[0]) for secret in secrets)
|
||||
split_path = pass_path.split(os.path.sep)
|
||||
for secret in secrets:
|
||||
secret_base = os.path.splitext(secret)[0]
|
||||
if domain not in (split_path + [secret_base]):
|
||||
continue
|
||||
|
||||
candidates.append(os.path.join(pass_path, secret_base))
|
||||
return candidates
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ get_selection() {
|
|||
# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/font
|
||||
[[ -s $confdir/dmenu/font ]] && read -r font < "$confdir"/dmenu/font
|
||||
|
||||
[[ $font ]] && opts+=(-fn "$font")
|
||||
[[ -n $font ]] && opts+=(-fn "$font")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
[[ -s $optsfile ]] && source "$optsfile"
|
||||
|
|
@ -46,7 +46,7 @@ url=$(get_selection)
|
|||
url=${url/*http/http}
|
||||
|
||||
# If no selection is made, exit (escape pressed, e.g.)
|
||||
[[ ! $url ]] && exit 0
|
||||
[[ -z $url ]] && exit 0
|
||||
|
||||
case $1 in
|
||||
open) printf '%s' "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
[mypy]
|
||||
# We also need to support 3.5, but if we'd chose that here, we'd need to deal
|
||||
# with conditional imports (like secrets.py).
|
||||
python_version = 3.6
|
||||
|
||||
# --strict
|
||||
warn_redundant_casts = True
|
||||
warn_unused_ignores = True
|
||||
disallow_subclassing_any = True
|
||||
disallow_untyped_decorators = True
|
||||
## https://github.com/python/mypy/issues/5957
|
||||
# warn_unused_configs = True
|
||||
# disallow_untyped_calls = True
|
||||
# disallow_untyped_defs = True
|
||||
## https://github.com/python/mypy/issues/5954
|
||||
# disallow_incomplete_defs = True
|
||||
# check_untyped_defs = True
|
||||
# no_implicit_optional = True
|
||||
# warn_return_any = True
|
||||
|
||||
[mypy-colorama]
|
||||
# https://github.com/tartley/colorama/issues/206
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-hunter]
|
||||
# https://github.com/ionelmc/python-hunter/issues/43
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-pygments.*]
|
||||
# https://bitbucket.org/birkenfeld/pygments-main/issues/1485/type-hints
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-cssutils]
|
||||
# Pretty much inactive currently
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-pypeg2]
|
||||
# Pretty much inactive currently
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-bdb]
|
||||
# stdlib, missing in typeshed
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-qutebrowser.browser.webkit.rfc6266]
|
||||
# subclasses dynamic PyPEG2 classes
|
||||
disallow_subclassing_any = False
|
||||
|
||||
[mypy-qutebrowser.browser.browsertab]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.misc.objects]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.commands.cmdutils]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.config.*]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.api.*]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.components.*]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.extensions.*]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.browser.webelem]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.browser.webkit.webkitelem]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
|
||||
[mypy-qutebrowser.browser.webengine.webengineelem]
|
||||
disallow_untyped_defs = True
|
||||
disallow_incomplete_defs = True
|
||||
10
pytest.ini
10
pytest.ini
|
|
@ -64,10 +64,8 @@ qt_log_ignore =
|
|||
^QSettings::value: Empty key passed
|
||||
^Icon theme ".*" not found
|
||||
^Error receiving trust for a CA certificate
|
||||
^QBackingStore::endPaint\(\) called with active painter on backingstore paint device
|
||||
^QPaintDevice: Cannot destroy paint device that is being painted
|
||||
^DirectWrite: CreateFontFaceFromHDC\(\) failed .*
|
||||
xfail_strict = true
|
||||
filterwarnings =
|
||||
error
|
||||
# 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
|
||||
# WORKAROUND for https://github.com/ionelmc/pytest-benchmark/issues/124
|
||||
ignore:Node\.warn\(code, message\) form has been deprecated, use Node\.warn\(warning_instance\) instead:pytest.PytestDeprecationWarning
|
||||
filterwarnings = error
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -22,11 +22,11 @@
|
|||
import os.path
|
||||
|
||||
__author__ = "Florian Bruhin"
|
||||
__copyright__ = "Copyright 2014-2018 Florian Bruhin (The Compiler)"
|
||||
__copyright__ = "Copyright 2014-2019 Florian Bruhin (The Compiler)"
|
||||
__license__ = "GPL"
|
||||
__maintainer__ = __author__
|
||||
__email__ = "mail@qutebrowser.org"
|
||||
__version_info__ = (1, 5, 2)
|
||||
__version_info__ = (1, 6, 1)
|
||||
__version__ = '.'.join(str(e) for e in __version_info__)
|
||||
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018-2019 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/>.
|
||||
|
||||
"""API for extensions.
|
||||
|
||||
This API currently isn't exposed to third-party extensions yet, but will be in
|
||||
the future. Thus, care must be taken when adding new APIs here.
|
||||
|
||||
Code in qutebrowser.components only uses this API.
|
||||
"""
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018-2019 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/>.
|
||||
|
||||
"""A single tab."""
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from qutebrowser.browser.browsertab import WebTabError, AbstractTab as Tab
|
||||
from qutebrowser.browser.webelem import (Error as WebElemError,
|
||||
AbstractWebElement as WebElement)
|
||||
from qutebrowser.utils.usertypes import ClickTarget, JsWorld
|
||||
from qutebrowser.extensions.loader import InitContext
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2019 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/>.
|
||||
|
||||
"""qutebrowser has the concept of functions, exposed to the user as commands.
|
||||
|
||||
Creating a new command is straightforward::
|
||||
|
||||
from qutebrowser.api import cmdutils
|
||||
|
||||
@cmdutils.register(...)
|
||||
def foo():
|
||||
...
|
||||
|
||||
The commands arguments are automatically deduced by inspecting your function.
|
||||
|
||||
The types of the function arguments are inferred based on their default values,
|
||||
e.g., an argument `foo=True` will be converted to a flag `-f`/`--foo` in
|
||||
qutebrowser's commandline.
|
||||
|
||||
The type can be overridden using Python's function annotations::
|
||||
|
||||
@cmdutils.register(...)
|
||||
def foo(bar: int, baz=True):
|
||||
...
|
||||
|
||||
Possible values:
|
||||
|
||||
- A callable (``int``, ``float``, etc.): Gets called to validate/convert the
|
||||
value.
|
||||
- A python enum type: All members of the enum are possible values.
|
||||
- A ``typing.Union`` of multiple types above: Any of these types are valid
|
||||
values, e.g., ``typing.Union[str, int]``.
|
||||
"""
|
||||
|
||||
|
||||
import inspect
|
||||
import typing
|
||||
|
||||
from qutebrowser.utils import qtutils
|
||||
from qutebrowser.commands import command, cmdexc
|
||||
# pylint: disable=unused-import
|
||||
from qutebrowser.utils.usertypes import KeyMode, CommandValue as Value
|
||||
|
||||
|
||||
class CommandError(cmdexc.Error):
|
||||
|
||||
"""Raised when a command encounters an error while running.
|
||||
|
||||
If your command handler encounters an error and cannot continue, raise this
|
||||
exception with an appropriate error message::
|
||||
|
||||
raise cmdexc.CommandError("Message")
|
||||
|
||||
The message will then be shown in the qutebrowser status bar.
|
||||
|
||||
.. note::
|
||||
|
||||
You should only raise this exception while a command handler is run.
|
||||
Raising it at another point causes qutebrowser to crash due to an
|
||||
unhandled exception.
|
||||
"""
|
||||
|
||||
|
||||
def check_overflow(arg: int, ctype: str) -> None:
|
||||
"""Check if the given argument is in bounds for the given type.
|
||||
|
||||
Args:
|
||||
arg: The argument to check.
|
||||
ctype: The C++/Qt type to check as a string ('int'/'int64').
|
||||
"""
|
||||
try:
|
||||
qtutils.check_overflow(arg, ctype)
|
||||
except OverflowError:
|
||||
raise CommandError("Numeric argument is too large for internal {} "
|
||||
"representation.".format(ctype))
|
||||
|
||||
|
||||
def check_exclusive(flags: typing.Iterable[bool],
|
||||
names: typing.Iterable[str]) -> None:
|
||||
"""Check if only one flag is set with exclusive flags.
|
||||
|
||||
Raise a CommandError if not.
|
||||
|
||||
Args:
|
||||
flags: The flag values to check.
|
||||
names: A list of names (corresponding to the flags argument).
|
||||
"""
|
||||
if sum(1 for e in flags if e) > 1:
|
||||
argstr = '/'.join('-' + e for e in names)
|
||||
raise CommandError("Only one of {} can be given!".format(argstr))
|
||||
|
||||
|
||||
class register: # noqa: N801,N806 pylint: disable=invalid-name
|
||||
|
||||
"""Decorator to register a new command handler."""
|
||||
|
||||
def __init__(self, *,
|
||||
instance: str = None,
|
||||
name: str = None,
|
||||
**kwargs: typing.Any) -> None:
|
||||
"""Save decorator arguments.
|
||||
|
||||
Gets called on parse-time with the decorator arguments.
|
||||
|
||||
Args:
|
||||
See class attributes.
|
||||
"""
|
||||
# The object from the object registry to be used as "self".
|
||||
self._instance = instance
|
||||
# The name (as string) or names (as list) of the command.
|
||||
self._name = name
|
||||
# The arguments to pass to Command.
|
||||
self._kwargs = kwargs
|
||||
|
||||
def __call__(self, func: typing.Callable) -> typing.Callable:
|
||||
"""Register the command before running the function.
|
||||
|
||||
Gets called when a function should be decorated.
|
||||
|
||||
Doesn't actually decorate anything, but creates a Command object and
|
||||
registers it in the global commands dict.
|
||||
|
||||
Args:
|
||||
func: The function to be decorated.
|
||||
|
||||
Return:
|
||||
The original function (unmodified).
|
||||
"""
|
||||
if self._name is None:
|
||||
name = func.__name__.lower().replace('_', '-')
|
||||
else:
|
||||
assert isinstance(self._name, str), self._name
|
||||
name = self._name
|
||||
|
||||
cmd = command.Command(name=name, instance=self._instance,
|
||||
handler=func, **self._kwargs)
|
||||
cmd.register()
|
||||
return func
|
||||
|
||||
|
||||
class argument: # noqa: N801,N806 pylint: disable=invalid-name
|
||||
|
||||
"""Decorator to customize an argument.
|
||||
|
||||
You can customize how an argument is handled using the
|
||||
``@cmdutils.argument`` decorator *after* ``@cmdutils.register``. This can,
|
||||
for example, be used to customize the flag an argument should get::
|
||||
|
||||
@cmdutils.register(...)
|
||||
@cmdutils.argument('bar', flag='c')
|
||||
def foo(bar):
|
||||
...
|
||||
|
||||
For a ``str`` argument, you can restrict the allowed strings using
|
||||
``choices``::
|
||||
|
||||
@cmdutils.register(...)
|
||||
@cmdutils.argument('bar', choices=['val1', 'val2'])
|
||||
def foo(bar: str):
|
||||
...
|
||||
|
||||
For ``typing.Union`` types, the given ``choices`` are only checked if other
|
||||
types (like ``int``) don't match.
|
||||
|
||||
The following arguments are supported for ``@cmdutils.argument``:
|
||||
|
||||
- ``flag``: Customize the short flag (``-x``) the argument will get.
|
||||
- ``value``: Tell qutebrowser to fill the argument with special values:
|
||||
|
||||
* ``value=cmdutils.Value.count``: The ``count`` given by the user to the
|
||||
command.
|
||||
* ``value=cmdutils.Value.win_id``: The window ID of the current window.
|
||||
* ``value=cmdutils.Value.cur_tab``: The tab object which is currently
|
||||
focused.
|
||||
|
||||
- ``completion``: A completion function to use when completing arguments
|
||||
for the given command.
|
||||
- ``choices``: The allowed string choices for the argument.
|
||||
|
||||
The name of an argument will always be the parameter name, with any
|
||||
trailing underscores stripped and underscores replaced by dashes.
|
||||
"""
|
||||
|
||||
def __init__(self, argname: str, **kwargs: typing.Any) -> None:
|
||||
self._argname = argname # The name of the argument to handle.
|
||||
self._kwargs = kwargs # Valid ArgInfo members.
|
||||
|
||||
def __call__(self, func: typing.Callable) -> typing.Callable:
|
||||
funcname = func.__name__
|
||||
|
||||
if self._argname not in inspect.signature(func).parameters:
|
||||
raise ValueError("{} has no argument {}!".format(funcname,
|
||||
self._argname))
|
||||
if not hasattr(func, 'qute_args'):
|
||||
func.qute_args = {} # type: ignore
|
||||
elif func.qute_args is None: # type: ignore
|
||||
raise ValueError("@cmdutils.argument got called above (after) "
|
||||
"@cmdutils.register for {}!".format(funcname))
|
||||
|
||||
arginfo = command.ArgInfo(**self._kwargs)
|
||||
func.qute_args[self._argname] = arginfo # type: ignore
|
||||
|
||||
return func
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018-2019 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/>.
|
||||
|
||||
"""Access to the qutebrowser configuration."""
|
||||
|
||||
import typing
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
|
||||
from qutebrowser.config import config
|
||||
|
||||
#: Simplified access to config values using attribute acccess.
|
||||
#: For example, to access the ``content.javascript.enabled`` setting,
|
||||
#: you can do::
|
||||
#:
|
||||
#: if config.val.content.javascript.enabled:
|
||||
#: ...
|
||||
#:
|
||||
#: This also supports setting configuration values::
|
||||
#:
|
||||
#: config.val.content.javascript.enabled = False
|
||||
val = typing.cast('config.ConfigContainer', None)
|
||||
|
||||
|
||||
def get(name: str, url: QUrl = None) -> typing.Any:
|
||||
"""Get a value from the config based on a string name."""
|
||||
return config.instance.get(name, url)
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018-2019 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/>.
|
||||
|
||||
|
||||
"""APIs related to downloading files."""
|
||||
|
||||
|
||||
import io
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QUrl
|
||||
|
||||
from qutebrowser.browser import downloads, qtnetworkdownloads
|
||||
from qutebrowser.utils import objreg
|
||||
|
||||
|
||||
class TempDownload(QObject):
|
||||
|
||||
"""A download of some data into a file object."""
|
||||
|
||||
finished = pyqtSignal()
|
||||
|
||||
def __init__(self, item: qtnetworkdownloads.DownloadItem) -> None:
|
||||
super().__init__()
|
||||
self._item = item
|
||||
self._item.finished.connect(self._on_download_finished)
|
||||
self.successful = False
|
||||
self.fileobj = item.fileobj
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_download_finished(self) -> None:
|
||||
self.successful = self._item.successful
|
||||
self.finished.emit()
|
||||
|
||||
|
||||
def download_temp(url: QUrl) -> TempDownload:
|
||||
"""Download the given URL into a file object.
|
||||
|
||||
The download is not saved to disk.
|
||||
|
||||
Returns a ``TempDownload`` object, which triggers a ``finished`` signal
|
||||
when the download has finished::
|
||||
|
||||
dl = downloads.download_temp(QUrl("https://www.example.com/"))
|
||||
dl.finished.connect(functools.partial(on_download_finished, dl))
|
||||
|
||||
After the download has finished, its ``successful`` attribute can be
|
||||
checked to make sure it finished successfully. If so, its contents can be
|
||||
read by accessing the ``fileobj`` attribute::
|
||||
|
||||
def on_download_finished(download: downloads.TempDownload) -> None:
|
||||
if download.successful:
|
||||
print(download.fileobj.read())
|
||||
download.fileobj.close()
|
||||
"""
|
||||
fobj = io.BytesIO()
|
||||
fobj.name = 'temporary: ' + url.host()
|
||||
target = downloads.FileObjDownloadTarget(fobj)
|
||||
download_manager = objreg.get('qtnetwork-download-manager')
|
||||
return download_manager.get(url, target=target, auto_remove=True)
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018-2019 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/>.
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
"""Hooks for extensions."""
|
||||
|
||||
import importlib
|
||||
import typing
|
||||
|
||||
|
||||
from qutebrowser.extensions import loader
|
||||
|
||||
|
||||
def _add_module_info(func: typing.Callable) -> loader.ModuleInfo:
|
||||
"""Add module info to the given function."""
|
||||
module = importlib.import_module(func.__module__)
|
||||
return loader.add_module_info(module)
|
||||
|
||||
|
||||
class init:
|
||||
|
||||
"""Decorator to mark a function to run when initializing.
|
||||
|
||||
The decorated function gets called with a
|
||||
:class:`qutebrowser.api.apitypes.InitContext` as argument.
|
||||
|
||||
Example::
|
||||
|
||||
@hook.init()
|
||||
def init(_context):
|
||||
message.info("Extension initialized.")
|
||||
"""
|
||||
|
||||
def __call__(self, func: typing.Callable) -> typing.Callable:
|
||||
info = _add_module_info(func)
|
||||
if info.init_hook is not None:
|
||||
raise ValueError("init hook is already registered!")
|
||||
info.init_hook = func
|
||||
return func
|
||||
|
||||
|
||||
class config_changed:
|
||||
|
||||
"""Decorator to get notified about changed configs.
|
||||
|
||||
By default, the decorated function is called when any change in the config
|
||||
occurs::
|
||||
|
||||
@hook.config_changed()
|
||||
def on_config_changed():
|
||||
...
|
||||
|
||||
When an option name is passed, it's only called when the given option was
|
||||
changed::
|
||||
|
||||
@hook.config_changed('content.javascript.enabled')
|
||||
def on_config_changed():
|
||||
...
|
||||
|
||||
Alternatively, a part of an option name can be specified. In the following
|
||||
snippet, ``on_config_changed`` gets called when either
|
||||
``bindings.commands`` or ``bindings.key_mappings`` have changed::
|
||||
|
||||
@hook.config_changed('bindings')
|
||||
def on_config_changed():
|
||||
...
|
||||
"""
|
||||
|
||||
def __init__(self, option_filter: str = None) -> None:
|
||||
self._filter = option_filter
|
||||
|
||||
def __call__(self, func: typing.Callable) -> typing.Callable:
|
||||
info = _add_module_info(func)
|
||||
info.config_changed_hooks.append((self._filter, func))
|
||||
return func
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018-2019 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/>.
|
||||
|
||||
"""APIs related to intercepting/blocking requests."""
|
||||
|
||||
from qutebrowser.extensions import interceptors
|
||||
# pylint: disable=unused-import
|
||||
from qutebrowser.extensions.interceptors import Request
|
||||
|
||||
|
||||
#: Type annotation for an interceptor function.
|
||||
InterceptorType = interceptors.InterceptorType
|
||||
|
||||
#: Possible resource types for requests sent to interceptor.
|
||||
ResourceType = interceptors.ResourceType
|
||||
|
||||
|
||||
def register(interceptor: InterceptorType) -> None:
|
||||
"""Register a request interceptor.
|
||||
|
||||
Whenever a request happens, the interceptor gets called with a
|
||||
:class:`Request` object.
|
||||
|
||||
Example::
|
||||
|
||||
def intercept(request: interceptor.Request) -> None:
|
||||
if request.request_url.host() == 'badhost.example.com':
|
||||
request.block()
|
||||
"""
|
||||
interceptors.register(interceptor)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018-2019 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/>.
|
||||
|
||||
"""Utilities to display messages above the status bar."""
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from qutebrowser.utils.message import error, warning, info
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -60,13 +60,15 @@ except ImportError:
|
|||
import qutebrowser
|
||||
import qutebrowser.resources
|
||||
from qutebrowser.completion.models import miscmodels
|
||||
from qutebrowser.commands import cmdutils, runners, cmdexc
|
||||
from qutebrowser.commands import runners
|
||||
from qutebrowser.api import cmdutils
|
||||
from qutebrowser.config import config, websettings, configfiles, configinit
|
||||
from qutebrowser.browser import (urlmarks, adblock, history, browsertab,
|
||||
from qutebrowser.browser import (urlmarks, history, browsertab,
|
||||
qtnetworkdownloads, downloads, greasemonkey)
|
||||
from qutebrowser.browser.network import proxy
|
||||
from qutebrowser.browser.webkit import cookies, cache
|
||||
from qutebrowser.browser.webkit.network import networkmanager
|
||||
from qutebrowser.extensions import loader
|
||||
from qutebrowser.keyinput import macros
|
||||
from qutebrowser.mainwindow import mainwindow, prompt
|
||||
from qutebrowser.misc import (readline, ipc, savemanager, sessions,
|
||||
|
|
@ -81,7 +83,7 @@ from qutebrowser.misc import utilcmds
|
|||
# pylint: enable=unused-import
|
||||
|
||||
|
||||
qApp = None
|
||||
q_app = None
|
||||
|
||||
|
||||
def run(args):
|
||||
|
|
@ -99,25 +101,25 @@ def run(args):
|
|||
log.init.debug("Initializing config...")
|
||||
configinit.early_init(args)
|
||||
|
||||
global qApp
|
||||
qApp = Application(args)
|
||||
qApp.setOrganizationName("qutebrowser")
|
||||
qApp.setApplicationName("qutebrowser")
|
||||
qApp.setDesktopFileName("qutebrowser")
|
||||
qApp.setApplicationVersion(qutebrowser.__version__)
|
||||
qApp.lastWindowClosed.connect(quitter.on_last_window_closed)
|
||||
global q_app
|
||||
q_app = Application(args)
|
||||
q_app.setOrganizationName("qutebrowser")
|
||||
q_app.setApplicationName("qutebrowser")
|
||||
q_app.setDesktopFileName("org.qutebrowser.qutebrowser")
|
||||
q_app.setApplicationVersion(qutebrowser.__version__)
|
||||
q_app.lastWindowClosed.connect(quitter.on_last_window_closed)
|
||||
|
||||
if args.version:
|
||||
print(version.version())
|
||||
sys.exit(usertypes.Exit.ok)
|
||||
|
||||
crash_handler = crashsignal.CrashHandler(
|
||||
app=qApp, quitter=quitter, args=args, parent=qApp)
|
||||
app=q_app, quitter=quitter, args=args, parent=q_app)
|
||||
crash_handler.activate()
|
||||
objreg.register('crash-handler', crash_handler)
|
||||
|
||||
signal_handler = crashsignal.SignalHandler(app=qApp, quitter=quitter,
|
||||
parent=qApp)
|
||||
signal_handler = crashsignal.SignalHandler(app=q_app, quitter=quitter,
|
||||
parent=q_app)
|
||||
signal_handler.activate()
|
||||
objreg.register('signal-handler', signal_handler)
|
||||
|
||||
|
|
@ -149,7 +151,7 @@ def qt_mainloop():
|
|||
WARNING: misc/crashdialog.py checks the stacktrace for this function
|
||||
name, so if this is changed, it should be changed there as well!
|
||||
"""
|
||||
return qApp.exec_()
|
||||
return q_app.exec_()
|
||||
|
||||
|
||||
def init(args, crash_handler):
|
||||
|
|
@ -160,9 +162,11 @@ def init(args, crash_handler):
|
|||
crash_handler: The CrashHandler instance.
|
||||
"""
|
||||
log.init.debug("Starting init...")
|
||||
qApp.setQuitOnLastWindowClosed(False)
|
||||
q_app.setQuitOnLastWindowClosed(False)
|
||||
_init_icon()
|
||||
|
||||
loader.init()
|
||||
loader.load_components()
|
||||
try:
|
||||
_init_modules(args, crash_handler)
|
||||
except (OSError, UnicodeDecodeError, browsertab.WebTabError) as e:
|
||||
|
|
@ -171,12 +175,12 @@ def init(args, crash_handler):
|
|||
sys.exit(usertypes.Exit.err_init)
|
||||
|
||||
log.init.debug("Initializing eventfilter...")
|
||||
event_filter = EventFilter(qApp)
|
||||
qApp.installEventFilter(event_filter)
|
||||
event_filter = EventFilter(q_app)
|
||||
q_app.installEventFilter(event_filter)
|
||||
objreg.register('event-filter', event_filter)
|
||||
|
||||
log.init.debug("Connecting signals...")
|
||||
qApp.focusChanged.connect(on_focus_changed)
|
||||
q_app.focusChanged.connect(on_focus_changed)
|
||||
|
||||
_process_args(args)
|
||||
|
||||
|
|
@ -193,7 +197,7 @@ def _init_icon():
|
|||
icon = QIcon()
|
||||
fallback_icon = QIcon()
|
||||
for size in [16, 24, 32, 48, 64, 96, 128, 256, 512]:
|
||||
filename = ':/icons/qutebrowser-{}x{}.png'.format(size, size)
|
||||
filename = ':/icons/qutebrowser-{size}x{size}.png'.format(size=size)
|
||||
pixmap = QPixmap(filename)
|
||||
if pixmap.isNull():
|
||||
log.init.warning("Failed to load {}".format(filename))
|
||||
|
|
@ -203,7 +207,7 @@ def _init_icon():
|
|||
if icon.isNull():
|
||||
log.init.warning("Failed to load icon")
|
||||
else:
|
||||
qApp.setWindowIcon(icon)
|
||||
q_app.setWindowIcon(icon)
|
||||
|
||||
|
||||
def _process_args(args):
|
||||
|
|
@ -216,7 +220,7 @@ def _process_args(args):
|
|||
window = mainwindow.MainWindow(private=None)
|
||||
if not args.nowindow:
|
||||
window.show()
|
||||
qApp.setActiveWindow(window)
|
||||
q_app.setActiveWindow(window)
|
||||
|
||||
process_pos_args(args.command)
|
||||
_open_startpage()
|
||||
|
|
@ -421,7 +425,7 @@ def _init_modules(args, crash_handler):
|
|||
crash_handler: The CrashHandler instance.
|
||||
"""
|
||||
log.init.debug("Initializing save manager...")
|
||||
save_manager = savemanager.SaveManager(qApp)
|
||||
save_manager = savemanager.SaveManager(q_app)
|
||||
objreg.register('save-manager', save_manager)
|
||||
configinit.late_init(save_manager)
|
||||
|
||||
|
|
@ -446,7 +450,7 @@ def _init_modules(args, crash_handler):
|
|||
sql.init(os.path.join(standarddir.data(), 'history.sqlite'))
|
||||
|
||||
log.init.debug("Initializing web history...")
|
||||
history.init(qApp)
|
||||
history.init(q_app)
|
||||
except sql.SqlEnvironmentError as e:
|
||||
error.handle_fatal_exc(e, args, 'Error initializing SQL',
|
||||
pre_text='Error initializing SQL')
|
||||
|
|
@ -460,36 +464,31 @@ def _init_modules(args, crash_handler):
|
|||
crash_handler.handle_segfault()
|
||||
|
||||
log.init.debug("Initializing sessions...")
|
||||
sessions.init(qApp)
|
||||
sessions.init(q_app)
|
||||
|
||||
log.init.debug("Initializing websettings...")
|
||||
websettings.init(args)
|
||||
|
||||
log.init.debug("Initializing adblock...")
|
||||
host_blocker = adblock.HostBlocker()
|
||||
host_blocker.read_hosts()
|
||||
objreg.register('host-blocker', host_blocker)
|
||||
|
||||
log.init.debug("Initializing quickmarks...")
|
||||
quickmark_manager = urlmarks.QuickmarkManager(qApp)
|
||||
quickmark_manager = urlmarks.QuickmarkManager(q_app)
|
||||
objreg.register('quickmark-manager', quickmark_manager)
|
||||
|
||||
log.init.debug("Initializing bookmarks...")
|
||||
bookmark_manager = urlmarks.BookmarkManager(qApp)
|
||||
bookmark_manager = urlmarks.BookmarkManager(q_app)
|
||||
objreg.register('bookmark-manager', bookmark_manager)
|
||||
|
||||
log.init.debug("Initializing cookies...")
|
||||
cookie_jar = cookies.CookieJar(qApp)
|
||||
ram_cookie_jar = cookies.RAMCookieJar(qApp)
|
||||
cookie_jar = cookies.CookieJar(q_app)
|
||||
ram_cookie_jar = cookies.RAMCookieJar(q_app)
|
||||
objreg.register('cookie-jar', cookie_jar)
|
||||
objreg.register('ram-cookie-jar', ram_cookie_jar)
|
||||
|
||||
log.init.debug("Initializing cache...")
|
||||
diskcache = cache.DiskCache(standarddir.cache(), parent=qApp)
|
||||
diskcache = cache.DiskCache(standarddir.cache(), parent=q_app)
|
||||
objreg.register('cache', diskcache)
|
||||
|
||||
log.init.debug("Initializing downloads...")
|
||||
download_manager = qtnetworkdownloads.DownloadManager(parent=qApp)
|
||||
download_manager = qtnetworkdownloads.DownloadManager(parent=q_app)
|
||||
objreg.register('qtnetwork-download-manager', download_manager)
|
||||
|
||||
log.init.debug("Initializing Greasemonkey...")
|
||||
|
|
@ -619,10 +618,11 @@ class Quitter:
|
|||
ok = self.restart(session='_restart')
|
||||
except sessions.SessionError as e:
|
||||
log.destroy.exception("Failed to save session!")
|
||||
raise cmdexc.CommandError("Failed to save session: {}!".format(e))
|
||||
raise cmdutils.CommandError("Failed to save session: {}!"
|
||||
.format(e))
|
||||
except SyntaxError as e:
|
||||
log.destroy.exception("Got SyntaxError")
|
||||
raise cmdexc.CommandError("SyntaxError in {}:{}: {}".format(
|
||||
raise cmdutils.CommandError("SyntaxError in {}:{}: {}".format(
|
||||
e.filename, e.lineno, e))
|
||||
if ok:
|
||||
self.shutdown(restart=True)
|
||||
|
|
@ -684,7 +684,7 @@ class Quitter:
|
|||
session: The name of the session to save.
|
||||
"""
|
||||
if session is not None and not save:
|
||||
raise cmdexc.CommandError("Session name given without --save!")
|
||||
raise cmdutils.CommandError("Session name given without --save!")
|
||||
if save:
|
||||
if session is None:
|
||||
session = sessions.default
|
||||
|
|
@ -735,7 +735,7 @@ class Quitter:
|
|||
def _shutdown(self, status, restart): # noqa
|
||||
"""Second stage of shutdown."""
|
||||
log.destroy.debug("Stage 2 of shutting down...")
|
||||
if qApp is None:
|
||||
if q_app is None:
|
||||
# No QApplication exists yet, so quit hard.
|
||||
sys.exit(status)
|
||||
# Remove eventfilter
|
||||
|
|
@ -743,7 +743,7 @@ class Quitter:
|
|||
log.destroy.debug("Removing eventfilter...")
|
||||
event_filter = objreg.get('event-filter', None)
|
||||
if event_filter is not None:
|
||||
qApp.removeEventFilter(event_filter)
|
||||
q_app.removeEventFilter(event_filter)
|
||||
except AttributeError:
|
||||
pass
|
||||
# Close all windows
|
||||
|
|
@ -792,7 +792,7 @@ class Quitter:
|
|||
session_manager.delete_autosave()
|
||||
# We use a singleshot timer to exit here to minimize the likelihood of
|
||||
# segfaults.
|
||||
QTimer.singleShot(0, functools.partial(qApp.exit, status))
|
||||
QTimer.singleShot(0, functools.partial(q_app.exit, status))
|
||||
|
||||
|
||||
class Application(QApplication):
|
||||
|
|
@ -893,7 +893,7 @@ class EventFilter(QObject):
|
|||
Return:
|
||||
True if the event should be filtered, False if it's passed through.
|
||||
"""
|
||||
if qApp.activeWindow() not in objreg.window_registry.values():
|
||||
if q_app.activeWindow() not in objreg.window_registry.values():
|
||||
# Some other window (print dialog, etc.) is focused so we pass the
|
||||
# event through.
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -33,14 +33,18 @@ from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
|
|||
QTimer, QAbstractListModel, QUrl)
|
||||
|
||||
from qutebrowser.browser import pdfjs
|
||||
from qutebrowser.commands import cmdexc, cmdutils
|
||||
from qutebrowser.api import cmdutils
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import (usertypes, standarddir, utils, message, log,
|
||||
qtutils, objreg)
|
||||
from qutebrowser.qt import sip
|
||||
|
||||
|
||||
ModelRole = enum.IntEnum('ModelRole', ['item'], start=Qt.UserRole)
|
||||
class ModelRole(enum.IntEnum):
|
||||
|
||||
"""Custom download model roles."""
|
||||
|
||||
item = Qt.UserRole
|
||||
|
||||
|
||||
# Remember the last used directory
|
||||
|
|
@ -60,8 +64,6 @@ class UnsupportedAttribute:
|
|||
supported with QtWebengine.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UnsupportedOperationError(Exception):
|
||||
|
||||
|
|
@ -1007,11 +1009,11 @@ class DownloadModel(QAbstractListModel):
|
|||
count: The index of the download
|
||||
"""
|
||||
if not count:
|
||||
raise cmdexc.CommandError("There's no download!")
|
||||
raise cmdexc.CommandError("There's no download {}!".format(count))
|
||||
raise cmdutils.CommandError("There's no download!")
|
||||
raise cmdutils.CommandError("There's no download {}!".format(count))
|
||||
|
||||
@cmdutils.register(instance='download-model', scope='window')
|
||||
@cmdutils.argument('count', count=True)
|
||||
@cmdutils.argument('count', value=cmdutils.Value.count)
|
||||
def download_cancel(self, all_=False, count=0):
|
||||
"""Cancel the last/[count]th download.
|
||||
|
||||
|
|
@ -1032,12 +1034,12 @@ class DownloadModel(QAbstractListModel):
|
|||
if download.done:
|
||||
if not count:
|
||||
count = len(self)
|
||||
raise cmdexc.CommandError("Download {} is already done!"
|
||||
.format(count))
|
||||
raise cmdutils.CommandError("Download {} is already done!"
|
||||
.format(count))
|
||||
download.cancel()
|
||||
|
||||
@cmdutils.register(instance='download-model', scope='window')
|
||||
@cmdutils.argument('count', count=True)
|
||||
@cmdutils.argument('count', value=cmdutils.Value.count)
|
||||
def download_delete(self, count=0):
|
||||
"""Delete the last/[count]th download from disk.
|
||||
|
||||
|
|
@ -1051,14 +1053,15 @@ class DownloadModel(QAbstractListModel):
|
|||
if not download.successful:
|
||||
if not count:
|
||||
count = len(self)
|
||||
raise cmdexc.CommandError("Download {} is not done!".format(count))
|
||||
raise cmdutils.CommandError("Download {} is not done!"
|
||||
.format(count))
|
||||
download.delete()
|
||||
download.remove()
|
||||
log.downloads.debug("deleted download {}".format(download))
|
||||
|
||||
@cmdutils.register(instance='download-model', scope='window', maxsplit=0)
|
||||
@cmdutils.argument('count', count=True)
|
||||
def download_open(self, cmdline: str = None, count=0):
|
||||
@cmdutils.argument('count', value=cmdutils.Value.count)
|
||||
def download_open(self, cmdline: str = None, count: int = 0) -> None:
|
||||
"""Open the last/[count]th download.
|
||||
|
||||
If no specific command is given, this will use the system's default
|
||||
|
|
@ -1078,11 +1081,12 @@ class DownloadModel(QAbstractListModel):
|
|||
if not download.successful:
|
||||
if not count:
|
||||
count = len(self)
|
||||
raise cmdexc.CommandError("Download {} is not done!".format(count))
|
||||
raise cmdutils.CommandError("Download {} is not done!"
|
||||
.format(count))
|
||||
download.open_file(cmdline)
|
||||
|
||||
@cmdutils.register(instance='download-model', scope='window')
|
||||
@cmdutils.argument('count', count=True)
|
||||
@cmdutils.argument('count', value=cmdutils.Value.count)
|
||||
def download_retry(self, count=0):
|
||||
"""Retry the first failed/[count]th download.
|
||||
|
||||
|
|
@ -1095,14 +1099,13 @@ class DownloadModel(QAbstractListModel):
|
|||
except IndexError:
|
||||
self._raise_no_download(count)
|
||||
if download.successful or not download.done:
|
||||
raise cmdexc.CommandError("Download {} did not fail!".format(
|
||||
count))
|
||||
raise cmdutils.CommandError("Download {} did not fail!"
|
||||
.format(count))
|
||||
else:
|
||||
to_retry = [d for d in self if d.done and not d.successful]
|
||||
if not to_retry:
|
||||
raise cmdexc.CommandError("No failed downloads!")
|
||||
else:
|
||||
download = to_retry[0]
|
||||
raise cmdutils.CommandError("No failed downloads!")
|
||||
download = to_retry[0]
|
||||
download.try_retry()
|
||||
|
||||
def can_clear(self):
|
||||
|
|
@ -1117,7 +1120,7 @@ class DownloadModel(QAbstractListModel):
|
|||
download.remove()
|
||||
|
||||
@cmdutils.register(instance='download-model', scope='window')
|
||||
@cmdutils.argument('count', count=True)
|
||||
@cmdutils.argument('count', value=cmdutils.Value.count)
|
||||
def download_remove(self, all_=False, count=0):
|
||||
"""Remove the last/[count]th download from the list.
|
||||
|
||||
|
|
@ -1135,8 +1138,8 @@ class DownloadModel(QAbstractListModel):
|
|||
if not download.done:
|
||||
if not count:
|
||||
count = len(self)
|
||||
raise cmdexc.CommandError("Download {} is not done!"
|
||||
.format(count))
|
||||
raise cmdutils.CommandError("Download {} is not done!"
|
||||
.format(count))
|
||||
download.remove()
|
||||
|
||||
def running_downloads(self):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -75,7 +75,8 @@ class DownloadView(QListView):
|
|||
|
||||
def __init__(self, win_id, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setStyle(QStyleFactory.create('Fusion'))
|
||||
if not utils.is_mac:
|
||||
self.setStyle(QStyleFactory.create('Fusion'))
|
||||
config.set_register_stylesheet(self)
|
||||
self.setResizeMode(QListView.Adjust)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2017-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2017-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -31,8 +31,9 @@ import attr
|
|||
from PyQt5.QtCore import pyqtSignal, QObject, QUrl
|
||||
|
||||
from qutebrowser.utils import (log, standarddir, jinja, objreg, utils,
|
||||
javascript, urlmatch, version, usertypes)
|
||||
from qutebrowser.commands import cmdutils
|
||||
javascript, urlmatch, version, usertypes,
|
||||
qtutils)
|
||||
from qutebrowser.api import cmdutils
|
||||
from qutebrowser.browser import downloads
|
||||
from qutebrowser.misc import objects
|
||||
|
||||
|
|
@ -116,6 +117,40 @@ class GreasemonkeyScript:
|
|||
script.includes = ['*']
|
||||
return script
|
||||
|
||||
def needs_document_end_workaround(self):
|
||||
"""Check whether to force @run-at document-end.
|
||||
|
||||
This needs to be done on QtWebEngine with Qt 5.12 for known-broken
|
||||
scripts.
|
||||
|
||||
On Qt 5.12, accessing the DOM isn't possible with "@run-at
|
||||
document-start". It was documented to be impossible before, but seems
|
||||
to work fine.
|
||||
|
||||
However, some scripts do DOM access with "@run-at document-start". Fix
|
||||
those by forcing them to use document-end instead.
|
||||
"""
|
||||
if objects.backend != usertypes.Backend.QtWebEngine:
|
||||
return False
|
||||
elif not qtutils.version_check('5.12', compiled=False):
|
||||
return False
|
||||
|
||||
broken_scripts = [
|
||||
('http://userstyles.org', None),
|
||||
('https://github.com/ParticleCore', 'Iridium'),
|
||||
]
|
||||
return any(self._matches_id(namespace=namespace, name=name)
|
||||
for namespace, name in broken_scripts)
|
||||
|
||||
def _matches_id(self, *, namespace, name):
|
||||
"""Check if this script matches the given namespace/name.
|
||||
|
||||
Both namespace and name can be None in order to match any script.
|
||||
"""
|
||||
matches_namespace = namespace is None or self.namespace == namespace
|
||||
matches_name = name is None or self.name == name
|
||||
return matches_namespace and matches_name
|
||||
|
||||
def code(self):
|
||||
"""Return the processed JavaScript code of this script.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -34,7 +34,8 @@ from PyQt5.QtWidgets import QLabel
|
|||
from qutebrowser.config import config, configexc
|
||||
from qutebrowser.keyinput import modeman, modeparsers
|
||||
from qutebrowser.browser import webelem
|
||||
from qutebrowser.commands import userscripts, cmdexc, cmdutils, runners
|
||||
from qutebrowser.commands import userscripts, runners
|
||||
from qutebrowser.api import cmdutils
|
||||
from qutebrowser.utils import usertypes, log, qtutils, message, objreg, utils
|
||||
|
||||
|
||||
|
|
@ -217,9 +218,7 @@ class HintActions:
|
|||
|
||||
if context.target in [Target.normal, Target.current]:
|
||||
# Set the pre-jump mark ', so we can jump back here after following
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=self._win_id)
|
||||
tabbed_browser.set_mark("'")
|
||||
context.tab.scroller.before_jump_requested.emit()
|
||||
|
||||
try:
|
||||
if context.target == Target.hover:
|
||||
|
|
@ -304,8 +303,8 @@ class HintActions:
|
|||
raise HintingError("No suitable link found for this element.")
|
||||
|
||||
prompt = False if context.rapid else None
|
||||
qnam = context.tab.networkaccessmanager()
|
||||
user_agent = context.tab.user_agent()
|
||||
qnam = context.tab.private_api.networkaccessmanager()
|
||||
user_agent = context.tab.private_api.user_agent()
|
||||
|
||||
# FIXME:qtwebengine do this with QtWebEngine downloads?
|
||||
download_manager = objreg.get('qtnetwork-download-manager')
|
||||
|
|
@ -563,12 +562,12 @@ class HintManager(QObject):
|
|||
if target in [Target.userscript, Target.spawn, Target.run,
|
||||
Target.fill]:
|
||||
if not args:
|
||||
raise cmdexc.CommandError(
|
||||
raise cmdutils.CommandError(
|
||||
"'args' is required with target userscript/spawn/run/"
|
||||
"fill.")
|
||||
else:
|
||||
if args:
|
||||
raise cmdexc.CommandError(
|
||||
raise cmdutils.CommandError(
|
||||
"'args' is only allowed with target userscript/spawn.")
|
||||
|
||||
def _filter_matches(self, filterstr, elemstr):
|
||||
|
|
@ -596,13 +595,6 @@ class HintManager(QObject):
|
|||
log.hints.debug("In _start_cb without context!")
|
||||
return
|
||||
|
||||
if elems is None:
|
||||
message.error("Unknown error while getting hint elements.")
|
||||
return
|
||||
elif isinstance(elems, webelem.Error):
|
||||
message.error(str(elems))
|
||||
return
|
||||
|
||||
if not elems:
|
||||
message.error("No elements found.")
|
||||
return
|
||||
|
|
@ -705,7 +697,7 @@ class HintManager(QObject):
|
|||
window=self._win_id)
|
||||
tab = tabbed_browser.widget.currentWidget()
|
||||
if tab is None:
|
||||
raise cmdexc.CommandError("No WebView available yet!")
|
||||
raise cmdutils.CommandError("No WebView available yet!")
|
||||
|
||||
mode_manager = objreg.get('mode-manager', scope='window',
|
||||
window=self._win_id)
|
||||
|
|
@ -722,8 +714,8 @@ class HintManager(QObject):
|
|||
pass
|
||||
else:
|
||||
name = target.name.replace('_', '-')
|
||||
raise cmdexc.CommandError("Rapid hinting makes no sense with "
|
||||
"target {}!".format(name))
|
||||
raise cmdutils.CommandError("Rapid hinting makes no sense "
|
||||
"with target {}!".format(name))
|
||||
|
||||
self._check_args(target, *args)
|
||||
self._context = HintContext()
|
||||
|
|
@ -736,18 +728,21 @@ class HintManager(QObject):
|
|||
try:
|
||||
self._context.baseurl = tabbed_browser.current_url()
|
||||
except qtutils.QtValueError:
|
||||
raise cmdexc.CommandError("No URL set for this page yet!")
|
||||
self._context.args = args
|
||||
raise cmdutils.CommandError("No URL set for this page yet!")
|
||||
self._context.args = list(args)
|
||||
self._context.group = group
|
||||
|
||||
try:
|
||||
selector = webelem.css_selector(self._context.group,
|
||||
self._context.baseurl)
|
||||
except webelem.Error as e:
|
||||
raise cmdexc.CommandError(str(e))
|
||||
raise cmdutils.CommandError(str(e))
|
||||
|
||||
self._context.tab.elements.find_css(selector, self._start_cb,
|
||||
only_visible=True)
|
||||
self._context.tab.elements.find_css(
|
||||
selector,
|
||||
callback=self._start_cb,
|
||||
error_cb=lambda err: message.error(str(err)),
|
||||
only_visible=True)
|
||||
|
||||
def _get_hint_mode(self, mode):
|
||||
"""Get the hinting mode to use based on a mode argument."""
|
||||
|
|
@ -758,7 +753,7 @@ class HintManager(QObject):
|
|||
try:
|
||||
opt.typ.to_py(mode)
|
||||
except configexc.ValidationError as e:
|
||||
raise cmdexc.CommandError("Invalid mode: {}".format(e))
|
||||
raise cmdutils.CommandError("Invalid mode: {}".format(e))
|
||||
return mode
|
||||
|
||||
def current_mode(self):
|
||||
|
|
@ -960,13 +955,12 @@ class HintManager(QObject):
|
|||
"""
|
||||
if keystring is None:
|
||||
if self._context.to_follow is None:
|
||||
raise cmdexc.CommandError("No hint to follow")
|
||||
elif select:
|
||||
raise cmdexc.CommandError("Can't use --select without hint.")
|
||||
else:
|
||||
keystring = self._context.to_follow
|
||||
raise cmdutils.CommandError("No hint to follow")
|
||||
if select:
|
||||
raise cmdutils.CommandError("Can't use --select without hint.")
|
||||
keystring = self._context.to_follow
|
||||
elif keystring not in self._context.labels:
|
||||
raise cmdexc.CommandError("No hint {}!".format(keystring))
|
||||
raise cmdutils.CommandError("No hint {}!".format(keystring))
|
||||
|
||||
if select:
|
||||
self.handle_partial_key(keystring)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -27,7 +27,7 @@ from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal
|
|||
from PyQt5.QtWidgets import QProgressDialog, QApplication
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.commands import cmdutils, cmdexc
|
||||
from qutebrowser.api import cmdutils
|
||||
from qutebrowser.utils import utils, objreg, log, usertypes, message, qtutils
|
||||
from qutebrowser.misc import objects, sql
|
||||
|
||||
|
|
@ -365,7 +365,8 @@ class WebHistory(sql.SqlTable):
|
|||
f.write('\n'.join(lines))
|
||||
message.info("Dumped history to {}".format(dest))
|
||||
except OSError as e:
|
||||
raise cmdexc.CommandError('Could not write history: {}'.format(e))
|
||||
raise cmdutils.CommandError('Could not write history: {}'
|
||||
.format(e))
|
||||
|
||||
|
||||
def init(parent=None):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -49,8 +49,6 @@ class WebInspectorError(Exception):
|
|||
|
||||
"""Raised when the inspector could not be initialized."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AbstractWebInspector(QWidget):
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -240,7 +240,7 @@ class MouseEventFilter(QObject):
|
|||
evtype = event.type()
|
||||
if evtype not in self._handlers:
|
||||
return False
|
||||
if obj is not self._tab.event_target():
|
||||
if obj is not self._tab.private_api.event_target():
|
||||
log.mouse.debug("Ignoring {} to {}".format(
|
||||
event.__class__.__name__, obj))
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -116,13 +116,6 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
|
|||
window: True to open in a new window, False for the current one.
|
||||
"""
|
||||
def _prevnext_cb(elems):
|
||||
if elems is None:
|
||||
message.error("Unknown error while getting hint elements")
|
||||
return
|
||||
elif isinstance(elems, webelem.Error):
|
||||
message.error(str(elems))
|
||||
return
|
||||
|
||||
elem = _find_prevnext(prev, elems)
|
||||
word = 'prev' if prev else 'forward'
|
||||
|
||||
|
|
@ -140,7 +133,7 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
|
|||
|
||||
if window:
|
||||
new_window = mainwindow.MainWindow(
|
||||
private=cur_tabbed_browser.private)
|
||||
private=cur_tabbed_browser.is_private)
|
||||
new_window.show()
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=new_window.win_id)
|
||||
|
|
@ -148,11 +141,12 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
|
|||
elif tab:
|
||||
cur_tabbed_browser.tabopen(url, background=background)
|
||||
else:
|
||||
browsertab.openurl(url)
|
||||
browsertab.load_url(url)
|
||||
|
||||
try:
|
||||
link_selector = webelem.css_selector('links', baseurl)
|
||||
except webelem.Error as e:
|
||||
raise Error(str(e))
|
||||
|
||||
browsertab.elements.find_css(link_selector, _prevnext_cb)
|
||||
browsertab.elements.find_css(link_selector, callback=_prevnext_cb,
|
||||
error_cb=lambda err: message.error(str(err)))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -35,15 +35,11 @@ class ParseProxyError(Exception):
|
|||
|
||||
"""Error while parsing PAC result string."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class EvalProxyError(Exception):
|
||||
|
||||
"""Error while evaluating PAC script."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def _js_slot(*args):
|
||||
"""Wrap a methods as a JavaScript function.
|
||||
|
|
@ -146,7 +142,8 @@ class PACResolver:
|
|||
config = [c.strip() for c in proxy_str.split(' ') if c]
|
||||
if not config:
|
||||
raise ParseProxyError("Empty proxy entry")
|
||||
elif config[0] == "DIRECT":
|
||||
|
||||
if config[0] == "DIRECT":
|
||||
if len(config) != 1:
|
||||
raise ParseProxyError("Invalid number of parameters for " +
|
||||
"DIRECT")
|
||||
|
|
@ -184,6 +181,8 @@ class PACResolver:
|
|||
"""
|
||||
self._engine = QJSEngine()
|
||||
|
||||
self._engine.installExtensions(QJSEngine.ConsoleExtension)
|
||||
|
||||
self._ctx = _PACContext(self._engine)
|
||||
self._engine.globalObject().setProperty(
|
||||
"PAC", self._engine.newQObject(self._ctx))
|
||||
|
|
@ -210,6 +209,8 @@ class PACResolver:
|
|||
Return:
|
||||
A list of QNetworkProxy objects in order of preference.
|
||||
"""
|
||||
qtutils.ensure_valid(query.url())
|
||||
|
||||
if from_file:
|
||||
string_flags = QUrl.PrettyDecoded
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -20,10 +20,12 @@
|
|||
"""Handling of proxies."""
|
||||
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtNetwork import QNetworkProxy, QNetworkProxyFactory
|
||||
|
||||
from qutebrowser.config import config, configtypes
|
||||
from qutebrowser.utils import objreg
|
||||
from qutebrowser.utils import objreg, message, usertypes, urlutils
|
||||
from qutebrowser.misc import objects
|
||||
from qutebrowser.browser.network import pac
|
||||
|
||||
|
||||
|
|
@ -33,6 +35,18 @@ def init():
|
|||
objreg.register('proxy-factory', proxy_factory)
|
||||
QNetworkProxyFactory.setApplicationProxyFactory(proxy_factory)
|
||||
|
||||
config.instance.changed.connect(_warn_for_pac)
|
||||
_warn_for_pac()
|
||||
|
||||
|
||||
@config.change_filter('content.proxy', function=True)
|
||||
def _warn_for_pac():
|
||||
"""Show a warning if PAC is used with QtWebEngine."""
|
||||
proxy = config.val.content.proxy
|
||||
if (isinstance(proxy, pac.PACFetcher) and
|
||||
objects.backend == usertypes.Backend.QtWebEngine):
|
||||
message.error("PAC support isn't implemented for QtWebEngine yet!")
|
||||
|
||||
|
||||
def shutdown():
|
||||
QNetworkProxyFactory.setApplicationProxyFactory(None)
|
||||
|
|
@ -70,7 +84,11 @@ class ProxyFactory(QNetworkProxyFactory):
|
|||
# ref. http://doc.qt.io/qt-5/qnetworkproxyfactory.html#systemProxyForQuery
|
||||
proxies = QNetworkProxyFactory.systemProxyForQuery(query)
|
||||
elif isinstance(proxy, pac.PACFetcher):
|
||||
proxies = proxy.resolve(query)
|
||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||
# Looks like query.url() is always invalid on QtWebEngine...
|
||||
proxies = [urlutils.proxy_from_url(QUrl('direct://'))]
|
||||
else:
|
||||
proxies = proxy.resolve(query)
|
||||
else:
|
||||
proxies = [proxy]
|
||||
for p in proxies:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015 Daniel Schadt
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -37,7 +37,7 @@ try:
|
|||
import secrets
|
||||
except ImportError:
|
||||
# New in Python 3.6
|
||||
secrets = None
|
||||
secrets = None # type: ignore
|
||||
|
||||
from PyQt5.QtCore import QUrlQuery, QUrl, qVersion
|
||||
|
||||
|
|
@ -61,36 +61,26 @@ class Error(Exception):
|
|||
|
||||
"""Exception for generic errors on a qute:// page."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class NotFoundError(Error):
|
||||
|
||||
"""Raised when the given URL was not found."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SchemeOSError(Error):
|
||||
|
||||
"""Raised when there was an OSError inside a handler."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UrlInvalidError(Error):
|
||||
|
||||
"""Raised when an invalid URL was opened."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class RequestDeniedError(Error):
|
||||
|
||||
"""Raised when the request is forbidden."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Redirect(Exception):
|
||||
|
||||
|
|
@ -350,19 +340,11 @@ def qute_gpl(_url):
|
|||
|
||||
def _asciidoc_fallback_path(html_path):
|
||||
"""Fall back to plaintext asciidoc if the HTML is unavailable."""
|
||||
asciidoc_path = html_path.replace('.html', '.asciidoc')
|
||||
asciidoc_paths = [asciidoc_path]
|
||||
if asciidoc_path.startswith('html/doc/'):
|
||||
asciidoc_paths += [asciidoc_path.replace('html/doc/', '../doc/help/'),
|
||||
asciidoc_path.replace('html/doc/', '../doc/')]
|
||||
|
||||
for path in asciidoc_paths:
|
||||
try:
|
||||
return utils.read_file(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return None
|
||||
path = html_path.replace('.html', '.asciidoc')
|
||||
try:
|
||||
return utils.read_file(path)
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
@add_handler('help')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -42,7 +42,6 @@ def custom_headers(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.instance.get('content.headers.custom', url=url)
|
||||
for header, value in conf_headers.items():
|
||||
|
|
@ -262,7 +261,7 @@ def get_tab(win_id, target):
|
|||
elif target == usertypes.ClickTarget.window:
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=win_id)
|
||||
window = mainwindow.MainWindow(private=tabbed_browser.private)
|
||||
window = mainwindow.MainWindow(private=tabbed_browser.is_private)
|
||||
window.show()
|
||||
win_id = window.win_id
|
||||
bg_tab = False
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015-2018 Antoni Boucher <bouanto@zoho.com>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
|
|
@ -35,7 +35,7 @@ from PyQt5.QtCore import pyqtSignal, QUrl, QObject
|
|||
|
||||
from qutebrowser.utils import (message, usertypes, qtutils, urlutils,
|
||||
standarddir, objreg, log)
|
||||
from qutebrowser.commands import cmdutils
|
||||
from qutebrowser.api import cmdutils
|
||||
from qutebrowser.misc import lineparser
|
||||
|
||||
|
||||
|
|
@ -43,29 +43,21 @@ class Error(Exception):
|
|||
|
||||
"""Base class for all errors in this module."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InvalidUrlError(Error):
|
||||
|
||||
"""Exception emitted when a URL is invalid."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class DoesNotExistError(Error):
|
||||
|
||||
"""Exception emitted when a given URL does not exist."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AlreadyExistsError(Error):
|
||||
|
||||
"""Exception emitted when a given URL does already exist."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UrlMarkManager(QObject):
|
||||
|
||||
|
|
@ -174,7 +166,7 @@ class QuickmarkManager(UrlMarkManager):
|
|||
url: The url to add as quickmark.
|
||||
name: The name for the new quickmark.
|
||||
"""
|
||||
# We don't raise cmdexc.CommandError here as this can be called async
|
||||
# We don't raise cmdutils.CommandError here as this can be called async
|
||||
# via prompt_save.
|
||||
if not name:
|
||||
message.error("Can't set mark with empty name!")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -19,32 +19,36 @@
|
|||
|
||||
"""Generic web element related code."""
|
||||
|
||||
import typing
|
||||
import collections.abc
|
||||
|
||||
from PyQt5.QtCore import QUrl, Qt, QEvent, QTimer
|
||||
from PyQt5.QtCore import QUrl, Qt, QEvent, QTimer, QRect, QPoint
|
||||
from PyQt5.QtGui import QMouseEvent
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
from qutebrowser.utils import log, usertypes, utils, qtutils, objreg
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
# pylint: disable=unused-import,useless-suppression
|
||||
from qutebrowser.browser import browsertab
|
||||
|
||||
|
||||
JsValueType = typing.Union[int, float, str, None]
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
|
||||
"""Base class for WebElement errors."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OrphanedError(Error):
|
||||
|
||||
"""Raised when a webelement's parent has vanished."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def css_selector(group, url):
|
||||
def css_selector(group: str, url: QUrl) -> str:
|
||||
"""Get a CSS selector for the given group/URL."""
|
||||
selectors = config.instance.get('hints.selectors', url)
|
||||
if group not in selectors:
|
||||
|
|
@ -58,76 +62,74 @@ def css_selector(group, url):
|
|||
|
||||
class AbstractWebElement(collections.abc.MutableMapping):
|
||||
|
||||
"""A wrapper around QtWebKit/QtWebEngine web element.
|
||||
"""A wrapper around QtWebKit/QtWebEngine web element."""
|
||||
|
||||
Attributes:
|
||||
tab: The tab associated with this element.
|
||||
"""
|
||||
|
||||
def __init__(self, tab):
|
||||
def __init__(self, tab: 'browsertab.AbstractTab') -> None:
|
||||
self._tab = tab
|
||||
|
||||
def __eq__(self, other):
|
||||
def __eq__(self, other: object) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def __getitem__(self, key):
|
||||
def __getitem__(self, key: str) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
def __setitem__(self, key: str, val: str) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def __delitem__(self, key):
|
||||
def __delitem__(self, key: str) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> typing.Iterator[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
try:
|
||||
html = utils.compact_text(self.outer_xml(), 500)
|
||||
except Error:
|
||||
html = None
|
||||
return utils.get_repr(self, html=html)
|
||||
|
||||
def has_frame(self):
|
||||
def has_frame(self) -> bool:
|
||||
"""Check if this element has a valid frame attached."""
|
||||
raise NotImplementedError
|
||||
|
||||
def geometry(self):
|
||||
def geometry(self) -> QRect:
|
||||
"""Get the geometry for this element."""
|
||||
raise NotImplementedError
|
||||
|
||||
def classes(self):
|
||||
def classes(self) -> typing.List[str]:
|
||||
"""Get a list of classes assigned to this element."""
|
||||
raise NotImplementedError
|
||||
|
||||
def tag_name(self):
|
||||
def tag_name(self) -> str:
|
||||
"""Get the tag name of this element.
|
||||
|
||||
The returned name will always be lower-case.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def outer_xml(self):
|
||||
def outer_xml(self) -> str:
|
||||
"""Get the full HTML representation of this element."""
|
||||
raise NotImplementedError
|
||||
|
||||
def value(self):
|
||||
def value(self) -> JsValueType:
|
||||
"""Get the value attribute for this element, or None."""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_value(self, value):
|
||||
def set_value(self, value: JsValueType) -> None:
|
||||
"""Set the element value."""
|
||||
raise NotImplementedError
|
||||
|
||||
def dispatch_event(self, event, bubbles=False,
|
||||
cancelable=False, composed=False):
|
||||
def dispatch_event(self, event: str,
|
||||
bubbles: bool = False,
|
||||
cancelable: bool = False,
|
||||
composed: bool = False) -> None:
|
||||
"""Dispatch an event to the element.
|
||||
|
||||
Args:
|
||||
|
|
@ -138,35 +140,25 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def insert_text(self, text):
|
||||
def insert_text(self, text: str) -> None:
|
||||
"""Insert the given text into the element."""
|
||||
raise NotImplementedError
|
||||
|
||||
def rect_on_view(self, *, elem_geometry=None, no_js=False):
|
||||
def rect_on_view(self, *, elem_geometry: QRect = None,
|
||||
no_js: bool = False) -> QRect:
|
||||
"""Get the geometry of the element relative to the webview.
|
||||
|
||||
Uses the getClientRects() JavaScript method to obtain the collection of
|
||||
rectangles containing the element and returns the first rectangle which
|
||||
is large enough (larger than 1px times 1px). If all rectangles returned
|
||||
by getClientRects() are too small, falls back to elem.rect_on_view().
|
||||
|
||||
Skipping of small rectangles is due to <a> elements containing other
|
||||
elements with "display:block" style, see
|
||||
https://github.com/qutebrowser/qutebrowser/issues/1298
|
||||
|
||||
Args:
|
||||
elem_geometry: The geometry of the element, or None.
|
||||
Calling QWebElement::geometry is rather expensive so
|
||||
we want to avoid doing it twice.
|
||||
no_js: Fall back to the Python implementation
|
||||
no_js: Fall back to the Python implementation.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_writable(self):
|
||||
def is_writable(self) -> bool:
|
||||
"""Check whether an element is writable."""
|
||||
return not ('disabled' in self or 'readonly' in self)
|
||||
|
||||
def is_content_editable(self):
|
||||
def is_content_editable(self) -> bool:
|
||||
"""Check if an element has a contenteditable attribute.
|
||||
|
||||
Args:
|
||||
|
|
@ -181,7 +173,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
except KeyError:
|
||||
return False
|
||||
|
||||
def _is_editable_object(self):
|
||||
def _is_editable_object(self) -> bool:
|
||||
"""Check if an object-element is editable."""
|
||||
if 'type' not in self:
|
||||
log.webelem.debug("<object> without type clicked...")
|
||||
|
|
@ -197,7 +189,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
# Image/Audio/...
|
||||
return False
|
||||
|
||||
def _is_editable_input(self):
|
||||
def _is_editable_input(self) -> bool:
|
||||
"""Check if an input-element is editable.
|
||||
|
||||
Return:
|
||||
|
|
@ -214,7 +206,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
else:
|
||||
return False
|
||||
|
||||
def _is_editable_classes(self):
|
||||
def _is_editable_classes(self) -> bool:
|
||||
"""Check if an element is editable based on its classes.
|
||||
|
||||
Return:
|
||||
|
|
@ -233,7 +225,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
return True
|
||||
return False
|
||||
|
||||
def is_editable(self, strict=False):
|
||||
def is_editable(self, strict: bool = False) -> bool:
|
||||
"""Check whether we should switch to insert mode for this element.
|
||||
|
||||
Args:
|
||||
|
|
@ -264,17 +256,17 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
return self._is_editable_classes() and not strict
|
||||
return False
|
||||
|
||||
def is_text_input(self):
|
||||
def is_text_input(self) -> bool:
|
||||
"""Check if this element is some kind of text box."""
|
||||
roles = ('combobox', 'textbox')
|
||||
tag = self.tag_name()
|
||||
return self.get('role', None) in roles or tag in ['input', 'textarea']
|
||||
|
||||
def remove_blank_target(self):
|
||||
def remove_blank_target(self) -> None:
|
||||
"""Remove target from link."""
|
||||
raise NotImplementedError
|
||||
|
||||
def resolve_url(self, baseurl):
|
||||
def resolve_url(self, baseurl: QUrl) -> typing.Optional[QUrl]:
|
||||
"""Resolve the URL in the element's src/href attribute.
|
||||
|
||||
Args:
|
||||
|
|
@ -301,16 +293,16 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
qtutils.ensure_valid(url)
|
||||
return url
|
||||
|
||||
def is_link(self):
|
||||
def is_link(self) -> bool:
|
||||
"""Return True if this AbstractWebElement is a link."""
|
||||
href_tags = ['a', 'area', 'link']
|
||||
return self.tag_name() in href_tags and 'href' in self
|
||||
|
||||
def _requires_user_interaction(self):
|
||||
def _requires_user_interaction(self) -> bool:
|
||||
"""Return True if clicking this element needs user interaction."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _mouse_pos(self):
|
||||
def _mouse_pos(self) -> QPoint:
|
||||
"""Get the position to click/hover."""
|
||||
# Click the center of the largest square fitting into the top/left
|
||||
# corner of the rectangle, this will help if part of the <a> element
|
||||
|
|
@ -326,35 +318,38 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
raise Error("Element position is out of view!")
|
||||
return pos
|
||||
|
||||
def _move_text_cursor(self):
|
||||
def _move_text_cursor(self) -> None:
|
||||
"""Move cursor to end after clicking."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _click_fake_event(self, click_target):
|
||||
def _click_fake_event(self, click_target: usertypes.ClickTarget) -> None:
|
||||
"""Send a fake click event to the element."""
|
||||
pos = self._mouse_pos()
|
||||
|
||||
log.webelem.debug("Sending fake click to {!r} at position {} with "
|
||||
"target {}".format(self, pos, click_target))
|
||||
|
||||
modifiers = {
|
||||
target_modifiers = {
|
||||
usertypes.ClickTarget.normal: Qt.NoModifier,
|
||||
usertypes.ClickTarget.window: Qt.AltModifier | Qt.ShiftModifier,
|
||||
usertypes.ClickTarget.tab: Qt.ControlModifier,
|
||||
usertypes.ClickTarget.tab_bg: Qt.ControlModifier,
|
||||
}
|
||||
if config.val.tabs.background:
|
||||
modifiers[usertypes.ClickTarget.tab] |= Qt.ShiftModifier
|
||||
target_modifiers[usertypes.ClickTarget.tab] |= Qt.ShiftModifier
|
||||
else:
|
||||
modifiers[usertypes.ClickTarget.tab_bg] |= Qt.ShiftModifier
|
||||
target_modifiers[usertypes.ClickTarget.tab_bg] |= Qt.ShiftModifier
|
||||
|
||||
modifiers = typing.cast(Qt.KeyboardModifiers,
|
||||
target_modifiers[click_target])
|
||||
|
||||
events = [
|
||||
QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton,
|
||||
Qt.NoModifier),
|
||||
QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton,
|
||||
Qt.LeftButton, modifiers[click_target]),
|
||||
Qt.LeftButton, modifiers),
|
||||
QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
|
||||
Qt.NoButton, modifiers[click_target]),
|
||||
Qt.NoButton, modifiers),
|
||||
]
|
||||
|
||||
for evt in events:
|
||||
|
|
@ -362,15 +357,15 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
|
||||
QTimer.singleShot(0, self._move_text_cursor)
|
||||
|
||||
def _click_editable(self, click_target):
|
||||
def _click_editable(self, click_target: usertypes.ClickTarget) -> None:
|
||||
"""Fake a click on an editable input field."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _click_js(self, click_target):
|
||||
def _click_js(self, click_target: usertypes.ClickTarget) -> None:
|
||||
"""Fake a click by using the JS .click() method."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _click_href(self, click_target):
|
||||
def _click_href(self, click_target: usertypes.ClickTarget) -> None:
|
||||
"""Fake a click on an element with a href by opening the link."""
|
||||
baseurl = self._tab.url()
|
||||
url = self.resolve_url(baseurl)
|
||||
|
|
@ -386,13 +381,14 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
background = click_target == usertypes.ClickTarget.tab_bg
|
||||
tabbed_browser.tabopen(url, background=background)
|
||||
elif click_target == usertypes.ClickTarget.window:
|
||||
window = mainwindow.MainWindow(private=tabbed_browser.private)
|
||||
window = mainwindow.MainWindow(private=tabbed_browser.is_private)
|
||||
window.show()
|
||||
window.tabbed_browser.tabopen(url)
|
||||
else:
|
||||
raise ValueError("Unknown ClickTarget {}".format(click_target))
|
||||
|
||||
def click(self, click_target, *, force_event=False):
|
||||
def click(self, click_target: usertypes.ClickTarget, *,
|
||||
force_event: bool = False) -> None:
|
||||
"""Simulate a click on the element.
|
||||
|
||||
Args:
|
||||
|
|
@ -429,7 +425,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
|
|||
else:
|
||||
raise ValueError("Unknown ClickTarget {}".format(click_target))
|
||||
|
||||
def hover(self):
|
||||
def hover(self) -> None:
|
||||
"""Simulate a mouse hover over the element."""
|
||||
pos = self._mouse_pos()
|
||||
event = QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2018-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -26,15 +26,59 @@ from PyQt5.QtWebEngineCore import (QWebEngineUrlRequestInterceptor,
|
|||
from qutebrowser.config import config
|
||||
from qutebrowser.browser import shared
|
||||
from qutebrowser.utils import utils, log, debug
|
||||
from qutebrowser.extensions import interceptors
|
||||
|
||||
|
||||
class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
||||
|
||||
"""Handle ad blocking and custom headers."""
|
||||
|
||||
def __init__(self, host_blocker, args, parent=None):
|
||||
# This dict should be from QWebEngine Resource Types to qutebrowser
|
||||
# extension ResourceTypes. If a ResourceType is added to Qt, this table
|
||||
# should be updated too.
|
||||
RESOURCE_TYPES = {
|
||||
QWebEngineUrlRequestInfo.ResourceTypeMainFrame:
|
||||
interceptors.ResourceType.main_frame,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeSubFrame:
|
||||
interceptors.ResourceType.sub_frame,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeStylesheet:
|
||||
interceptors.ResourceType.stylesheet,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeScript:
|
||||
interceptors.ResourceType.script,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeImage:
|
||||
interceptors.ResourceType.image,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeFontResource:
|
||||
interceptors.ResourceType.font_resource,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeSubResource:
|
||||
interceptors.ResourceType.sub_resource,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeObject:
|
||||
interceptors.ResourceType.object,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeMedia:
|
||||
interceptors.ResourceType.media,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeWorker:
|
||||
interceptors.ResourceType.worker,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeSharedWorker:
|
||||
interceptors.ResourceType.shared_worker,
|
||||
QWebEngineUrlRequestInfo.ResourceTypePrefetch:
|
||||
interceptors.ResourceType.prefetch,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeFavicon:
|
||||
interceptors.ResourceType.favicon,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeXhr:
|
||||
interceptors.ResourceType.xhr,
|
||||
QWebEngineUrlRequestInfo.ResourceTypePing:
|
||||
interceptors.ResourceType.ping,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeServiceWorker:
|
||||
interceptors.ResourceType.service_worker,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeCspReport:
|
||||
interceptors.ResourceType.csp_report,
|
||||
QWebEngineUrlRequestInfo.ResourceTypePluginResource:
|
||||
interceptors.ResourceType.plugin_resource,
|
||||
QWebEngineUrlRequestInfo.ResourceTypeUnknown:
|
||||
interceptors.ResourceType.unknown,
|
||||
}
|
||||
|
||||
def __init__(self, args, parent=None):
|
||||
super().__init__(parent)
|
||||
self._host_blocker = host_blocker
|
||||
self._args = args
|
||||
|
||||
def install(self, profile):
|
||||
|
|
@ -71,6 +115,17 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
|||
|
||||
url = info.requestUrl()
|
||||
first_party = info.firstPartyUrl()
|
||||
# Per QWebEngineUrlRequestInfo::ResourceType documentation, if we fail
|
||||
# our lookup, we should fall back to ResourceTypeUnknown
|
||||
try:
|
||||
resource_type = RequestInterceptor.RESOURCE_TYPES[
|
||||
info.resourceType()]
|
||||
except KeyError:
|
||||
log.webview.warning(
|
||||
"Resource type {} not found in RequestInterceptor dict."
|
||||
.format(debug.qenum_key(QWebEngineUrlRequestInfo,
|
||||
info.resourceType())))
|
||||
resource_type = interceptors.ResourceType.unknown
|
||||
|
||||
if ((url.scheme(), url.host(), url.path()) ==
|
||||
('qute', 'settings', '/set')):
|
||||
|
|
@ -84,9 +139,11 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
|||
return
|
||||
|
||||
# FIXME:qtwebengine only block ads for NavigationTypeOther?
|
||||
if self._host_blocker.is_blocked(url, first_party):
|
||||
log.webview.info("Request to {} blocked by host blocker.".format(
|
||||
url.host()))
|
||||
request = interceptors.Request(first_party_url=first_party,
|
||||
request_url=url,
|
||||
resource_type=resource_type)
|
||||
interceptors.run(request)
|
||||
if request.is_blocked:
|
||||
info.block(True)
|
||||
|
||||
for header, value in shared.custom_headers(url=url):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2017-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2017-2018 Michal Siedlaczek <michal.siedlaczek@gmail.com>
|
||||
|
||||
# This file is part of qutebrowser.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -117,8 +117,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
|
|||
def _get_open_filename(self):
|
||||
return self._filename
|
||||
|
||||
def _set_fileobj(self, fileobj, *,
|
||||
autoclose=True): # pylint: disable=unused-argument
|
||||
def _set_fileobj(self, fileobj, *, autoclose=True):
|
||||
raise downloads.UnsupportedOperationError
|
||||
|
||||
def _set_tempfile(self, fileobj):
|
||||
|
|
@ -181,7 +180,21 @@ def _get_suggested_filename(path):
|
|||
See https://bugreports.qt.io/browse/QTBUG-56978
|
||||
"""
|
||||
filename = os.path.basename(path)
|
||||
filename = re.sub(r'\([0-9]+\)(?=\.|$)', '', filename)
|
||||
|
||||
suffix_re = re.compile(r"""
|
||||
\ ? # Optional space between filename and suffix
|
||||
(
|
||||
# Numerical suffix
|
||||
\([0-9]+\)
|
||||
|
|
||||
# ISO-8601 suffix
|
||||
# https://cs.chromium.org/chromium/src/base/time/time_to_iso8601.cc
|
||||
\ -\ \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z
|
||||
)
|
||||
(?=\.|$) # Begin of extension, or filename without extension
|
||||
""", re.VERBOSE)
|
||||
|
||||
filename = suffix_re.sub('', filename)
|
||||
if not qtutils.version_check('5.9', compiled=False):
|
||||
# https://bugreports.qt.io/browse/QTBUG-58155
|
||||
filename = urllib.parse.unquote(filename)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -22,20 +22,27 @@
|
|||
|
||||
"""QtWebEngine specific part of the web element API."""
|
||||
|
||||
import typing
|
||||
|
||||
from PyQt5.QtCore import QRect, Qt, QPoint, QEventLoop
|
||||
from PyQt5.QtGui import QMouseEvent
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtWebEngineWidgets import QWebEngineSettings
|
||||
|
||||
from qutebrowser.utils import log, javascript, urlutils
|
||||
from qutebrowser.utils import log, javascript, urlutils, usertypes
|
||||
from qutebrowser.browser import webelem
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
# pylint: disable=unused-import,useless-suppression
|
||||
from qutebrowser.browser.webengine import webenginetab
|
||||
|
||||
|
||||
class WebEngineElement(webelem.AbstractWebElement):
|
||||
|
||||
"""A web element for QtWebEngine, using JS under the hood."""
|
||||
|
||||
def __init__(self, js_dict, tab):
|
||||
def __init__(self, js_dict: typing.Dict[str, typing.Any],
|
||||
tab: 'webenginetab.WebEngineTab') -> None:
|
||||
super().__init__(tab)
|
||||
# Do some sanity checks on the data we get from JS
|
||||
js_dict_types = {
|
||||
|
|
@ -48,7 +55,7 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
'rects': list,
|
||||
'attributes': dict,
|
||||
'caret_position': (int, type(None)),
|
||||
}
|
||||
} # type: typing.Dict[str, typing.Union[type, typing.Tuple[type,...]]]
|
||||
assert set(js_dict.keys()).issubset(js_dict_types.keys())
|
||||
for name, typ in js_dict_types.items():
|
||||
if name in js_dict and not isinstance(js_dict[name], typ):
|
||||
|
|
@ -73,50 +80,51 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
self._id = js_dict['id']
|
||||
self._js_dict = js_dict
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self._js_dict.get('text', '')
|
||||
|
||||
def __eq__(self, other):
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, WebEngineElement):
|
||||
return NotImplemented
|
||||
return self._id == other._id # pylint: disable=protected-access
|
||||
|
||||
def __getitem__(self, key):
|
||||
def __getitem__(self, key: str) -> str:
|
||||
attrs = self._js_dict['attributes']
|
||||
return attrs[key]
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
def __setitem__(self, key: str, val: str) -> None:
|
||||
self._js_dict['attributes'][key] = val
|
||||
self._js_call('set_attribute', key, val)
|
||||
|
||||
def __delitem__(self, key):
|
||||
def __delitem__(self, key: str) -> None:
|
||||
log.stub()
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> typing.Iterator[str]:
|
||||
return iter(self._js_dict['attributes'])
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
return len(self._js_dict['attributes'])
|
||||
|
||||
def _js_call(self, name, *args, callback=None):
|
||||
def _js_call(self, name: str, *args: webelem.JsValueType,
|
||||
callback: typing.Callable[[typing.Any], None] = None) -> None:
|
||||
"""Wrapper to run stuff from webelem.js."""
|
||||
if self._tab.is_deleted():
|
||||
raise webelem.OrphanedError("Tab containing element vanished")
|
||||
js_code = javascript.assemble('webelem', name, self._id, *args)
|
||||
self._tab.run_js_async(js_code, callback=callback)
|
||||
|
||||
def has_frame(self):
|
||||
def has_frame(self) -> bool:
|
||||
return True
|
||||
|
||||
def geometry(self):
|
||||
def geometry(self) -> QRect:
|
||||
log.stub()
|
||||
return QRect()
|
||||
|
||||
def classes(self):
|
||||
def classes(self) -> typing.List[str]:
|
||||
"""Get a list of classes assigned to this element."""
|
||||
return self._js_dict['class_name'].split()
|
||||
|
||||
def tag_name(self):
|
||||
def tag_name(self) -> str:
|
||||
"""Get the tag name of this element.
|
||||
|
||||
The returned name will always be lower-case.
|
||||
|
|
@ -125,34 +133,37 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
assert isinstance(tag, str), tag
|
||||
return tag.lower()
|
||||
|
||||
def outer_xml(self):
|
||||
def outer_xml(self) -> str:
|
||||
"""Get the full HTML representation of this element."""
|
||||
return self._js_dict['outer_xml']
|
||||
|
||||
def value(self):
|
||||
def value(self) -> webelem.JsValueType:
|
||||
return self._js_dict.get('value', None)
|
||||
|
||||
def set_value(self, value):
|
||||
def set_value(self, value: webelem.JsValueType) -> None:
|
||||
self._js_call('set_value', value)
|
||||
|
||||
def dispatch_event(self, event, bubbles=False,
|
||||
cancelable=False, composed=False):
|
||||
def dispatch_event(self, event: str,
|
||||
bubbles: bool = False,
|
||||
cancelable: bool = False,
|
||||
composed: bool = False) -> None:
|
||||
self._js_call('dispatch_event', event, bubbles, cancelable, composed)
|
||||
|
||||
def caret_position(self):
|
||||
def caret_position(self) -> typing.Optional[int]:
|
||||
"""Get the text caret position for the current element.
|
||||
|
||||
If the element is not a text element, None is returned.
|
||||
"""
|
||||
return self._js_dict.get('caret_position', None)
|
||||
|
||||
def insert_text(self, text):
|
||||
def insert_text(self, text: str) -> None:
|
||||
if not self.is_editable(strict=True):
|
||||
raise webelem.Error("Element is not editable!")
|
||||
log.webelem.debug("Inserting text into element {!r}".format(self))
|
||||
self._js_call('insert_text', text)
|
||||
|
||||
def rect_on_view(self, *, elem_geometry=None, no_js=False):
|
||||
def rect_on_view(self, *, elem_geometry: QRect = None,
|
||||
no_js: bool = False) -> QRect:
|
||||
"""Get the geometry of the element relative to the webview.
|
||||
|
||||
Skipping of small rectangles is due to <a> elements containing other
|
||||
|
|
@ -193,16 +204,16 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
self, rects))
|
||||
return QRect()
|
||||
|
||||
def remove_blank_target(self):
|
||||
def remove_blank_target(self) -> None:
|
||||
if self._js_dict['attributes'].get('target') == '_blank':
|
||||
self._js_dict['attributes']['target'] = '_top'
|
||||
self._js_call('remove_blank_target')
|
||||
|
||||
def _move_text_cursor(self):
|
||||
def _move_text_cursor(self) -> None:
|
||||
if self.is_text_input() and self.is_editable():
|
||||
self._js_call('move_cursor_to_end')
|
||||
|
||||
def _requires_user_interaction(self):
|
||||
def _requires_user_interaction(self) -> bool:
|
||||
baseurl = self._tab.url()
|
||||
url = self.resolve_url(baseurl)
|
||||
if url is None:
|
||||
|
|
@ -211,7 +222,7 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
return False
|
||||
return url.scheme() not in urlutils.WEBENGINE_SCHEMES
|
||||
|
||||
def _click_editable(self, click_target):
|
||||
def _click_editable(self, click_target: usertypes.ClickTarget) -> None:
|
||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-58515
|
||||
ev = QMouseEvent(QMouseEvent.MouseButtonPress, QPoint(0, 0),
|
||||
QPoint(0, 0), QPoint(0, 0), Qt.NoButton, Qt.NoButton,
|
||||
|
|
@ -221,10 +232,11 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
self._js_call('focus')
|
||||
self._move_text_cursor()
|
||||
|
||||
def _click_js(self, _click_target):
|
||||
def _click_js(self, _click_target: usertypes.ClickTarget) -> None:
|
||||
# FIXME:qtwebengine Have a proper API for this
|
||||
# pylint: disable=protected-access
|
||||
view = self._tab._widget
|
||||
assert view is not None
|
||||
# pylint: enable=protected-access
|
||||
attribute = QWebEngineSettings.JavascriptCanOpenWindows
|
||||
could_open_windows = view.settings().testAttribute(attribute)
|
||||
|
|
@ -238,8 +250,9 @@ class WebEngineElement(webelem.AbstractWebElement):
|
|||
qapp.processEvents(QEventLoop.ExcludeSocketNotifiers |
|
||||
QEventLoop.ExcludeUserInputEvents)
|
||||
|
||||
def reset_setting(_arg):
|
||||
def reset_setting(_arg: typing.Any) -> None:
|
||||
"""Set the JavascriptCanOpenWindows setting to its old value."""
|
||||
assert view is not None
|
||||
try:
|
||||
view.settings().setAttribute(attribute, could_open_windows)
|
||||
except RuntimeError:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -22,6 +22,11 @@
|
|||
from PyQt5.QtCore import QBuffer, QIODevice, QUrl
|
||||
from PyQt5.QtWebEngineCore import (QWebEngineUrlSchemeHandler,
|
||||
QWebEngineUrlRequestJob)
|
||||
try:
|
||||
from PyQt5.QtWebEngineCore import QWebEngineUrlScheme # type: ignore
|
||||
except ImportError:
|
||||
# Added in Qt 5.12
|
||||
QWebEngineUrlScheme = None
|
||||
|
||||
from qutebrowser.browser import qutescheme
|
||||
from qutebrowser.utils import log, qtutils
|
||||
|
|
@ -33,8 +38,12 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
|
|||
|
||||
def install(self, profile):
|
||||
"""Install the handler for qute:// URLs on the given profile."""
|
||||
if QWebEngineUrlScheme is not None:
|
||||
assert QWebEngineUrlScheme.schemeByName(b'qute') is not None
|
||||
|
||||
profile.installUrlSchemeHandler(b'qute', self)
|
||||
if qtutils.version_check('5.11', compiled=False):
|
||||
if (qtutils.version_check('5.11', compiled=False) and
|
||||
not qtutils.version_check('5.12', compiled=False)):
|
||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-63378
|
||||
profile.installUrlSchemeHandler(b'chrome-error', self)
|
||||
profile.installUrlSchemeHandler(b'chrome-extension', self)
|
||||
|
|
@ -53,18 +62,33 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
|
|||
"""
|
||||
try:
|
||||
initiator = job.initiator()
|
||||
request_url = job.requestUrl()
|
||||
except AttributeError:
|
||||
# Added in Qt 5.11
|
||||
return True
|
||||
|
||||
if initiator == QUrl('null') and not qtutils.version_check('5.12'):
|
||||
# https://codereview.qt-project.org/#/c/234849/
|
||||
is_opaque = initiator == QUrl('null')
|
||||
target = request_url.scheme(), request_url.host()
|
||||
|
||||
if is_opaque and not qtutils.version_check('5.12'):
|
||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-70421
|
||||
# When we don't register the qute:// scheme, all requests are
|
||||
# flagged as opaque.
|
||||
return True
|
||||
|
||||
if (target == ('qute', 'testdata') and
|
||||
is_opaque and
|
||||
qtutils.version_check('5.12')):
|
||||
# Allow requests to qute://testdata, as this is needed in Qt 5.12
|
||||
# for all tests to work properly. No qute://testdata handler is
|
||||
# installed outside of tests.
|
||||
return True
|
||||
|
||||
if initiator.isValid() and initiator.scheme() != 'qute':
|
||||
log.misc.warning("Blocking malicious request from {} to {}".format(
|
||||
initiator.toDisplayString(),
|
||||
job.requestUrl().toDisplayString()))
|
||||
request_url.toDisplayString()))
|
||||
job.fail(QWebEngineUrlRequestJob.RequestDenied)
|
||||
return False
|
||||
|
||||
|
|
@ -130,3 +154,17 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
|
|||
buf.seek(0)
|
||||
buf.close()
|
||||
job.reply(mimetype.encode('ascii'), buf)
|
||||
|
||||
|
||||
def init():
|
||||
"""Register the qute:// scheme.
|
||||
|
||||
Note this needs to be called early, before constructing any QtWebEngine
|
||||
classes.
|
||||
"""
|
||||
if QWebEngineUrlScheme is not None:
|
||||
assert not QWebEngineUrlScheme.schemeByName(b'qute').name()
|
||||
scheme = QWebEngineUrlScheme(b'qute')
|
||||
scheme.setFlags(QWebEngineUrlScheme.LocalScheme |
|
||||
QWebEngineUrlScheme.LocalAccessAllowed)
|
||||
QWebEngineUrlScheme.registerScheme(scheme)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -25,12 +25,13 @@ Module attributes:
|
|||
"""
|
||||
|
||||
import os
|
||||
import operator
|
||||
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile,
|
||||
QWebEnginePage)
|
||||
|
||||
from qutebrowser.browser.webengine import spell
|
||||
from qutebrowser.browser.webengine import spell, webenginequtescheme
|
||||
from qutebrowser.config import config, websettings
|
||||
from qutebrowser.config.websettings import AttributeInfo as Attr
|
||||
from qutebrowser.utils import utils, standarddir, qtutils, message, log
|
||||
|
|
@ -163,9 +164,14 @@ class WebEngineSettings(websettings.AbstractSettings):
|
|||
# Qt 5.8
|
||||
'content.print_element_backgrounds':
|
||||
('PrintElementBackgrounds', None),
|
||||
|
||||
# Qt 5.11
|
||||
'content.autoplay':
|
||||
('PlaybackRequiresUserGesture', lambda val: not val),
|
||||
('PlaybackRequiresUserGesture', operator.not_),
|
||||
|
||||
# Qt 5.12
|
||||
'content.dns_prefetch':
|
||||
('DnsPrefetchEnabled', None),
|
||||
}
|
||||
for name, (attribute, converter) in new_attributes.items():
|
||||
try:
|
||||
|
|
@ -298,6 +304,7 @@ def init(args):
|
|||
not hasattr(QWebEnginePage, 'setInspectedPage')): # only Qt < 5.11
|
||||
os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = str(utils.random_port())
|
||||
|
||||
webenginequtescheme.init()
|
||||
spell.init()
|
||||
|
||||
_init_profiles()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2016-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -24,9 +24,9 @@ import functools
|
|||
import re
|
||||
import html as html_utils
|
||||
|
||||
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QEvent, QPoint, QPointF,
|
||||
QUrl, QTimer, QObject)
|
||||
from PyQt5.QtGui import QKeyEvent, QIcon
|
||||
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QUrl,
|
||||
QTimer, QObject)
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtNetwork import QAuthenticator
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript
|
||||
|
|
@ -60,10 +60,8 @@ def init():
|
|||
_qute_scheme_handler.install(webenginesettings.private_profile)
|
||||
|
||||
log.init.debug("Initializing request interceptor...")
|
||||
host_blocker = objreg.get('host-blocker')
|
||||
args = objreg.get('args')
|
||||
req_interceptor = interceptor.RequestInterceptor(
|
||||
host_blocker, args=args, parent=app)
|
||||
req_interceptor = interceptor.RequestInterceptor(args=args, parent=app)
|
||||
req_interceptor.install(webenginesettings.default_profile)
|
||||
req_interceptor.install(webenginesettings.private_profile)
|
||||
|
||||
|
|
@ -132,7 +130,7 @@ class WebEnginePrinting(browsertab.AbstractPrinting):
|
|||
"""QtWebEngine implementations related to printing."""
|
||||
|
||||
def check_pdf_support(self):
|
||||
return True
|
||||
pass
|
||||
|
||||
def check_printer_support(self):
|
||||
if not hasattr(self._widget.page(), 'print'):
|
||||
|
|
@ -205,8 +203,8 @@ class WebEngineSearch(browsertab.AbstractSearch):
|
|||
|
||||
self._widget.findText(text, flags, wrapped_callback)
|
||||
|
||||
def search(self, text, *, ignore_case='never', reverse=False,
|
||||
result_cb=None):
|
||||
def search(self, text, *, ignore_case=usertypes.IgnoreCase.never,
|
||||
reverse=False, result_cb=None):
|
||||
# Don't go to next entry on duplicate search
|
||||
if self.text == text and self.search_displayed:
|
||||
log.webview.debug("Ignoring duplicate search request"
|
||||
|
|
@ -423,7 +421,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, 1000)):
|
||||
self._tab.key_press(key, modifier)
|
||||
self._tab.fake_key_press(key, modifier)
|
||||
|
||||
@pyqtSlot(QPointF)
|
||||
def _update_pos(self, pos):
|
||||
|
|
@ -478,7 +476,7 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
|||
def to_anchor(self, name):
|
||||
url = self._tab.url()
|
||||
url.setFragment(name)
|
||||
self._tab.openurl(url)
|
||||
self._tab.load_url(url)
|
||||
|
||||
def delta(self, x=0, y=0):
|
||||
self._tab.run_js_async(javascript.assemble('window', 'scrollBy', x, y))
|
||||
|
|
@ -500,10 +498,10 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
|||
self._repeated_key_press(Qt.Key_Right, count)
|
||||
|
||||
def top(self):
|
||||
self._tab.key_press(Qt.Key_Home)
|
||||
self._tab.fake_key_press(Qt.Key_Home)
|
||||
|
||||
def bottom(self):
|
||||
self._tab.key_press(Qt.Key_End)
|
||||
self._tab.fake_key_press(Qt.Key_End)
|
||||
|
||||
def page_up(self, count=1):
|
||||
self._repeated_key_press(Qt.Key_PageUp, count)
|
||||
|
|
@ -518,25 +516,9 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
|||
return self._at_bottom
|
||||
|
||||
|
||||
class WebEngineHistory(browsertab.AbstractHistory):
|
||||
class WebEngineHistoryPrivate(browsertab.AbstractHistoryPrivate):
|
||||
|
||||
"""QtWebEngine implementations related to page history."""
|
||||
|
||||
def current_idx(self):
|
||||
return self._history.currentItemIndex()
|
||||
|
||||
def can_go_back(self):
|
||||
return self._history.canGoBack()
|
||||
|
||||
def can_go_forward(self):
|
||||
return self._history.canGoForward()
|
||||
|
||||
def _item_at(self, i):
|
||||
return self._history.itemAt(i)
|
||||
|
||||
def _go_to_item(self, item):
|
||||
self._tab.predicted_navigation.emit(item.url())
|
||||
self._history.goToItem(item)
|
||||
"""History-related methods which are not part of the extension API."""
|
||||
|
||||
def serialize(self):
|
||||
if not qtutils.version_check('5.9', compiled=False):
|
||||
|
|
@ -551,11 +533,11 @@ class WebEngineHistory(browsertab.AbstractHistory):
|
|||
return qtutils.serialize(self._history)
|
||||
|
||||
def deserialize(self, data):
|
||||
return qtutils.deserialize(data, self._history)
|
||||
qtutils.deserialize(data, self._history)
|
||||
|
||||
def load_items(self, items):
|
||||
if items:
|
||||
self._tab.predicted_navigation.emit(items[-1].url)
|
||||
self._tab.before_load_started.emit(items[-1].url)
|
||||
|
||||
stream, _data, cur_data = tabhistory.serialize(items)
|
||||
qtutils.deserialize_stream(stream, self._history)
|
||||
|
|
@ -573,6 +555,37 @@ class WebEngineHistory(browsertab.AbstractHistory):
|
|||
self._tab.load_finished.connect(_on_load_finished)
|
||||
|
||||
|
||||
class WebEngineHistory(browsertab.AbstractHistory):
|
||||
|
||||
"""QtWebEngine implementations related to page history."""
|
||||
|
||||
def __init__(self, tab):
|
||||
super().__init__(tab)
|
||||
self.private_api = WebEngineHistoryPrivate(tab)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._history)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._history.items())
|
||||
|
||||
def current_idx(self):
|
||||
return self._history.currentItemIndex()
|
||||
|
||||
def can_go_back(self):
|
||||
return self._history.canGoBack()
|
||||
|
||||
def can_go_forward(self):
|
||||
return self._history.canGoForward()
|
||||
|
||||
def _item_at(self, i):
|
||||
return self._history.itemAt(i)
|
||||
|
||||
def _go_to_item(self, item):
|
||||
self._tab.before_load_started.emit(item.url())
|
||||
self._history.goToItem(item)
|
||||
|
||||
|
||||
class WebEngineZoom(browsertab.AbstractZoom):
|
||||
|
||||
"""QtWebEngine implementations related to zooming."""
|
||||
|
|
@ -585,19 +598,20 @@ class WebEngineElements(browsertab.AbstractElements):
|
|||
|
||||
"""QtWebEngine implemementations related to elements on the page."""
|
||||
|
||||
def _js_cb_multiple(self, callback, js_elems):
|
||||
def _js_cb_multiple(self, callback, error_cb, js_elems):
|
||||
"""Handle found elements coming from JS and call the real callback.
|
||||
|
||||
Args:
|
||||
callback: The callback to call with the found elements.
|
||||
Called with None if there was an error.
|
||||
error_cb: The callback to call in case of an error.
|
||||
js_elems: The elements serialized from javascript.
|
||||
"""
|
||||
if js_elems is None:
|
||||
callback(None)
|
||||
error_cb(webelem.Error("Unknown error while getting "
|
||||
"elements"))
|
||||
return
|
||||
elif not js_elems['success']:
|
||||
callback(webelem.Error(js_elems['error']))
|
||||
error_cb(webelem.Error(js_elems['error']))
|
||||
return
|
||||
|
||||
elems = []
|
||||
|
|
@ -624,10 +638,11 @@ class WebEngineElements(browsertab.AbstractElements):
|
|||
elem = webengineelem.WebEngineElement(js_elem, tab=self._tab)
|
||||
callback(elem)
|
||||
|
||||
def find_css(self, selector, callback, *, only_visible=False):
|
||||
def find_css(self, selector, callback, error_cb, *,
|
||||
only_visible=False):
|
||||
js_code = javascript.assemble('webelem', 'find_css', selector,
|
||||
only_visible)
|
||||
js_cb = functools.partial(self._js_cb_multiple, callback)
|
||||
js_cb = functools.partial(self._js_cb_multiple, callback, error_cb)
|
||||
self._tab.run_js_async(js_code, js_cb)
|
||||
|
||||
def find_id(self, elem_id, callback):
|
||||
|
|
@ -670,8 +685,9 @@ class WebEngineAudio(browsertab.AbstractAudio):
|
|||
self._tab.url_changed.connect(self._on_url_changed)
|
||||
config.instance.changed.connect(self._on_config_changed)
|
||||
|
||||
def set_muted(self, muted: bool, override: bool = False):
|
||||
def set_muted(self, muted: bool, override: bool = False) -> None:
|
||||
self._overridden = override
|
||||
assert self._widget is not None
|
||||
page = self._widget.page()
|
||||
page.setAudioMuted(muted)
|
||||
|
||||
|
|
@ -699,8 +715,6 @@ class _WebEnginePermissions(QObject):
|
|||
|
||||
"""Handling of various permission-related signals."""
|
||||
|
||||
_abort_questions = pyqtSignal()
|
||||
|
||||
def __init__(self, tab, parent=None):
|
||||
super().__init__(parent)
|
||||
self._tab = tab
|
||||
|
|
@ -720,9 +734,6 @@ class _WebEnginePermissions(QObject):
|
|||
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()
|
||||
|
|
@ -800,7 +811,7 @@ class _WebEnginePermissions(QObject):
|
|||
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])
|
||||
abort_on=[self._tab.abort_questions])
|
||||
|
||||
if question is not None:
|
||||
page.featurePermissionRequestCanceled.connect(
|
||||
|
|
@ -828,7 +839,7 @@ class _WebEnginePermissions(QObject):
|
|||
option='content.persistent_storage',
|
||||
msg='use {} of persistent storage'.format(size),
|
||||
yes_action=request.accept, no_action=request.reject,
|
||||
abort_on=[self._abort_questions],
|
||||
abort_on=[self._tab.abort_questions],
|
||||
blocking=True)
|
||||
|
||||
def _on_register_protocol_handler_requested(self, request):
|
||||
|
|
@ -837,7 +848,7 @@ class _WebEnginePermissions(QObject):
|
|||
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],
|
||||
abort_on=[self._tab.abort_questions],
|
||||
blocking=True)
|
||||
|
||||
|
||||
|
|
@ -911,10 +922,14 @@ class _WebEngineScripts(QObject):
|
|||
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)
|
||||
if not qtutils.version_check('5.12'):
|
||||
# WORKAROUND for Qt versions < 5.12 not exposing window.print().
|
||||
# Qt 5.12 has a printRequested() signal so we don't need this hack
|
||||
# anymore.
|
||||
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()
|
||||
|
|
@ -1023,14 +1038,42 @@ class _WebEngineScripts(QObject):
|
|||
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)
|
||||
elif script.needs_document_end_workaround():
|
||||
log.greasemonkey.debug("Forcing @run-at document-end for {}"
|
||||
.format(script.name))
|
||||
new_script.setInjectionPoint(QWebEngineScript.DocumentReady)
|
||||
|
||||
log.greasemonkey.debug('adding script: {}'
|
||||
.format(new_script.name()))
|
||||
page_scripts.insert(new_script)
|
||||
|
||||
|
||||
class WebEngineTabPrivate(browsertab.AbstractTabPrivate):
|
||||
|
||||
"""QtWebEngine-related methods which aren't part of the public API."""
|
||||
|
||||
def networkaccessmanager(self):
|
||||
return None
|
||||
|
||||
def user_agent(self):
|
||||
return None
|
||||
|
||||
def clear_ssl_errors(self):
|
||||
raise browsertab.UnsupportedOperationError
|
||||
|
||||
def event_target(self):
|
||||
return self._widget.render_widget()
|
||||
|
||||
def shutdown(self):
|
||||
self._tab.shutting_down.emit()
|
||||
self._tab.action.exit_fullscreen()
|
||||
self._widget.shutdown()
|
||||
|
||||
|
||||
class WebEngineTab(browsertab.AbstractTab):
|
||||
|
||||
"""A QtWebEngine tab in the browser.
|
||||
|
|
@ -1038,14 +1081,16 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
Signals:
|
||||
_load_finished_fake:
|
||||
Used in place of unreliable loadFinished
|
||||
abort_questions: Emitted when a new load started or we're shutting
|
||||
down.
|
||||
"""
|
||||
|
||||
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-65223
|
||||
_load_finished_fake = pyqtSignal(bool)
|
||||
abort_questions = pyqtSignal()
|
||||
|
||||
def __init__(self, *, win_id, mode_manager, private, parent=None):
|
||||
super().__init__(win_id=win_id, mode_manager=mode_manager,
|
||||
private=private, parent=parent)
|
||||
super().__init__(win_id=win_id, private=private, parent=parent)
|
||||
widget = webview.WebEngineView(tabdata=self.data, win_id=win_id,
|
||||
private=private)
|
||||
self.history = WebEngineHistory(tab=self)
|
||||
|
|
@ -1058,6 +1103,8 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
self.elements = WebEngineElements(tab=self)
|
||||
self.action = WebEngineAction(tab=self)
|
||||
self.audio = WebEngineAudio(tab=self, parent=self)
|
||||
self.private_api = WebEngineTabPrivate(mode_manager=mode_manager,
|
||||
tab=self)
|
||||
self._permissions = _WebEnginePermissions(tab=self, parent=self)
|
||||
self._scripts = _WebEngineScripts(tab=self, parent=self)
|
||||
# We're assigning settings in _set_widget
|
||||
|
|
@ -1095,21 +1142,23 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
self.zoom.set_factor(self._saved_zoom)
|
||||
self._saved_zoom = None
|
||||
|
||||
def openurl(self, url, *, predict=True):
|
||||
"""Open the given URL in this tab.
|
||||
def load_url(self, url, *, emit_before_load_started=True):
|
||||
"""Load the given URL in this tab.
|
||||
|
||||
Arguments:
|
||||
url: The QUrl to open.
|
||||
predict: If set to False, predicted_navigation is not emitted.
|
||||
url: The QUrl to load.
|
||||
emit_before_load_started: If set to False, before_load_started 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, predict=predict)
|
||||
self._load_url_prepare(
|
||||
url, emit_before_load_started=emit_before_load_started)
|
||||
self._widget.load(url)
|
||||
|
||||
def url(self, requested=False):
|
||||
def url(self, *, requested=False):
|
||||
page = self._widget.page()
|
||||
if requested:
|
||||
return page.requestedUrl()
|
||||
|
|
@ -1139,11 +1188,6 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
else:
|
||||
self._widget.page().runJavaScript(code, world_id, callback)
|
||||
|
||||
def shutdown(self):
|
||||
self.shutting_down.emit()
|
||||
self.action.exit_fullscreen()
|
||||
self._widget.shutdown()
|
||||
|
||||
def reload(self, *, force=False):
|
||||
if force:
|
||||
action = QWebEnginePage.ReloadAndBypassCache
|
||||
|
|
@ -1168,22 +1212,6 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
# percent encoded content is 2 megabytes minus 30 bytes.
|
||||
self._widget.setHtml(html, base_url)
|
||||
|
||||
def networkaccessmanager(self):
|
||||
return None
|
||||
|
||||
def user_agent(self):
|
||||
return None
|
||||
|
||||
def clear_ssl_errors(self):
|
||||
raise browsertab.UnsupportedOperationError
|
||||
|
||||
def key_press(self, key, modifier=Qt.NoModifier):
|
||||
press_evt = QKeyEvent(QEvent.KeyPress, key, modifier, 0, 0, 0)
|
||||
release_evt = QKeyEvent(QEvent.KeyRelease, key, modifier,
|
||||
0, 0, 0)
|
||||
self.send_event(press_evt)
|
||||
self.send_event(release_evt)
|
||||
|
||||
def _show_error_page(self, url, error):
|
||||
"""Show an error page in the tab."""
|
||||
log.misc.debug("Showing error page for {}".format(error))
|
||||
|
|
@ -1220,7 +1248,7 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
log.misc.debug("Ignoring invalid URL being added to history")
|
||||
return
|
||||
|
||||
self.add_history_item.emit(url, requested_url, title)
|
||||
self.history_item_triggered.emit(url, requested_url, title)
|
||||
|
||||
@pyqtSlot(QUrl, 'QAuthenticator*', 'QString')
|
||||
def _on_proxy_authentication_required(self, url, authenticator,
|
||||
|
|
@ -1232,15 +1260,13 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
answer = message.ask(
|
||||
title="Proxy authentication required", text=msg,
|
||||
mode=usertypes.PromptMode.user_pwd,
|
||||
abort_on=[self.shutting_down, self.load_started], url=urlstr)
|
||||
abort_on=[self.abort_questions], url=urlstr)
|
||||
if answer is not None:
|
||||
authenticator.setUser(answer.user)
|
||||
authenticator.setPassword(answer.password)
|
||||
else:
|
||||
try:
|
||||
# pylint: disable=no-member, useless-suppression
|
||||
sip.assign(authenticator, QAuthenticator())
|
||||
# pylint: enable=no-member, useless-suppression
|
||||
except AttributeError:
|
||||
self._show_error_page(url, "Proxy authentication required")
|
||||
|
||||
|
|
@ -1256,15 +1282,12 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
|
||||
if not netrc_success:
|
||||
log.network.debug("Asking for credentials")
|
||||
abort_on = [self.shutting_down, self.load_started]
|
||||
answer = shared.authentication_required(url, authenticator,
|
||||
abort_on)
|
||||
answer = shared.authentication_required(
|
||||
url, authenticator, abort_on=[self.abort_questions])
|
||||
if not netrc_success and answer is None:
|
||||
log.network.debug("Aborting auth")
|
||||
try:
|
||||
# pylint: disable=no-member, useless-suppression
|
||||
sip.assign(authenticator, QAuthenticator())
|
||||
# pylint: enable=no-member, useless-suppression
|
||||
except AttributeError:
|
||||
# WORKAROUND for
|
||||
# https://www.riverbankcomputing.com/pipermail/pyqt/2016-December/038400.html
|
||||
|
|
@ -1348,9 +1371,9 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
log.config.debug(
|
||||
"Loading {} again because of config change".format(
|
||||
self._reload_url.toDisplayString()))
|
||||
QTimer.singleShot(100, functools.partial(self.openurl,
|
||||
self._reload_url,
|
||||
predict=False))
|
||||
QTimer.singleShot(100, functools.partial(
|
||||
self.load_url, self._reload_url,
|
||||
emit_before_load_started=False))
|
||||
self._reload_url = None
|
||||
|
||||
if not qtutils.version_check('5.10', compiled=False):
|
||||
|
|
@ -1369,7 +1392,7 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
|
||||
if error.is_overridable():
|
||||
error.ignore = shared.ignore_certificate_errors(
|
||||
url, [error], abort_on=[self.shutting_down, self.load_started])
|
||||
url, [error], abort_on=[self.abort_questions])
|
||||
else:
|
||||
log.webview.error("Non-overridable certificate error: "
|
||||
"{}".format(error))
|
||||
|
|
@ -1389,24 +1412,29 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
self._show_error_page(url, str(error))
|
||||
|
||||
@pyqtSlot(QUrl)
|
||||
def _on_predicted_navigation(self, url):
|
||||
def _on_before_load_started(self, url):
|
||||
"""If we know we're going to visit a URL soon, change the settings.
|
||||
|
||||
This is a WORKAROUND for https://bugreports.qt.io/browse/QTBUG-66656
|
||||
"""
|
||||
super()._on_predicted_navigation(url)
|
||||
super()._on_before_load_started(url)
|
||||
if not qtutils.version_check('5.11.1', compiled=False):
|
||||
self.settings.update_for_url(url)
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_print_requested(self):
|
||||
"""Slot for window.print() in JS."""
|
||||
try:
|
||||
self.printing.show_dialog()
|
||||
except browsertab.WebTabError as e:
|
||||
message.error(str(e))
|
||||
|
||||
@pyqtSlot(usertypes.NavigationRequest)
|
||||
def _on_navigation_request(self, navigation):
|
||||
super()._on_navigation_request(navigation)
|
||||
|
||||
if navigation.url == QUrl('qute://print'):
|
||||
try:
|
||||
self.printing.show_dialog()
|
||||
except browsertab.WebTabError as e:
|
||||
message.error(str(e))
|
||||
self._on_print_requested()
|
||||
navigation.accepted = False
|
||||
|
||||
if not navigation.accepted or not navigation.is_main_frame:
|
||||
|
|
@ -1438,6 +1466,37 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
if reload_needed:
|
||||
self._reload_url = navigation.url
|
||||
|
||||
def _on_select_client_certificate(self, selection):
|
||||
"""Handle client certificates.
|
||||
|
||||
Currently, we simply pick the first available certificate and show an
|
||||
additional note if there are multiple matches.
|
||||
"""
|
||||
certificate = selection.certificates()[0]
|
||||
text = ('<b>Subject:</b> {subj}<br/>'
|
||||
'<b>Issuer:</b> {issuer}<br/>'
|
||||
'<b>Serial:</b> {serial}'.format(
|
||||
subj=html_utils.escape(certificate.subjectDisplayName()),
|
||||
issuer=html_utils.escape(certificate.issuerDisplayName()),
|
||||
serial=bytes(certificate.serialNumber()).decode('ascii')))
|
||||
if len(selection.certificates()) > 1:
|
||||
text += ('<br/><br/><b>Note:</b> Multiple matching certificates '
|
||||
'were found, but certificate selection is not '
|
||||
'implemented yet!')
|
||||
urlstr = selection.host().host()
|
||||
|
||||
present = message.ask(
|
||||
title='Present client certificate to {}?'.format(urlstr),
|
||||
text=text,
|
||||
mode=usertypes.PromptMode.yesno,
|
||||
abort_on=[self.abort_questions],
|
||||
url=urlstr)
|
||||
|
||||
if present:
|
||||
selection.select(certificate)
|
||||
else:
|
||||
selection.selectNone()
|
||||
|
||||
def _connect_signals(self):
|
||||
view = self._widget
|
||||
page = view.page()
|
||||
|
|
@ -1453,6 +1512,11 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
page.contentsSizeChanged.connect(self.contents_size_changed)
|
||||
page.navigation_request.connect(self._on_navigation_request)
|
||||
|
||||
if qtutils.version_check('5.12'):
|
||||
page.printRequested.connect(self._on_print_requested)
|
||||
page.selectClientCertificate.connect(
|
||||
self._on_select_client_certificate)
|
||||
|
||||
view.titleChanged.connect(self.title_changed)
|
||||
view.urlChanged.connect(self._on_url_changed)
|
||||
view.renderProcessTerminated.connect(
|
||||
|
|
@ -1472,12 +1536,11 @@ class WebEngineTab(browsertab.AbstractTab):
|
|||
page.loadFinished.connect(self._restore_zoom)
|
||||
page.loadFinished.connect(self._on_load_finished)
|
||||
|
||||
self.predicted_navigation.connect(self._on_predicted_navigation)
|
||||
self.before_load_started.connect(self._on_before_load_started)
|
||||
self.shutting_down.connect(self.abort_questions)
|
||||
self.load_started.connect(self.abort_questions)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.audio._connect_signals()
|
||||
self._permissions.connect_signals()
|
||||
self._scripts.connect_signals()
|
||||
|
||||
def event_target(self):
|
||||
return self._widget.render_widget()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
@ -64,6 +64,8 @@ class WebEngineView(QWebEngineView):
|
|||
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
|
||||
|
||||
This got introduced in Qt 5.11.0 and fixed in 5.12.0.
|
||||
"""
|
||||
if 'lost-focusproxy' not in objreg.get('args').debug_flags:
|
||||
proxy = self.focusProxy()
|
||||
|
|
@ -240,7 +242,7 @@ class WebEnginePage(QWebEnginePage):
|
|||
def acceptNavigationRequest(self,
|
||||
url: QUrl,
|
||||
typ: QWebEnginePage.NavigationType,
|
||||
is_main_frame: bool):
|
||||
is_main_frame: bool) -> bool:
|
||||
"""Override acceptNavigationRequest to forward it to the tab API."""
|
||||
type_map = {
|
||||
QWebEnginePage.NavigationTypeLinkClicked:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2019 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue