Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95d1721f01 | ||
|
|
410be07f54 | ||
|
|
01de52c23a | ||
|
|
a84807ed05 | ||
|
|
2795ae9478 |
@@ -5,15 +5,13 @@ cache:
|
||||
build: off
|
||||
environment:
|
||||
PYTHONUNBUFFERED: 1
|
||||
PYTHON: C:\Python36-x64\python.exe
|
||||
matrix:
|
||||
- TESTENV: py36-pyqt511
|
||||
- TESTENV: py34
|
||||
- TESTENV: unittests-frozen
|
||||
- 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'
|
||||
- C:\Python27\python -u scripts\dev\ci\appveyor_install.py
|
||||
|
||||
test_script:
|
||||
- '%PYTHON% -m tox -e %TESTENV%'
|
||||
- C:\Python34\Scripts\tox -e %TESTENV%
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
coverage:
|
||||
status:
|
||||
project: off
|
||||
patch: off
|
||||
changes: off
|
||||
|
||||
comment: off
|
||||
@@ -12,7 +12,6 @@ exclude_lines =
|
||||
def __repr__
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
raise utils\.Unreachable
|
||||
if __name__ == ["']__main__["']:
|
||||
|
||||
[xml]
|
||||
|
||||
1
.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
qutebrowser/3rdparty/pdfjs/*
|
||||
49
.eslintrc
Normal file
@@ -0,0 +1,49 @@
|
||||
# vim: ft=yaml
|
||||
|
||||
env:
|
||||
browser: true
|
||||
|
||||
rules:
|
||||
block-scoped-var: 2
|
||||
dot-location: 2
|
||||
default-case: 2
|
||||
guard-for-in: 2
|
||||
no-div-regex: 2
|
||||
no-param-reassign: 2
|
||||
no-eq-null: 2
|
||||
no-floating-decimal: 2
|
||||
no-self-compare: 2
|
||||
no-throw-literal: 2
|
||||
no-void: 2
|
||||
radix: 2
|
||||
wrap-iife: [2, "inside"]
|
||||
brace-style: [2, "1tbs", {"allowSingleLine": true}]
|
||||
comma-style: [2, "last"]
|
||||
consistent-this: [2, "self"]
|
||||
func-style: [2, "declaration"]
|
||||
indent: [2, 4, {"SwitchCase": 1}]
|
||||
linebreak-style: [2, "unix"]
|
||||
max-nested-callbacks: [2, 3]
|
||||
no-lonely-if: 2
|
||||
no-multiple-empty-lines: [2, {"max": 2}]
|
||||
no-nested-ternary: 2
|
||||
no-unneeded-ternary: 2
|
||||
operator-assignment: [2, "always"]
|
||||
operator-linebreak: [2, "after"]
|
||||
keyword-spacing: 2
|
||||
space-before-blocks: [2, "always"]
|
||||
space-before-function-paren: [2, {"anonymous": "never", "named": "never"}]
|
||||
object-curly-spacing: [2, "never"]
|
||||
array-bracket-spacing: [2, "never"]
|
||||
computed-property-spacing: [2, "never"]
|
||||
space-in-parens: [2, "never"]
|
||||
space-unary-ops: [2, {"words": true, "nonwords": false}]
|
||||
spaced-comment: [2, "always"]
|
||||
max-depth: [2, 5]
|
||||
max-len: [2, 79, 4]
|
||||
max-params: [2, 5]
|
||||
max-statements: [2, 30]
|
||||
no-bitwise: 2
|
||||
quote-props: [2, "always"]
|
||||
global-strict: 0
|
||||
quotes: 0
|
||||
38
.flake8
@@ -1,20 +1,13 @@
|
||||
[flake8]
|
||||
exclude = .*,__pycache__,resources.py
|
||||
# B001: bare except
|
||||
# B008: Do not perform calls in argument defaults. (fine with some Qt stuff)
|
||||
# B305: .next() (false-positives)
|
||||
# E128: continuation line under-indented for visual indent
|
||||
# E226: missing whitespace around arithmetic operator
|
||||
# E265: Block comment should start with '#'
|
||||
# E501: Line too long
|
||||
# E402: module level import not at top of file
|
||||
# E266: too many leading '#' for block comment
|
||||
# E722: do not use bare except
|
||||
# E731: do not assign a lambda expression, use a def
|
||||
# (for pytest's __tracebackhide__)
|
||||
# F401: Unused import
|
||||
# N802: function name should be lowercase
|
||||
# N806: variable in function should be lowercase
|
||||
# P101: format string does contain unindexed parameters
|
||||
# P102: docstring does contain unindexed parameters
|
||||
# P103: other string does contain unindexed parameters
|
||||
@@ -22,33 +15,34 @@ exclude = .*,__pycache__,resources.py
|
||||
# D103: Missing docstring in public function (will be handled by others)
|
||||
# D104: Missing docstring in public package (will be handled by others)
|
||||
# D105: Missing docstring in magic method (will be handled by others)
|
||||
# D106: Missing docstring in public nested class (will be handled by others)
|
||||
# D107: Missing docstring in __init__ (will be handled by others)
|
||||
# D209: Blank line before closing """ (removed from PEP257)
|
||||
# D211: No blank lines allowed before class docstring
|
||||
# (PEP257 got changed, but let's stick to the old standard)
|
||||
# D401: First line should be in imperative mood (okay sometimes)
|
||||
# D402: First line should not be function's signature (false-positives)
|
||||
# D403: First word of the first line should be properly capitalized
|
||||
# (false-positives)
|
||||
# D413: Missing blank line after last section (not in pep257?)
|
||||
# A003: Builtin name for class attribute (needed for overridden methods)
|
||||
# H101: Use TODO(NAME)
|
||||
# H201: bare except
|
||||
# H238: Use new-stule classes
|
||||
# H301: one import per line
|
||||
# H306: imports not in alphabetical order
|
||||
ignore =
|
||||
B001,B008,B305,
|
||||
E128,E226,E265,E501,E402,E266,E722,E731,
|
||||
E128,E226,E265,E501,E402,E266,
|
||||
F401,
|
||||
N802,
|
||||
P101,P102,P103,
|
||||
D102,D103,D106,D107,D104,D105,D209,D211,D401,D402,D403,D413,
|
||||
A003
|
||||
D102,D103,D104,D105,D209,D211,D402,D403,
|
||||
H101,H201,H238,H301,H306
|
||||
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
|
||||
putty-auto-ignore = True
|
||||
putty-ignore =
|
||||
/# pylint: disable=invalid-name/ : +N801,N806
|
||||
/# pylint: disable=wildcard-import/ : +F403
|
||||
/# pragma: no mccabe/ : +C901
|
||||
tests/*/test_*.py : +D100,D101,D401
|
||||
tests/unit/browser/webkit/http/test_content_disposition.py : +D400
|
||||
scripts/dev/ci/appveyor_install.py : +FI53
|
||||
copyright-check = True
|
||||
copyright-regexp = # Copyright [\d-]+ .*
|
||||
copyright-min-file-size = 110
|
||||
|
||||
12
.github/CODEOWNERS
vendored
@@ -1,12 +0,0 @@
|
||||
qutebrowser/browser/history.py @rcorre
|
||||
qutebrowser/completion/* @rcorre
|
||||
qutebrowser/misc/sql.py @rcorre
|
||||
tests/end2end/features/completion.feature @rcorre
|
||||
tests/end2end/features/test_completion_bdd.py @rcorre
|
||||
tests/unit/browser/test_history.py @rcorre
|
||||
tests/unit/completion/* @rcorre
|
||||
tests/unit/misc/test_sql.py @rcorre
|
||||
|
||||
qutebrowser/config/configdata.yml @mschilli87
|
||||
|
||||
qutebrowser/javascript/caret.js @artur-shaik
|
||||
46
.github/CODE_OF_CONDUCT.md
vendored
@@ -1,46 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mail@qutebrowser.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
17
.github/CONTRIBUTING.asciidoc
vendored
@@ -1,17 +0,0 @@
|
||||
IMPORTANT: I'm currently (July 2018) more busy than usual until September,
|
||||
because of exams coming up. Review of non-trivial pull requests will thus be
|
||||
delayed until then. If you're reading this note after mid-September, please
|
||||
open an issue.
|
||||
|
||||
- Before you start to work on something, please leave a comment on the relevant
|
||||
issue (or open one). This makes sure there is no duplicate work done.
|
||||
|
||||
- Either run the testsuite locally, or keep an eye on Travis CI / AppVeyor
|
||||
after pushing changes.
|
||||
|
||||
- If you are stuck somewhere or have questions,
|
||||
https://github.com/qutebrowser/qutebrowser#getting-help[please ask]!
|
||||
|
||||
See the full contribution documentation for details and other useful hints:
|
||||
|
||||
include::../doc/contributing.asciidoc[]
|
||||
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,2 +1,4 @@
|
||||
<!-- If this is a bug report, please remember to mention your version info from
|
||||
`:open qute:version` or `qutebrowser --version` -->
|
||||
Please remember to mention your version info (qutebrowser, Qt, PyQt,
|
||||
OS/distribution) from the `qute:version` page or `qutebrowser --version`
|
||||
|
||||
---
|
||||
|
||||
21
.gitignore
vendored
@@ -15,9 +15,12 @@ __pycache__
|
||||
/qutebrowser/3rdparty
|
||||
/doc/*.html
|
||||
/README.html
|
||||
/CHANGELOG.html
|
||||
/CONTRIBUTING.html
|
||||
/FAQ.html
|
||||
/INSTALL.html
|
||||
/qutebrowser/html/doc/
|
||||
/qutebrowser/html/*.html
|
||||
/.venv*
|
||||
/.venv
|
||||
/.coverage
|
||||
/htmlcov
|
||||
/coverage.xml
|
||||
@@ -25,20 +28,12 @@ __pycache__
|
||||
/.tox
|
||||
/testresults.html
|
||||
/.cache
|
||||
/.pytest_cache
|
||||
/.testmondata
|
||||
/.hypothesis
|
||||
/.mypy_cache
|
||||
/prof
|
||||
/venv
|
||||
TODO
|
||||
/scripts/testbrowser/cpp/webkit/Makefile
|
||||
/scripts/testbrowser/cpp/webkit/main.o
|
||||
/scripts/testbrowser/cpp/webkit/testbrowser
|
||||
/scripts/testbrowser/cpp/webkit/.qmake.stash
|
||||
/scripts/testbrowser/cpp/webengine/Makefile
|
||||
/scripts/testbrowser/cpp/webengine/main.o
|
||||
/scripts/testbrowser/cpp/webengine/testbrowser
|
||||
/scripts/testbrowser/cpp/webengine/.qmake.stash
|
||||
/scripts/testbrowser_cpp/Makefile
|
||||
/scripts/testbrowser_cpp/main.o
|
||||
/scripts/testbrowser_cpp/testbrowser
|
||||
/scripts/dev/pylint_checkers/qute_pylint.egg-info
|
||||
/misc/file_version_info.txt
|
||||
|
||||
55
.pylintrc
@@ -13,40 +13,38 @@ persistent=n
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
enable=all
|
||||
disable=locally-disabled,
|
||||
locally-enabled,
|
||||
suppressed-message,
|
||||
disable=no-self-use,
|
||||
fixme,
|
||||
no-self-use,
|
||||
global-statement,
|
||||
locally-disabled,
|
||||
locally-enabled,
|
||||
too-many-ancestors,
|
||||
too-few-public-methods,
|
||||
too-many-public-methods,
|
||||
cyclic-import,
|
||||
bad-continuation,
|
||||
too-many-instance-attributes,
|
||||
blacklisted-name,
|
||||
too-many-lines,
|
||||
logging-format-interpolation,
|
||||
logging-not-lazy,
|
||||
broad-except,
|
||||
bare-except,
|
||||
eval-used,
|
||||
exec-used,
|
||||
global-statement,
|
||||
wrong-import-position,
|
||||
duplicate-code,
|
||||
no-else-return,
|
||||
too-many-ancestors,
|
||||
too-many-public-methods,
|
||||
too-many-instance-attributes,
|
||||
too-many-lines,
|
||||
file-ignored,
|
||||
wrong-import-order,
|
||||
ungrouped-imports,
|
||||
redefined-variable-type,
|
||||
suppressed-message,
|
||||
too-many-return-statements,
|
||||
too-many-boolean-expressions,
|
||||
too-many-locals,
|
||||
too-many-branches,
|
||||
too-many-statements,
|
||||
too-few-public-methods
|
||||
duplicate-code,
|
||||
wrong-import-position
|
||||
|
||||
[BASIC]
|
||||
function-rgx=[a-z_][a-z0-9_]{2,50}$
|
||||
const-rgx=[A-Za-z_][A-Za-z0-9_]{0,30}$
|
||||
method-rgx=[a-z_][A-Za-z0-9_]{1,50}$
|
||||
attr-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{1,30}|(__.*__))$
|
||||
argument-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
docstring-min-length=3
|
||||
@@ -54,9 +52,12 @@ no-docstring-rgx=(^_|^main$)
|
||||
|
||||
[FORMAT]
|
||||
max-line-length=79
|
||||
ignore-long-lines=(<?https?://|^# Copyright 201\d)
|
||||
ignore-long-lines=(<?https?://|^# Copyright 201\d|# (pylint|flake8): disable=)
|
||||
expected-line-ending-format=LF
|
||||
|
||||
[SIMILARITIES]
|
||||
min-similarity-lines=8
|
||||
|
||||
[VARIABLES]
|
||||
dummy-variables-rgx=_.*
|
||||
|
||||
@@ -67,11 +68,9 @@ max-args=10
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
[TYPECHECK]
|
||||
ignored-modules=PyQt5,PyQt5.QtWebKit
|
||||
|
||||
[IMPORTS]
|
||||
# WORKAROUND
|
||||
# For some reason, pylint doesn't know about some Python 3 modules on
|
||||
# AppVeyor...
|
||||
known-standard-library=faulthandler,http,enum,tokenize,posixpath,importlib,types
|
||||
known-third-party=sip
|
||||
# MsgType added as WORKAROUND for
|
||||
# https://bitbucket.org/logilab/pylint/issues/690/
|
||||
# UnsetObject because pylint infers any objreg.get(...) as UnsetObject.
|
||||
ignored-classes=qutebrowser.utils.objreg.UnsetObject,
|
||||
qutebrowser.browser.webkit.webelem.WebElementWrapper,
|
||||
scripts.dev.check_coverage.MsgType
|
||||
|
||||
72
.travis.yml
@@ -1,52 +1,31 @@
|
||||
sudo: false
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: python
|
||||
group: edge
|
||||
python: 3.6
|
||||
language: generic
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: TESTENV=py34-cov
|
||||
- os: linux
|
||||
env: DOCKER=debian-jessie
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=archlinux
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=archlinux-webengine QUTE_BDD_WEBENGINE=true
|
||||
env: DOCKER=ubuntu-wily
|
||||
services: docker
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt571
|
||||
- os: linux
|
||||
python: 3.5
|
||||
env: TESTENV=py35-pyqt571
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt59
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt510
|
||||
- os: linux
|
||||
env: TESTENV=py36-pyqt511-cov
|
||||
# https://github.com/travis-ci/travis-ci/issues/9069
|
||||
- os: linux
|
||||
python: 3.7
|
||||
sudo: required
|
||||
dist: xenial
|
||||
env: TESTENV=py37-pyqt511
|
||||
env: DOCKER=ubuntu-xenial
|
||||
services: docker
|
||||
- os: osx
|
||||
env: TESTENV=py37 OSX=sierra
|
||||
osx_image: xcode9.2
|
||||
language: generic
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/2013
|
||||
# - os: osx
|
||||
# env: TESTENV=py35 OSX=yosemite
|
||||
# osx_image: xcode6.4
|
||||
env: TESTENV=py35
|
||||
- os: linux
|
||||
env: TESTENV=pylint PYTHON=python3.6
|
||||
env: TESTENV=pylint
|
||||
- os: linux
|
||||
env: TESTENV=flake8
|
||||
- os: linux
|
||||
env: TESTENV=docs
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- asciidoc
|
||||
- os: linux
|
||||
env: TESTENV=vulture
|
||||
- os: linux
|
||||
@@ -57,27 +36,21 @@ matrix:
|
||||
env: TESTENV=check-manifest
|
||||
- os: linux
|
||||
env: TESTENV=eslint
|
||||
language: node_js
|
||||
python: null
|
||||
node_js: "lts/*"
|
||||
- os: linux
|
||||
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
|
||||
- os: osx
|
||||
env: TESTENV=py35
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
- $HOME/build/qutebrowser/qutebrowser/.cache
|
||||
- $HOME/build/The-Compiler/qutebrowser/.cache
|
||||
|
||||
before_install:
|
||||
# We need to do this so we pick up the system-wide python properly
|
||||
- 'export PATH="/usr/bin:$PATH"'
|
||||
|
||||
install:
|
||||
- bash scripts/dev/ci/travis_install.sh
|
||||
- ulimit -c unlimited
|
||||
|
||||
script:
|
||||
- bash scripts/dev/ci/travis_run.sh
|
||||
@@ -85,16 +58,13 @@ script:
|
||||
after_success:
|
||||
- '[[ $TESTENV == *-cov ]] && codecov -e TESTENV -X gcov'
|
||||
|
||||
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
|
||||
- "chat.freenode.net#qutebrowser"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
skip_join: true
|
||||
template:
|
||||
|
||||
904
CHANGELOG.asciidoc
Normal file
@@ -0,0 +1,904 @@
|
||||
Change Log
|
||||
===========
|
||||
|
||||
// http://keepachangelog.com/
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to http://semver.org/[Semantic Versioning].
|
||||
|
||||
// tags:
|
||||
// `Added` for new features.
|
||||
// `Changed` for changes in existing functionality.
|
||||
// `Deprecated` for once-stable features removed in upcoming releases.
|
||||
// `Removed` for deprecated features removed in this release.
|
||||
// `Fixed` for any bug fixes.
|
||||
// `Security` to invite users to upgrade in case of vulnerabilities.
|
||||
|
||||
v0.8.1
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fix crash when pressing enter without a command
|
||||
- Adjust error message to point out QtWebEngine is unsupported with the OS
|
||||
X .app currently.
|
||||
- Hide Harfbuzz warning with the OS X .app
|
||||
|
||||
v0.8.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New `:repeat-command` command (mapped to `.`) to repeat the last command.
|
||||
Note that two former default bundings conflict with that binding, unbinding
|
||||
them via `:unbind .i` and `:unbind .o` is recommended.
|
||||
- New `qute:bookmarks` page which displays all bookmarks and quickmarks.
|
||||
- New `:prompt-open-download` (bound to `Ctrl-X`) which can be used to open a
|
||||
download directly when getting the filename prompt.
|
||||
- New `{host}` replacement for tab- and window titles which evaluates
|
||||
to the current host.
|
||||
- New default binding `;t` for `:hint input`.
|
||||
- New variables `$QUTE_CONFIG_DIR`, `$QUTE_DATA_DIR` and
|
||||
`$QUTE_DOWNLOAD_DIR` available for userscripts.
|
||||
- New option `ui` -> `status-position` to configure the position of the
|
||||
status bar (top/bottom).
|
||||
- New `--pdf <filename>` argument for `:print` which can be used to generate a
|
||||
PDF without a dialog.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- `:scroll-perc` now prefers a count over the argument given to it, which means
|
||||
`gg` can be used with a count.
|
||||
- Aliases can now use `;;` to have an alias which executed multiple commands.
|
||||
- `:edit-url` now does nothing if the URL isn't changed in the spawned editor.
|
||||
- `:bookmark-add` can now be passed a URL and title to add that as a bookmark
|
||||
rather than the current page.
|
||||
- New `taskadd` userscript to add a taskwarrior task annotated with the
|
||||
current URL.
|
||||
- `:bookmark-del` and `:quickmark-del` now delete the current page's URL if none
|
||||
is given.
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
- Compatibility with PyQt 5.7
|
||||
- Fixed some configuration values being lost when a config option gets removed
|
||||
from qutebrowser's code.
|
||||
- Fix crash when downloading with a full disk
|
||||
- Using `:jump-mark` (e.g. `''`) when the current URL is invalid doesn't crash
|
||||
anymore.
|
||||
|
||||
Removed
|
||||
-------
|
||||
|
||||
- The ability to display status messages from webpages, as well as the related
|
||||
`ui -> display-statusbar-messages` setting.
|
||||
- The `general -> wrap-search` setting as searches now always wrap.
|
||||
According to a quick straw poll and prior crash logs, almost nobody is using
|
||||
`wrap-search = false`, and turning off wrapping is not possible with
|
||||
QtWebEngine.
|
||||
- `:edit-url` now doesn't accept a count anymore as its behavior was confusing
|
||||
and it doesn't make much sense to add a count.
|
||||
|
||||
v0.7.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New `:edit-url` command to edit the URL in an external editor.
|
||||
- New `network -> custom-headers` setting to send custom headers with every request.
|
||||
- New `{url:pretty}` commandline replacement which gets replaced by the decoded URL.
|
||||
- New marks to remember a scroll position:
|
||||
- New `:jump-mark` command to jump to a mark, bound to `'`
|
||||
- New `:set-mark` command to set a mark, bound to ```(backtick)
|
||||
- The `'` mark gets set when moving away (hinting link with anchor, searching, etc.) so you can move back with `''`
|
||||
- New `--force-color` argument to force colored logging even if stdout is not a
|
||||
terminal
|
||||
- New `:messages` command to show error messages
|
||||
- New pop-up showing possible keybinding when the first key of a keychain is
|
||||
pressed. This can be turned off using `:set ui keyhint-blacklist *`.
|
||||
- New `hints -> auto-follow-timeout` setting to ignore keypresses after
|
||||
following a hint when filtering in number mode.
|
||||
- New `:history-clear` command to clear the entire history
|
||||
- New `hints -> find-implementation` to select which implementation (JS/Python)
|
||||
should be used to find hints on a page. The `javascript` implementation is
|
||||
better, but slower.
|
||||
- New `inputs` group for `:hint` to hint text input fields.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- qutebrowser got a new (slightly updated) logo
|
||||
- `:tab-focus` can now take a negative index to focus the nth tab counted from
|
||||
the right.
|
||||
- `:yank` can now yank the pretty/decoded URL by adding `--pretty`
|
||||
- `:navigate` now clears the URL fragment
|
||||
- `:completion-item-del` (`Ctrl-D`) can now be used in `:buffer` completion to
|
||||
close a tab
|
||||
- Counts can now be used with special keybindings (e.g. with modifiers)
|
||||
- Various SSL ciphers are now disabled by default. With recent Qt/OpenSSL
|
||||
versions those already all are disabled, but with older versions they might
|
||||
not be.
|
||||
- Show favicons as window icon with `tabs-are-windows` set.
|
||||
- `:bind <key>` without a command now shows the existing binding
|
||||
- The optional `colorlog` dependency got removed, as qutebrowser now displays
|
||||
colored logs without it.
|
||||
- URLs are now shown decoded when hovering.
|
||||
- Keybindings are now shown in the command completion
|
||||
- Improved behavior when pasting multiple lines
|
||||
- Rapid hints can now also be used for the `normal` hint target, which can be
|
||||
useful with javascript click handlers or checkboxes which don't actually open
|
||||
a new page.
|
||||
- `:zoom-in` or `:zoom-out` (`+`/`-`) with a too large count now zooms to the
|
||||
smallest/largest zoom instead of doing nothing.
|
||||
- The commandline now accepts partially typed commands if they're unique.
|
||||
- Number hints are now kept filtered after following a hint in rapid mode.
|
||||
- Number hints are now renumbered after filtering
|
||||
- Number hints can now be filtered with multiple space-separated search terms
|
||||
- `hints -> scatter` is now ignored for number hints
|
||||
- Better history implementation which also stores titles.
|
||||
As a consequence, URLs which redirect to another URL are now added to the
|
||||
history too, marked with a `-r` suffix to the timestamp field.
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
- Fixed using `:hint links spawn` with flags - you can now use things like the
|
||||
`-v` argument for `:spawn` or pass flags to the spawned commands.
|
||||
- Various fixes for hinting corner-cases where following a link didn't work or
|
||||
the hint was drawn at the wrong position.
|
||||
- Fixed crash when downloading from a URL with SSL errors
|
||||
- Close file handles correctly when a download failed
|
||||
- Fixed crash when using `;Y` (`:hint links yank-primary`) on a system without
|
||||
primary selection
|
||||
- Don't display quit confirmation with finished downloads
|
||||
- Fixed updating the tab index in the statusbar when opening a background tab
|
||||
- Fixed a crash when entering `:-- ` in the commandline
|
||||
- Fixed `:debug-console` with PyQt 5.6
|
||||
- Fixed qutebrowser not starting when `sys.stderr` is `None`
|
||||
- Fixed crash when cancelling a download which belongs to an MHTML download
|
||||
- Fixed rebinding of keybindings being case-sensitive
|
||||
- Fix for tab indicators getting lost when moving tabs
|
||||
- Fixed handling of backspace in number hinting mode
|
||||
- Fixed `FileNotFoundError` when starting in some cases on old Qt versions
|
||||
- Fixed sharing of cookies between tabs when `private-browsing` is enabled
|
||||
- Toggling values with `:set` now uses lower-case values
|
||||
- Hints now work with (non-standard) links with spaces around the URL
|
||||
- Strip off trailing spaces for history entries with no title
|
||||
|
||||
v0.6.2
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed crash when using `:tab-{prev,next,focus}` right after closing the last
|
||||
tab with `last-close` set to `close`.
|
||||
- Fixed crash when doing `:undo` in a new instance with `tabs -> last-close` set
|
||||
to `default-page`.
|
||||
- Fixed crash when starting with --cachedir=""
|
||||
- Fixed crash in some circumstances when using dictionary hints
|
||||
- Fixed various crashes related to PyQt 5.6
|
||||
|
||||
v0.6.1
|
||||
-----
|
||||
|
||||
Fixed
|
||||
~~~~~~
|
||||
|
||||
- Fixed broken cheatsheet image which was missing from package
|
||||
- Fixed occasional crash when switching/disconnecting monitors
|
||||
- Fixed crash when downloading non-ascii files with a broken locale (`LC_ALL=C`)
|
||||
- Added workaround for a Qt/PyQt bug which is too weird to describe here
|
||||
|
||||
v0.6.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New `:buffer` command to easily switch tabs by name. This is not bound to a
|
||||
key by default for existing users due to a conflict with the `gt`/`gT`
|
||||
bindings (which are now removed from the default bindings).
|
||||
You can bind it by hand by running `:bind -f gt set-cmd-text -s :buffer`.
|
||||
- New `--quiet` argument for the `:debug-pyeval` command to not open a tab with
|
||||
the results. Note `:debug-pyeval` is still only intended for debugging.
|
||||
- The completion now matches each entered word separately.
|
||||
- A new command `:paste-primary` got added to paste the primary selection, and
|
||||
`<Shift-Insert>` got added as a binding so it pastes primary rather than
|
||||
clipboard.
|
||||
- New mode `word` for `hints -> mode` which uses a dictionary and link-texts
|
||||
for hints instead of single characters.
|
||||
- New `--all` argument for `:download-cancel` to cancel all running downloads.
|
||||
- New `password_fill` userscript to fill passwords using the `pass` executable.
|
||||
- New `current` hinting mode which forces opening hints in the current tab
|
||||
(even with `target="_blank"`)
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Pasting multiple lines via `:paste` now opens each line in a new tab.
|
||||
- `:navigate increment/decrement` now preserves leading zeroes in URLs.
|
||||
- `general -> editor` can now also handle `{}` inside another argument (e.g. to open `vim` via `termite`)
|
||||
- Improved performance when scrolling with many tabs open.
|
||||
- Shift-Insert now also pastes primary selection for prompts.
|
||||
- `:download-remove --all` got un-deprecated to provide symmetry with
|
||||
`:download-cancel --all`. It does the same as `:download-clear`.
|
||||
- Improved detection of URLs/search terms when pasting multiple lines.
|
||||
- Don't remove `qutebrowser-editor-*` temporary file if editor subprocess crashed
|
||||
- Userscripts are also searched in `/usr/share/qutebrowser/userscripts`.
|
||||
- Blocked hosts are now also read from a `blocked-hosts` file in the config dir
|
||||
(e.g. `~/.config/qutebrowser/blocked-hosts`).
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed starting with -c "".
|
||||
- Fixed crash when a tab is closed twice via javascript (e.g. Dropbox
|
||||
authentication dialogs)
|
||||
- Fixed crash when a notification/geolocation prompt is answered after closing
|
||||
the tab it belongs to.
|
||||
- Fixed crash when downloading a file without any path information (e.g a
|
||||
magnet link).
|
||||
- Fixed crashes when opening an empty URL (e.g. via pasting).
|
||||
- Fixed validation of duplicate values in `hints -> chars`.
|
||||
- Fixed crash when PDF.js was partially installed.
|
||||
- Fixed crash when XDG_DOWNLOAD_DIR was not an absolute path.
|
||||
- Fixed very long filenames when downloading `data://`-URLs.
|
||||
- Fixed ugly UI fonts on Windows when Liberation Mono is installed
|
||||
- Fixed crash when unbinding key from a section which doesn't exist in the config
|
||||
- Fixed report window after a segfault
|
||||
- Fixed some directory browser issues on Windows
|
||||
- Fixed crash when closing a window with a finished download and delayed
|
||||
`remove-finished-downloads` setting.
|
||||
- Fixed crash when hitting `<Tab>` then `<Ctrl-C>` on pages without keyboard
|
||||
focus.
|
||||
- Fixed "Frame load interrupted by policy change" error showing up when
|
||||
downloading files with Qt 5.6.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- The `gt`/`gT` bindings (luakit-like alternatives to `J`/`K`) were removed
|
||||
(except for existing configs) to make room for the `gt` binding to show
|
||||
buffers.
|
||||
|
||||
v0.5.1
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed completion for various config values when using `:set`.
|
||||
- Fixed config validation for various config values.
|
||||
- Prevented an error being logged when a website with HTTP authentication was
|
||||
opened on Windows.
|
||||
|
||||
v0.5.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- Ability to preview PDFs using pdf.js in the browser if it's installed. This
|
||||
is disabled by default and can be enabled using the
|
||||
`content -> pdfjs-enabled` setting.
|
||||
- New setting `ui -> hide-wayland-decoration` to hide the window decoration
|
||||
when using wayland.
|
||||
- New userscripts in `misc/userscripts`:
|
||||
- `open_download` to easily open a file in your downloads folder.
|
||||
- `view_in_mpv` to open a video in mpv and remove it from the page.
|
||||
- `qutedmenu` and `dmenu_qutebrowser` to select URLs via dmenu
|
||||
- New setting `content -> host-blocking-whitelist` to whitelist certain domains
|
||||
from the adblocker.
|
||||
- `{scroll_pos}` can now be used in `ui -> window-title-format` and
|
||||
`tabs -> title-format`.
|
||||
- New setting `general -> url-incdec-segments` to configure which segments of
|
||||
the URL should be affected by `:navigate increment/decrement`.
|
||||
- New `--target` argument to specify how URLs should be opened in an existing
|
||||
instance.
|
||||
- New setting `statusbar.url.fg.success.https` to set the foreground color for
|
||||
the URL when a page was loaded via HTTPS.
|
||||
- The scrollbar in the completion is now styled, and the following new options
|
||||
got added:
|
||||
* `completion -> scrollbar-width`
|
||||
* `completion -> scrollbar-padding`
|
||||
* `colors -> completion.scrollbar.fg`
|
||||
* `colors -> completion.scrollbar.bg`
|
||||
- New value `none` for options taking a color system so they don't display a
|
||||
gradient:
|
||||
* `colors -> tabs.indicator.system`
|
||||
* `colors -> downloads.fg.system`
|
||||
* `colors -> downloads.bg.system`
|
||||
- New command `:download-retry` to retry a failed download.
|
||||
- New command `:download-clear` which replaces `:download-remove --all`.
|
||||
- `:set-cmd-text` has a new `--append` argument to append to the current
|
||||
statusbar text.
|
||||
- qutebrowser now uses `~/.netrc` if available to authenticate via HTTP.
|
||||
- New `:fake-key` command to send a fake keypress to a website or to
|
||||
qutebrowser.
|
||||
- New `--mhtml` argument for `:download` to download a page including all
|
||||
ressources as MHTML file.
|
||||
- New option `tabs -> title-alignment` to change the alignment of tab titles.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- The `colors -> tabs.bg/fg.selected` option got split into
|
||||
`tabs.bg/fg.selected.odd/even`.
|
||||
- `:spawn --userscript` and `:hint` with the `userscript` target now look up
|
||||
relative paths in `~/.local/share/qutebrowser/userscripts` or
|
||||
`$XDG_DATA_DIR`. Using a binary in `$PATH` won't work anymore with
|
||||
`--userscript`.
|
||||
- New design for error pages
|
||||
- Link filtering for hints now checks if the text is contained anywhere in
|
||||
the link, and matches case-insensitively.
|
||||
- The `ui -> remove-finished-downloads` option got changed to an integer and
|
||||
now takes a time (in milliseconds) to keep the download around after it's
|
||||
finished. When set to `-1`, downloads are never removed.
|
||||
- The `:follow-hint` command now optionally takes the keystring of a hint to
|
||||
follow.
|
||||
- `:scroll-px` now doesn't take floats anymore, which made little sense.
|
||||
- Updated the user agent list for the `:set network user-agent` completion.
|
||||
- Starting with `--debug` doesn't log `VDEBUG` messages anymore (add
|
||||
`--loglevel VDEBUG` to get them).
|
||||
- `:debug-console` now hides the console if it's already shown.
|
||||
- `:yank-selected` now doesn't log the selected text anymore.
|
||||
- `general -> log-javascript-console` got changed from a boolean to an option
|
||||
taking a loglevel (`none`, `info`, `debug`).
|
||||
- `:tab-move +/-` now wraps around if `tabs -> wrap` is `true`.
|
||||
- When a subprocess (like launched by `:spawn`) fails, its stdout/stderr is now
|
||||
logged to the console.
|
||||
- A search engine name can now contain any non-space character, like dashes.
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- `:download-remove --all` is now deprecated and `:download-clear` should be
|
||||
used instead.
|
||||
- `:download <url> <destination>` is now deprecated and
|
||||
`:download --dest <destination> <url>` should be used instead.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- `:scroll` with two pixel-arguments (deprecated in v0.3.0)
|
||||
- The `:run-userscript` command (deprecated in v0.2.0)
|
||||
- The `rapid` and `rapid-win` targets for `:hint` (deprecated in v0.2.0)
|
||||
- The `:cancel-download` command (deprecated in v0.2.0)
|
||||
- The `:download-page` command (deprecated in v0.2.0)
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed retrying of downloads which were started in a now closed tab.
|
||||
- Fixed displaying of web history if `web-history-max-items` is set to -1.
|
||||
- Cloned tabs now don't display favicons anymore if show-favicons is False.
|
||||
- Fixed a crash when clicking a bookmark name and pressing `Ctrl-D`.
|
||||
- Fixed a crash when a website presents a very small favicon.
|
||||
- Fixed prompting for download directory when
|
||||
`storage -> prompt-download-directory` was unset.
|
||||
- Fixed crash when using `:follow-hint` outside of hint mode.
|
||||
- Fixed crash when using `:set foo bar?` with invalid section/option.
|
||||
- Fixed scrolling to the very left/right with `:scroll-perc`.
|
||||
- Using an external editor should now work correctly with some funny chars
|
||||
(U+2028/U+2029/BOM).
|
||||
- Movements in caret mode now should work correctly on OS X and Windows.
|
||||
- Fixed upgrade from earlier config versions.
|
||||
- Fixed crash when killing a running userscript.
|
||||
- Fixed characters being passed through when shifted with
|
||||
`forward-unbound-keys` set to `auto`.
|
||||
- Fixed restarting after a crash is reported.
|
||||
- Removed `.pyc` files accidentally contained in source releases.
|
||||
|
||||
v0.4.1
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Adjusted AppArmor config for the IPC changes in v0.4.0.
|
||||
- Fixed atime update frequency for IPC file.
|
||||
- Worked around a Qt issue where middle-clicking caused scrolling with a
|
||||
touchpad to restart at the beginning of the page.
|
||||
- The `completion -> web-history-max-items` setting is now also respected for
|
||||
items added after starting qutebrowser.
|
||||
- Search terms are now shared between different tabs again
|
||||
- Tests (a reduced subset of them) now run correctly again when DISPLAY is not
|
||||
set.
|
||||
- Fixed an issue causing qutebrowser to crash with Python 3.5 as soon as an ad
|
||||
was blocked.
|
||||
- Fixed an issue causing qutebrowser to not start with more recent Python 3.4
|
||||
versions (e.g. on Debian experimental).
|
||||
- Fixed various `PendingDeprecationWarnings` shown with Python 3.5.
|
||||
|
||||
v0.4.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New bookmark functionality (similar to quickmarks without a name).
|
||||
* New command `:bookmark-add` to bookmark the current page (bound to `M`).
|
||||
* New command `:bookmark-load` to load a bookmark (bound to `gb`/`gB`/`wB`).
|
||||
- New (hidden) command `:completion-item-del` (bound to `<Ctrl-D>`) to delete
|
||||
the current item in the completion (for quickmarks/bookmarks).
|
||||
- New settings `tabs -> padding` and `tabs -> indicator-tabbing` to control the
|
||||
size/padding of the tabbar.
|
||||
- New setting `ui -> statusbar-padding` to control the size/padding of the
|
||||
status bar.
|
||||
- New setting `network -> referer-header` to configure when the referer should
|
||||
be sent (by default it's only sent while on the same domain).
|
||||
- New setting `tabs -> show` which supersedes the old `tabs -> hide-*` options
|
||||
and has an additional `switching` option which shows tab while switching
|
||||
them. There's also a new `show-switching` option to configure the timeout.
|
||||
- New setting `storage -> remember-download-directory` to remember the last
|
||||
used download directory.
|
||||
- New setting `storage -> prompt-download-directory` to download all downloads
|
||||
without asking.
|
||||
- Rapid hinting is now also possible for downloads.
|
||||
- Directory browsing via `file://` is now supported.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Some developer scripts got moved to `scripts/dev/`
|
||||
- When downloading to a FIFO or special file, a confirmation is displayed as
|
||||
this might cause qutebrowser to hang.
|
||||
- The `:yank-selected` command now works in all modes instead of just caret
|
||||
mode and is not hidden anymore.
|
||||
- `minimal_webkit_testbrowser.py` now has a `--webengine` switch to test
|
||||
QtWebEngine if it's installed.
|
||||
- The column width percentages for the completion view now depend on the
|
||||
completion model.
|
||||
- The values for `tabs -> position` and `ui -> downloads-position` got changed
|
||||
from `north`/`south`/`west/`east` to `top`/`bottom`/`left`/`right`. Existing
|
||||
configs should be adjusted automatically.
|
||||
- `:tab-focus`/`gt` now behaves like `:tab-next` if no count/index is given.
|
||||
- The completion widget doesn't show a border anymore.
|
||||
- The tabbar doesn't display ugly arrows anymore if there isn't enough space
|
||||
for all tabs.
|
||||
- Some insignificant Qt warnings which were printed on OS X are now hidden.
|
||||
- Better support for Qt 5.5 and Python 3.5.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed a bug where cookies were saved despite qutebrowser being started in
|
||||
private browsing mode.
|
||||
- The local socket used for inter-process communication (opening new instances)
|
||||
is now ensured to only be accessible by the user on all operating systems.
|
||||
- Various corner cases for inter-process communication issues got fixed.
|
||||
- `link_pyqt.py` now should work better on untested distributions.
|
||||
- Fixed various corner-cases with crashes when reading invalid config values
|
||||
and the history file.
|
||||
- Fixed various corner-cases when setting text via an external editor.
|
||||
- Fixed potential crash when hinting a text field.
|
||||
- Fixed entering of insert mode when certain disabled text fields were clicked.
|
||||
- Fixed a crash when using `:set` with `-p` and `!` (invert value)
|
||||
- Downloads with unknown size are now handled correctly.
|
||||
- `:navigate increment/decrement` (`<Ctrl-A>`/`<Ctrl-X>`) now handles some
|
||||
corner-cases better.
|
||||
- Fixed a bug where the completion got affected by another window's completion
|
||||
if it was open in both windows.
|
||||
- Fixed a performance issue with large histories when opening previously
|
||||
unvisited websites.
|
||||
- The progress bar now doesn't cause the statusbar to change it's height
|
||||
anymore.
|
||||
- `~` is now always expanded when spawning a script.
|
||||
- Fixed various corner cases when opening links in an existing instance.
|
||||
- Fixed a race-condition causing an exception when starting qutebrowser.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- The `tabs -> indicator-space` setting got removed as the new padding settings
|
||||
should be used instead.
|
||||
- The `tabs -> hide-always` and `tabs -> hide-auto` settings got merged into
|
||||
the new `tabs -> show` setting.
|
||||
|
||||
v0.3.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New commands `:message-info`, `:message-error` and `:message-warning` to show messages in the statusbar, e.g. from a userscript.
|
||||
- New command `:scroll-px` which replaces `:scroll` for pixel-exact scrolling.
|
||||
- New command `:jseval` to run a javascript snippet on the current page.
|
||||
- New (hidden) command `:follow-selected` (bound to `Enter`/`Ctrl-Enter` by default) to follow the link which is currently selected (e.g. after searching via `/`).
|
||||
- New (hidden) command `:clear-keychain` to clear a partially entered keychain (bound to `<Escape>` by default, in addition to clearing search).
|
||||
- New setting `ui -> smooth-scrolling`.
|
||||
- New setting `content -> webgl` to enable/disable https://www.khronos.org/webgl/[WebGL].
|
||||
- New setting `content -> css-regions` to enable/disable support for http://dev.w3.org/csswg/css-regions/[CSS Regions].
|
||||
- New setting `content -> hyperlink-auditing` to enable/disable support for https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing[hyperlink auditing].
|
||||
- New setting `tabs -> mousewheel-tab-switching` to control mousewheel behavior on the tab bar.
|
||||
- New arguments `--datadir` and `--cachedir` to set the data/cache location.
|
||||
- New arguments `--basedir` and `--temp-basedir` (intended for debugging) to set a different base directory for all data, which allows multiple invocations.
|
||||
- New argument `--no-err-windows` to suppress all error windows.
|
||||
- New arguments `--top-navigate` and `--bottom-navigate` (`-t`/`-b`) for `:scroll-page` to specify a navigation action (e.g. automatically go to the next page when arriving at the bottom).
|
||||
- New flag `-d`/`--detach` for `:spawn` to detach the spawned process so it's not closed when qutebrowser is.
|
||||
- New flag `-v`/`--verbose` for `:spawn` to print informations when the process started/exited successfully.
|
||||
- Many new color settings (foreground setting for every background setting).
|
||||
- New setting `ui -> modal-js-dialog` to use the standard modal dialogs for javascript questions instead of using the statusbar.
|
||||
- New setting `colors -> webpage.bg` to set the background color to use for websites which don't set one.
|
||||
- New setting `completion -> auto-open` to only open the completion when tab is pressed (if set to false).
|
||||
- New visual/caret mode (bound to `v`) to select text by keyboard.
|
||||
- There are now some example userscripts in `misc/userscripts`.
|
||||
- Support for Qt 5.5 and tox 2.0
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- *Breaking change for userscripts:* `QUTE_HTML` and `QUTE_TEXT` for userscripts now don't store the contents directly, and instead contain a filename.
|
||||
- The `content -> geolocation` and `notifications` settings now support a `true` value to always allow those. However, this is *not recommended*.
|
||||
- New bindings `<Ctrl-R>` (rapid), `<Ctrl-F>` (foreground) and `<Ctrl-B>` (background) to switch hint modes while hinting.
|
||||
- `<Ctrl-M>` and numpad-enter are now bound by default for bindings where `<Return>` was bound.
|
||||
- `:hint tab` and `F` now respect the `background-tabs` setting. To enforce a foreground tab (what `F` did before), use `:hint tab-fg` or `;f`.
|
||||
- `:scroll` now takes a direction argument (`up`/`down`/`left`/`right`/`top`/`bottom`/`page-up`/`page-down`) instead of two pixel arguments (`dx`/`dy`). The old form still works but is deprecated.
|
||||
- The `ui -> user-stylesheet` setting now also takes file paths relative to the config directory.
|
||||
- The `content -> cookies-accept` setting now has new `no-3rdparty` (default) and `no-unknown-3rdparty` values to block third-party cookies. The `default` value got renamed to `all`.
|
||||
- Improved startup time by reading the webpage history while qutebrowser is open.
|
||||
- The way `:spawn` splits its commandline has been changed slightly to allow commands with flags.
|
||||
- The default for the `new-instance-open-target` setting has been changed to `tab`.
|
||||
- Sessions now store zoom/scroll-position separately for each entry.
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- `:scroll` with two pixel-arguments is now deprecated - `:scroll-px` should be used instead.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- The `--no-crash-dialog` argument which was intended for debugging only was removed as it's replaced by `--no-err-windows` which suppresses all error windows.
|
||||
- Support for Qt installations without SSL support was dropped.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Scrolling should now work more reliably on some pages where arrow keys worked but `hjkl` didn't.
|
||||
- Small improvements when checking if an input is a URL or not.
|
||||
- Fixed wrong cursor position when completing the first item in the completion.
|
||||
- Fixed exception when using search engines with {foo} in their name.
|
||||
- Fixed a bug where the same title was shown for all tabs on some systems.
|
||||
- Don't install the scripts package when installing qutebrowser.
|
||||
- Fixed searching for terms starting with a hyphen (e.g. `/-foo`)
|
||||
- Proxy authentication credentials are now remembered between different tabs.
|
||||
- Fixed updating of the tab title on pages without title.
|
||||
- Fixed AssertionError when closing many windows quickly.
|
||||
- Various fixes for deprecated key bindings and auto-migrations.
|
||||
- Workaround for qutebrowser not starting when there are NUL-bytes in the history (because of a currently unknown bug).
|
||||
- Fixed handling of keybindings containing Ctrl/Meta on OS X.
|
||||
- Fixed crash when downloading a URL without filename (e.g. magnet links) via "Save as...".
|
||||
- Fixed exception when starting qutebrowser with `:set` as argument.
|
||||
- Fixed horrible completion performance when the `shrink` option was set.
|
||||
- Sessions now store zoom/scroll-position correctly.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.2.1[v0.2.1]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Added missing manpage (doc/qutebrowser.1.asciidoc) to archive.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.2.0[v0.2.0]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- Session support
|
||||
* new command `:session-load` to load a session.
|
||||
* new command `:session-save` to save a session.
|
||||
* new command `:session-delete` to delete a session.
|
||||
* new setting `general -> save-session` to always save the session on quit.
|
||||
* new setting `general -> session-default-name` to configure the session name to use if none is given.
|
||||
* new argument `-r`/`--restore` to specify a session to load.
|
||||
* new argument `-R`/`--override-restore` to not load a session even if one was saved.
|
||||
- New commands to manage downloads:
|
||||
* `:download` to download a URL or the current page.
|
||||
* `:download-cancel` to cancel a download.
|
||||
* `:download-delete` to delete a download from disk.
|
||||
* `:download-open` to open a finished download.
|
||||
* `:download-remove` to remove a download from the list. `:download-remove --all` or the new 'cd' keybinding can be used to clear all finished downloads.
|
||||
- History completion
|
||||
* New option `completion -> timestamp-format` to set the format used to display the history timestamps.
|
||||
* New option `completion -> web-history-max-items` to configure how many history items to show in the completion.
|
||||
* The option `completion -> history-length` for the command history got renamed to `cmd-history-max-items`.
|
||||
- Better save logic for the config/state:
|
||||
* Only save files if modified (e.g. don't overwrite the config if it was edited outside of qutebrowser and nothing was changed in qutebrowser).
|
||||
* Save things (cookies, config, quickmarks, ...) periodically all 15 seconds (time can be changed with the `general -> auto-save-interval` option).
|
||||
- Opera-like mouse rocker gestures
|
||||
* New option `input -> rocker-gestures`. When turned on, the history can be navigated back/forward by holding a mouse button and pressing the other one.
|
||||
- New `-f` option for `:reload` to reload and bypass the cache.
|
||||
- Pass more information (`QUTE_MODE`, `QUTE_SELECTED_TEXT`, `QUTE_SELECTED_HTML`, `QUTE_USER_AGENT`, `QUTE_HTML`, `QUTE_TEXT`) to userscripts.
|
||||
- New `--userscript` option to `:spawn` (which deprecates `:run-userscript`).
|
||||
- Ability to toggle a value to `:set` by appending a `!` to the value.
|
||||
- New options to hide the tab-/statusbar:
|
||||
* `tabs -> hide-always` for the tabbar
|
||||
* `ui -> hide-statusbar` for the statusbar
|
||||
- New options to configure how the tab/window titles should look:
|
||||
* `tabs -> title-format` for the tabbar
|
||||
* `ui -> window-title-format` for the window title
|
||||
- HTML5 Geolocation/Notification support:
|
||||
* New option `content -> geolocation` to permanently turn the geolocation off.
|
||||
* New option `content -> notifications` to permanently turn notifications off.
|
||||
- New options to disable javascript prompts/alerts:
|
||||
* `content -> ignore-javascript-prompt` to turn off prompts.
|
||||
* `content -> ignore-javascript-alerts` to turn off alerts.
|
||||
- Two new options to customize the behavior of hints:
|
||||
* `hints -> min-chars` to set minimum number of chars in hints.
|
||||
* `hints -> scatter` which when turned off distributes the hints sequentially (like dwb) instead of scattering their positions (like Vimium).
|
||||
- Make it possible to use `:open -[twb]` without url.
|
||||
* New option `general -> default-page` to set the page to be opened when doing that.
|
||||
- New `input -> partial-timeout` option to clear partial keystrings.
|
||||
- New option `completion -> download-path-suggestion` to configure what to show in the completion for downloads.
|
||||
- Queue messages shown in unfocused windows and show them when the window is focused.
|
||||
* New option `ui -> message-unfocused` to disable this behavior.
|
||||
- New `--relaxed-config` argument which ignores unknown options.
|
||||
- New `:tab-detach` command to open the current tab in a new window.
|
||||
- Zooming via Ctrl-Mousewheel.
|
||||
* New option `input -> mouse-zoom-divider` to control how much the page is zoomed when rotating the wheel.
|
||||
- New option (`content -> host-blocking-enabled`) to enable/disable host blocking.
|
||||
- New values `tab-bg`/`tab-bg-silent` for `new-instance-open-target` to open a background tab.
|
||||
- New `ui -> downloads-position` setting to move the downloads to the bottom.
|
||||
- New `ui -> hide-mouse-cursor` option to hide the mouse cursor inside qutebrowser.
|
||||
- New argument `-s` for qutebrowser to set a temporary config option.
|
||||
- New argument `-p` for the `:set` command to print the new value.
|
||||
- New `--rapid` option to `:hint`. The `rapid`/`rapid-win` targets are now deprecated, and `--rapid` can be used as well with the targets run/hover/userscript/spawn as well.
|
||||
- New `-f` argument to `:bind` to overwrite the old binding.
|
||||
- New `--qt-name` argument to qutebrowser which is passed to Qt to set `WM_CLASS`.
|
||||
- Alternating row colors in completion. This adds a new `colors -> completion.alternate-bg` option.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Ignore quotes with maxsplit-commands (`:open`, `:quickmark-load`, etc.) and don't quote arguments for those commands in the completions. This also means some commands needed adjustments:
|
||||
* Clear search when `:search` without arguments is given. (`:search ""` will now search for the literal text `""`)
|
||||
* Add `-s`/`--space` argument to `:set-cmd-text` (as `:set-cmd-text "foo "` will now set the literal text `"foo "`)
|
||||
- Ignore `;;` for splitting with some commands like `:bind`.
|
||||
- Add unbound (new) default keybindings to config. This also adds a new `<unbound>` special command.
|
||||
* To unbind a command keybinding without binding it to a new key, you now have to bind it to `<unbound>` or it'll be readded automatically.
|
||||
- If an SSL error is raised multiple times with the same error/certificate/host/scheme/port, the user is only asked once.
|
||||
- Jump to last instead of first item when pressing Shift-Tab the first time in the completion.
|
||||
- Add a fullscreen keybinding.
|
||||
- Add a `:search` command in addition to `/foo` so it's more visible and can be used from scripts.
|
||||
- Various improvements to documentation, logging, and the crash reporter.
|
||||
- Expand `~` to the users home directory with `:run-userscript`.
|
||||
- Improve the userscript runner on Linux/OS X by using `QSocketNotifier`.
|
||||
- Add luakit-like `gt`/`gT` keybindings to cycle through tabs.
|
||||
- Show default value for config values in the completion.
|
||||
- Clone tab icon, tab text and zoom level when cloning tabs.
|
||||
- Don't open relative file paths with `:open`, only with commandline arguments.
|
||||
- Expand environment variables in config settings which take a file path.
|
||||
- Add a list of common user agents to the user agent setting completion.
|
||||
- Move cursor to end of textboxes when hinting.
|
||||
- Don't start searches on invalid URLs for quickmarks/startpage.
|
||||
- Various performance improvements for the completion.
|
||||
- Always open URLs given as argument in the foreground.
|
||||
- Improve various error messages.
|
||||
- Add `startpage`/`default-page` values to `tabs -> last-close`.
|
||||
- Various improvements to `:restart` - it should be more robust now and uses sessions so all state (focused tab, scroll position, etc.) gets remembered.
|
||||
- Add tab index display to the statusbar.
|
||||
- Keep progress bar height fixed when the statusbar is multiline.
|
||||
- Many improvements to tests and related infrastructure:
|
||||
* `init_venv.py` and `run_checks.py` have been replaced by http://tox.readthedocs.org/[tox]. Install tox and run `tox -e mkvenv` instead.
|
||||
* The tests now use http://pytest.org/[pytest]
|
||||
* Many new tests added
|
||||
* Mac Mini buildbot to run the tests on OS X.
|
||||
* Coverage recording via http://nedbatchelder.com/code/coverage/[coverage.py].
|
||||
* New `--pdb-postmortem argument` to drop into the pdb debugger on exceptions.
|
||||
* Use https://github.com/ionelmc/python-hunter[hunter] for line tracing instead of a selfmade solution.
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- The `:run-userscript` command - use `:spawn --userscript` instead.
|
||||
- The `rapid` and `rapid-win` targets for `:hint` - use the `--rapid` argument to `:hint` instead.
|
||||
- The `:cancel-download` command - use `:download-cancel` instead.
|
||||
- The `:download-page` command - use `:download` instead.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- `init_venv.py` and `run_checks.py` have been replaced by http://tox.readthedocs.org/[tox]. Install tox and run `tox -e mkvenv` instead..
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fix for cache never being used.
|
||||
- Fixed handling of key release events (e.g. for javascript) when holding a key and pressing a second one.
|
||||
- Fix handling of commands using `;;` at various places (key config, command parser, `:bind`)
|
||||
- Fix splitting of flags with arguments (`:bind -m`/`--mode`).
|
||||
- Fix bindings of special keys with lower-case modifiers (e.g. `<ctrl-x>`)
|
||||
- Fix for weird search highlights when changing tabs while search is active.
|
||||
- Fix starting with `-c ""`.
|
||||
- Fix removing of partial downloads when a download is cancelled via context menu.
|
||||
- Fix retrying of downloads which were started in a now closed tab.
|
||||
- Highlight text case-insensitively in completion.
|
||||
- Scroll completion to top when showing it.
|
||||
- Handle unencodable file paths in config types correctly.
|
||||
- Fix for crash when executing a delayed command (because of a shadowed keybinding) and then unfocusing the window.
|
||||
- Fix for crash when hinting on a page which doesn't have a URL yet.
|
||||
- Fix exception when using `:set-cmd-text` with an empty argument.
|
||||
- Add a timeout to pastebin HTTP replies.
|
||||
- Various other fixes for small/rare bugs.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.1.4[v0.1.4]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
* The Windows builds come with Qt 5.4.1 which has some https://lists.schokokeks.org/pipermail/qutebrowser/2015-March/000054.html[related bugfixes].
|
||||
* Improvements to CPU usage when idle.
|
||||
* Ensure there's no size for `font-family` settings.
|
||||
* Handle URLs with double-colon as search strings.
|
||||
* Adjust prompt size hint based on content.
|
||||
* Refactor websettings and save/restore defaults.
|
||||
* Various small improvements to logging.
|
||||
* Various improvements for hinting.
|
||||
* Improve parsing of `faulthandler` logs.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
* Remove default search engines.
|
||||
* Remove debug console completing completely.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
* Ignore RuntimeError in `mouserelease_insertmode`.
|
||||
* Hide Qt warning when aborting download reply.
|
||||
* Hide "Error while shutting down tabs" message.
|
||||
* Clear open target in `acceptNavigationRequest`.
|
||||
* Fix handling of signals with deleted tabs.
|
||||
* Restore `sys.std*` in `utils.fake_io` on exceptions.
|
||||
* Allow font names with integers in them.
|
||||
* Fix `QIODevice` warnings when closing tabs.
|
||||
* Set the `QSettings` path to a config-subdirectory.
|
||||
* Add workaround for adblock-message without window.
|
||||
* Fix searching for terms starting with a slash.
|
||||
* Ignore tab key presses if they'd switch focus.
|
||||
|
||||
Security
|
||||
~~~~~~~~
|
||||
|
||||
* Stop the icon database from being created when private-browsing is set to true.
|
||||
* Disable insecure SSL ciphers.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.1.3[v0.1.3]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
* Various small logging improvements.
|
||||
* Don't open relative files in `fuzzy_url` with `:open`
|
||||
* Various crashdialog improvements.
|
||||
* Hide adblocked iframes.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
* Handle shutdown of page with prompt correctly.
|
||||
* fuzzy_url: handle invalid URLs with autosearch off
|
||||
* Handle explicit searches with `auto-search=false`.
|
||||
* Abort download override question on error/cancel.
|
||||
* Set a higher z-index for hint labels.
|
||||
* Close contextmenu when closing tab to avoid crash.
|
||||
* Fix statusbar quickly popping up as window.
|
||||
* Clean up `NetworkManager` after downloads finished.
|
||||
* Fix restoring of cmd widget after an error.
|
||||
* Fix retrying of downloads after the tab is closed.
|
||||
* Fix `check_libraries()` output for Arch Linux.
|
||||
* Handle all `IPCErrors` properly.
|
||||
* Handle another `webelem.IsNullError` with hints.
|
||||
* Handle `UnicodeDecodeError` when reading configs.
|
||||
|
||||
Security
|
||||
~~~~~~~~
|
||||
|
||||
* Fix for HTTP passwords accidentally being written to debug log.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.1.2[v0.1.2]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
* Uncheck sending of debug log by default when private browsing is on.
|
||||
* Add SSL info to version info.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
* Remove hosts-file.net from blocker default lists.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
* Fix rare exception when a key is pressed shortly after opening a window
|
||||
* Fix exception with certain invalid URLs like `http:foo:0`
|
||||
* Work around Qt bug which renders checkboxes on OS X unusable
|
||||
* Fix exception when a local files can't be read in `:adblock-update`
|
||||
* Hide 2 more Qt warnings.
|
||||
* Add `!important` to hint CSS so websites don't override the hint look
|
||||
* Make `init_venv.py` work with multiple sip `.so` files.
|
||||
* Fix splitting with certain commands with an empty argument
|
||||
* Fix uppercase hints.
|
||||
* Fix segfaults if another page is loaded while a prompt is open
|
||||
* Fix exception with invalid `ShellCommand` config values.
|
||||
* Replace unencodable chars
|
||||
* Fix user-stylesheet setting with an empty value.
|
||||
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.1.1[v0.1.1]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
* Set window icon and add a qutebrowser.ico file for Windows.
|
||||
* Ask the user when downloading to an already existing file.
|
||||
* Add a `network -> proxy-dns-requests` option.
|
||||
* Add "Remove finished" to the download context menu
|
||||
* Open and remove clicked downloads.
|
||||
|
||||
Changes
|
||||
~~~~~~~
|
||||
|
||||
* Windows releases are now built with Qt 5.4 which brings many improvements and bugfixes.
|
||||
* Add a troubleshooting section to the FAQ.
|
||||
* Display IPC errors to the user.
|
||||
* Rewrite keymode handling to use only one mode which also fixes various bugs.
|
||||
* Save version to state config.
|
||||
* Set zoom to default instead of 100% with `:zoom`/`=`.
|
||||
* Adjust page zoom if default zoom changed.
|
||||
* Force tabs to be focused on `:undo`.
|
||||
* Replace manual installation instructions on OS X with homebrew/macports.
|
||||
* Allow min-/maximizing of print preview on Windows.
|
||||
* Various documentation improvements.
|
||||
* Various other small improvements and cleanups.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
* Clean up and temporarily disable alias completion.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
* Fix setting of `QWebSettings` (e.g. web fonts) with empty strings.
|
||||
* Re-focus web view when leaving prompt/yesno mode.
|
||||
* Handle `:restart` correctly with Python eggs.
|
||||
* Handle an invalid cwd properly.
|
||||
* Fix popping of a dead question in prompter.
|
||||
* Fix `AttributeError` on config changes on Ubuntu.
|
||||
* Don't treat things like "31c3" as IP address.
|
||||
* Handle category being `None` in Qt message handler.
|
||||
* Force-include pygments in `freeze.py`.
|
||||
* Fix scroll percentage not updating on some pages like twitter.
|
||||
* Encode `Content-Disposition` header name properly.
|
||||
* Fix item sorting in `NeighborList`.
|
||||
* Handle data being `None` in download read timer.
|
||||
* Stop download read timer when reply has finished.
|
||||
* Fix handling of small/big `fuzzyval`'s in `NeighborList`.
|
||||
* Fix crashes when entering invalid values in `qute:settings`.
|
||||
* Abort questions in `NetworkManager` when destroyed.
|
||||
* Fix height calculation of download view.
|
||||
* Always auto-remove adblock downloads when done.
|
||||
* Ensure the docs get included in `freeze.py`.
|
||||
* Fix crash with `:zoom`.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.1[v0.1]
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Initial release.
|
||||
@@ -5,20 +5,15 @@ The Compiler <mail@qutebrowser.org>
|
||||
:data-uri:
|
||||
:toc:
|
||||
|
||||
IMPORTANT: I'm currently (July 2018) more busy than usual until September,
|
||||
because of exams coming up. Review of non-trivial pull requests will thus be
|
||||
delayed until then. If you're reading this note after mid-September, please
|
||||
open an issue.
|
||||
|
||||
I `<3` footnote:[Of course, that says `<3` in HTML.] contributors!
|
||||
|
||||
This document contains guidelines for contributing to qutebrowser, as well as
|
||||
useful hints when doing so.
|
||||
|
||||
If anything mentioned here would prevent you from contributing, please let me
|
||||
know, and contribute anyways! The guidelines are meant to make life easier for
|
||||
me, but if you don't follow everything in here, I won't be mad at you. In
|
||||
fact, I will probably change it for you.
|
||||
know, and contribute anyways! The guidelines are only meant to make life easier
|
||||
for me, but if you don't follow anything in here, I won't be mad at you. I will
|
||||
probably change it for you then, though.
|
||||
|
||||
If you have any problems, I'm more than happy to help! You can get help in
|
||||
several ways:
|
||||
@@ -39,22 +34,17 @@ this. It might be a good idea to ask on the mailing list or IRC channel to make
|
||||
sure nobody else started working on the same thing already.
|
||||
|
||||
If you want to find something useful to do, check the
|
||||
https://github.com/qutebrowser/qutebrowser/issues[issue tracker]. Some
|
||||
https://github.com/The-Compiler/qutebrowser/issues[issue tracker]. Some
|
||||
pointers:
|
||||
|
||||
* https://github.com/qutebrowser/qutebrowser/labels/easy[Issues which should
|
||||
* https://github.com/The-Compiler/qutebrowser/labels/easy[Issues which should
|
||||
be easy to solve]
|
||||
* https://github.com/qutebrowser/qutebrowser/labels/component%3A%20docs[Documentation issues which require little/no coding]
|
||||
|
||||
If you prefer C++ or Javascript to Python, see the relevant issues which involve
|
||||
work in those languages:
|
||||
|
||||
* https://github.com/qutebrowser/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3A%22language%3A+c%2B%2B%22[C++] (mostly work on Qt, the library behind qutebrowser)
|
||||
* https://github.com/qutebrowser/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3A%22language%3A+javascript%22[JavaScript]
|
||||
* https://github.com/The-Compiler/qutebrowser/labels/not%20code[Issues which
|
||||
require little/no coding]
|
||||
|
||||
There are also some things to do if you don't want to write code:
|
||||
|
||||
* Help the community, e.g., on the mailinglist and the IRC channel.
|
||||
* Help the community, e.g. on the mailinglist and the IRC channel.
|
||||
* Improve the documentation.
|
||||
* Help on the website and graphics (logo, etc.).
|
||||
|
||||
@@ -65,12 +55,12 @@ qutebrowser uses http://git-scm.com/[git] for its development. You can clone
|
||||
the repo like this:
|
||||
|
||||
----
|
||||
git clone https://github.com/qutebrowser/qutebrowser.git
|
||||
git clone https://github.com/The-Compiler/qutebrowser.git
|
||||
----
|
||||
|
||||
If you don't know git, a http://git-scm.com/[git cheatsheet] might come in
|
||||
handy. Of course, if using git is the issue which prevents you from
|
||||
contributing, feel free to send normal patches instead, e.g., generated via
|
||||
contributing, feel free to send normal patches instead, e.g. generated via
|
||||
`diff -Nur`.
|
||||
|
||||
Getting patches
|
||||
@@ -87,19 +77,9 @@ based on your changes like this:
|
||||
----
|
||||
git format-patch origin/master <1>
|
||||
----
|
||||
<1> Replace `master` by the branch your work was based on, e.g.,
|
||||
<1> Replace `master` by the branch your work was based on, e.g.
|
||||
`origin/develop`.
|
||||
|
||||
Running qutebrowser
|
||||
-------------------
|
||||
|
||||
After link:install.asciidoc#tox[installing qutebrowser via tox], you can run
|
||||
`.venv/bin/qutebrowser --debug --temp-basedir` to test your changes with debug
|
||||
logging enabled and without affecting existing running instances.
|
||||
|
||||
Alternatively, you can install qutebrowser's dependencies system-wide and run
|
||||
`python3 -m qutebrowser --debug --temp-basedir`.
|
||||
|
||||
Useful utilities
|
||||
----------------
|
||||
|
||||
@@ -109,39 +89,46 @@ Checkers
|
||||
qutebrowser uses http://tox.readthedocs.org/en/latest/[tox] to run its
|
||||
unittests and several linters/checkers.
|
||||
|
||||
Currently, the following tox environments are available:
|
||||
Currently, following tox environments are available:
|
||||
|
||||
* Tests using https://www.pytest.org[pytest]:
|
||||
- `py35`, `py36`: Run pytest for python 3.5/3.6 with the system-wide PyQt.
|
||||
- `py36-pyqt57`, ..., `py36-pyqt59`: Run pytest with the given PyQt version (`py35-*` also works).
|
||||
- `py36-pyqt59-cov`: Run with coverage support (other Python/PyQt versions work too).
|
||||
* `flake8`: Run various linting checks via https://pypi.python.org/pypi/flake8[flake8].
|
||||
- `py34`: Run pytest for python-3.4.
|
||||
- `py35`: Run pytest for python-3.5.
|
||||
- `py34-cov`: Run pytest for python-3.4 with code coverage report.
|
||||
- `py35-cov`: Run pytest for python-3.5 with code coverage report.
|
||||
* `flake8`: Run https://pypi.python.org/pypi/flake8[flake8] checks:
|
||||
https://pypi.python.org/pypi/pyflakes[pyflakes],
|
||||
https://pypi.python.org/pypi/pep8[pep8],
|
||||
https://pypi.python.org/pypi/mccabe[mccabe]
|
||||
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
|
||||
unused code portions.
|
||||
* `pylint`: Run http://pylint.org/[pylint] static code analysis.
|
||||
* `pydocstyle`: Check
|
||||
https://www.python.org/dev/peps/pep-0257/[PEP257] compliance with
|
||||
https://github.com/PyCQA/pydocstyle[pydocstyle]
|
||||
* `pyroma`: Check packaging practices with
|
||||
https://pypi.python.org/pypi/pyroma/[pyroma].
|
||||
https://pypi.python.org/pypi/pyroma/[pyroma]
|
||||
* `eslint`: Run http://eslint.org/[ESLint] javascript checker.
|
||||
* `check-manifest`: Check MANIFEST.in completeness with
|
||||
https://github.com/mgedmin/check-manifest[check-manifest].
|
||||
https://github.com/mgedmin/check-manifest[check-manifest]
|
||||
* `mkvenv`: Bootstrap a virtualenv for testing.
|
||||
* `misc`: Run `scripts/misc_checks.py` to check for:
|
||||
- untracked git files
|
||||
- VCS conflict markers
|
||||
- common spelling mistakes
|
||||
|
||||
The default test suite is run with `tox`; the list of default
|
||||
environments is obtained with `tox -l`.
|
||||
The default test suite is run with `tox`, the list of default
|
||||
environments is obtained with `tox -l` .
|
||||
|
||||
Please make sure the checks run without any warnings on your new contributions.
|
||||
|
||||
There's always the possibility of false positives; the following
|
||||
There's of course the possibility of false-positives, and the following
|
||||
techniques are useful to handle these:
|
||||
|
||||
* Use `_foo` for unused parameters, with `foo` being a descriptive name. Using
|
||||
`_` is discouraged.
|
||||
* If you think you have a good reason to suppress a message, then add the
|
||||
following comment:
|
||||
* If you think you have a good reason to suppress a message, add the following
|
||||
comment:
|
||||
+
|
||||
----
|
||||
# pylint: disable=message-name
|
||||
@@ -188,7 +175,7 @@ In the _scripts/_ subfolder there's a `run_profile.py` which profiles the code
|
||||
and shows a graphical representation of what takes how much time.
|
||||
|
||||
It uses the built-in Python
|
||||
https://docs.python.org/3.6/library/profile.html[cProfile] module and can show
|
||||
https://docs.python.org/3.4/library/profile.html[cProfile] module and can show
|
||||
the output in four different ways:
|
||||
|
||||
* Raw profile file (`--profile-tool=none`)
|
||||
@@ -199,12 +186,12 @@ the output in four different ways:
|
||||
Debugging
|
||||
~~~~~~~~~
|
||||
|
||||
There are some useful functions for debugging in the `qutebrowser.utils.debug`
|
||||
module.
|
||||
In the `qutebrowser.utils.debug` module there are some useful functions for
|
||||
debugging.
|
||||
|
||||
When starting qutebrowser with the `--debug` flag, you also get useful debug
|
||||
logs. You can add +--logfilter _[!]category[,category,...]_+ to restrict
|
||||
logging to the given categories.
|
||||
When starting qutebrowser with the `--debug` flag you also get useful debug
|
||||
logs. You can add +--logfilter _category[,category,...]_+ to restrict logging
|
||||
to the given categories.
|
||||
|
||||
With `--debug` there are also some additional +debug-_*_+ commands available,
|
||||
for example `:debug-all-objects` and `:debug-all-widgets` which print a list of
|
||||
@@ -227,7 +214,7 @@ Documentation of used Python libraries:
|
||||
* http://pygments.org/docs/[pygments]
|
||||
* http://fdik.org/pyPEG/index.html[pyPEG2]
|
||||
* http://pythonhosted.org/setuptools/[setuptools]
|
||||
* http://www.pyinstaller.org/[PyInstaller]
|
||||
* http://cx-freeze.readthedocs.org/en/latest/overview.html[cx_Freeze]
|
||||
* https://pypi.python.org/pypi/colorama[colorama]
|
||||
|
||||
Related RFCs and standards:
|
||||
@@ -289,23 +276,23 @@ Hints
|
||||
Python and Qt objects
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For many tasks, there are solutions available in both Qt and the Python
|
||||
standard library.
|
||||
For many tasks, there are solutions in both Qt and the Python standard library
|
||||
available.
|
||||
|
||||
In qutebrowser, the policy is usually to use the Python libraries, as they
|
||||
In qutebrowser, the policy is usually using the Python libraries, as they
|
||||
provide exceptions and other benefits.
|
||||
|
||||
There are some exceptions to that:
|
||||
|
||||
* `QThread` is used instead of Python threads because it provides signals and
|
||||
slots.
|
||||
* `QProcess` is used instead of Python's `subprocess`.
|
||||
* `QProcess` is used instead of Python's `subprocess`
|
||||
* `QUrl` is used instead of storing URLs as string, see the
|
||||
<<handling-urls,handling URLs>> section for details.
|
||||
|
||||
When using Qt objects, two issues must be taken care of:
|
||||
|
||||
* Methods of Qt objects report their status with their return values,
|
||||
* Methods of Qt objects report their status by using their return values,
|
||||
instead of using exceptions.
|
||||
+
|
||||
If a function gets or returns a Qt object which has an `.isValid()`
|
||||
@@ -317,12 +304,13 @@ on all such objects. It will raise
|
||||
If a function returns something else on error, the return value should
|
||||
carefully be checked.
|
||||
|
||||
* Methods of Qt objects have certain maximum values based on their
|
||||
* Methods of Qt objects have certain maximum values, based on their
|
||||
underlying C++ types.
|
||||
+
|
||||
To avoid passing too large of a numeric parameter to a Qt function, all
|
||||
numbers should be range-checked using `qutebrowser.qtutils.check_overflow`,
|
||||
or by other means (e.g. by setting a maximum value for a config object).
|
||||
When passing a numeric parameter to a Qt function, all numbers should
|
||||
be range-checked using `qutebrowser.qtutils.check_overflow`, or
|
||||
passing a value which is too large should be avoided by other means
|
||||
(e.g. by setting a maximum value for a config object).
|
||||
|
||||
[[object-registry]]
|
||||
The object registry
|
||||
@@ -334,7 +322,7 @@ dictionaries which map object names to the actual long-living objects.
|
||||
There are currently these object registries, also called 'scopes':
|
||||
|
||||
* The `global` scope, with objects which are used globally (`config`,
|
||||
`cookie-jar`, etc.).
|
||||
`cookie-jar`, etc.)
|
||||
* The `tab` scope with objects which are per-tab (`hintmanager`, `webview`,
|
||||
etc.). Passing this scope to `objreg.get()` selects the object in the currently
|
||||
focused tab by default. A tab can be explicitly selected by passing
|
||||
@@ -351,8 +339,8 @@ window=_win-id_, tab=_tab-id_])+. The default scope is `global`.
|
||||
All objects can be printed by starting with the `--debug` flag and using the
|
||||
`:debug-all-objects` command.
|
||||
|
||||
The registry is mainly used for <<commands,command handlers>>, but it can
|
||||
also be useful in places where using Qt's
|
||||
The registry is mainly used for <<commands,command handlers>> but also can be
|
||||
useful in places where using Qt's
|
||||
http://doc.qt.io/qt-5/signalsandslots.html[signals and slots] mechanism would
|
||||
be difficult.
|
||||
|
||||
@@ -362,7 +350,7 @@ Logging
|
||||
Logging is used at various places throughout the qutebrowser code. If you add a
|
||||
new feature, you should also add some strategic debug logging.
|
||||
|
||||
Unlike other Python projects, qutebrowser doesn't use a logger per file,
|
||||
Unless other Python projects, qutebrowser doesn't use a logger per file,
|
||||
instead it uses custom-named loggers.
|
||||
|
||||
The existing loggers are defined in `qutebrowser.utils.log`. If your feature
|
||||
@@ -390,7 +378,7 @@ The following logging levels are available for every logger:
|
||||
|error |There was an issue and some kind of operation was abandoned.
|
||||
|warning |There was an issue but the operation can continue running.
|
||||
|info |General informational messages.
|
||||
|debug |Verbose debugging information.
|
||||
|debug |Verbose debugging informations.
|
||||
|=======================================================================
|
||||
|
||||
[[commands]]
|
||||
@@ -425,11 +413,11 @@ selects which object registry (global, per-tab, etc.) to use. See the
|
||||
<<object-registry,object registry>> section for details.
|
||||
|
||||
There are also other arguments to customize the way the command is
|
||||
registered; see the class documentation for `register` in
|
||||
registered, see the class documentation for `register` in
|
||||
`qutebrowser.commands.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
|
||||
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
|
||||
@@ -443,15 +431,15 @@ 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]`.
|
||||
- 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]`
|
||||
|
||||
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:
|
||||
decorator *after* `@cmdutils.register`. This can e.g. be used to customize the
|
||||
flag an argument should get:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
@@ -477,10 +465,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.
|
||||
- `completion`: A completion function (see `qutebrowser.completions.models.*`)
|
||||
to use when completing arguments for the given command.
|
||||
- `win_id=True`: Mark the argument as special window ID argument
|
||||
- `count=True`: Mark the argument as special count argument
|
||||
- `hide=True`: Hide the argument from the documentation
|
||||
- `completion`: A `usertypes.Completion` member to use as completion.
|
||||
- `choices`: The allowed string choices for the argument.
|
||||
|
||||
The name of an argument will always be the parameter name, with any trailing
|
||||
@@ -494,13 +482,13 @@ qutebrowser handles two different types of URLs: URLs as a string, and URLs as
|
||||
the Qt `QUrl` type. As this can get confusing quickly, please follow the
|
||||
following guidelines:
|
||||
|
||||
* Convert a string to a QUrl object as early as possible, i.e., directly after
|
||||
* Convert a string to a QUrl object as early as possible, i.e. directly after
|
||||
the user did enter it.
|
||||
- Use `utils.urlutils.fuzzy_url` if the URL is entered by the user
|
||||
somewhere.
|
||||
- Be sure you handle `utils.urlutils.FuzzyError` and display an error
|
||||
message to the user.
|
||||
* Convert a `QUrl` object to a string as late as possible, i.e., before
|
||||
* Convert a `QUrl` object to a string as late as possible, e.g. before
|
||||
displaying it to the user.
|
||||
- If you want to display the URL to the user, use `url.toDisplayString()`
|
||||
so password information is removed.
|
||||
@@ -526,8 +514,8 @@ This is needed so valgrind handles self-modifying code correctly:
|
||||
[quote]
|
||||
____
|
||||
This option controls Valgrind's detection of self-modifying code. If no
|
||||
checking is done and a program executes some code, overwrites it with new
|
||||
code, and then executes the new code, Valgrind will continue to execute the
|
||||
checking is done, if a program executes some code, then overwrites it with new
|
||||
code, and executes the new code, Valgrind will continue to execute the
|
||||
translations it made for the old code. This will likely lead to incorrect
|
||||
behavior and/or crashes.
|
||||
|
||||
@@ -539,66 +527,6 @@ generate code and subsequently overwrite part or all of it. Running with all
|
||||
will slow Valgrind down noticeably.
|
||||
____
|
||||
|
||||
Setting up a Windows Development Environment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Install https://www.python.org/downloads/release/python-362/[Python 3.6].
|
||||
* Install PyQt via `pip install PyQt5`.
|
||||
* Create a file at `C:\Windows\system32\python3.bat` with the following content (adjust the path as necessary):
|
||||
`@C:\Python36\python %*`.
|
||||
This will make the Python 3.6 interpreter available as `python3`, which is used by various development scripts.
|
||||
* Install git from the https://git-scm.com/download/win[git-scm downloads page].
|
||||
Try not to enable `core.autocrlf`, since that will cause `flake8` to complain a lot. Use an editor that can deal with plain line feeds instead.
|
||||
* Clone your favourite qutebrowser repository.
|
||||
* To install tox, open an elevated cmd, enter your working directory and run `pip install -rmisc/requirements/requirements-tox.txt`.
|
||||
|
||||
Note that the `flake8` tox env might not run due to encoding errors despite having LANG/LC_* set correctly.
|
||||
|
||||
Rebuilding the website
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to rebuild the website, run `./scripts/asciidoc2html.py --website <outputdir>`.
|
||||
|
||||
Chrome URLs
|
||||
~~~~~~~~~~~
|
||||
|
||||
With the QtWebEngine backend, qutebrowser supports several chrome:// urls which
|
||||
can be useful for debugging:
|
||||
|
||||
- chrome://appcache-internals/
|
||||
- chrome://blob-internals/
|
||||
- chrome://gpu/
|
||||
- chrome://histograms/
|
||||
- chrome://indexeddb-internals/
|
||||
- chrome://media-internals/
|
||||
- chrome://network-errors/
|
||||
- chrome://serviceworker-internals/
|
||||
- chrome://webrtc-internals/
|
||||
- chrome://crash/ (crashes the current renderer process!)
|
||||
- chrome://kill/ (kills the current renderer process!)
|
||||
- chrome://gpucrash/ (crashes qutebrowser!)
|
||||
- chrome://gpuhang/ (hangs qutebrowser!)
|
||||
- chrome://gpuclean/ (crashes the current renderer process!)
|
||||
- chrome://ppapiflashcrash/
|
||||
- chrome://ppapiflashhang/
|
||||
- chrome://quota-internals/ (Qt 5.11)
|
||||
- chrome://taskscheduler-internals/ (Qt 5.11)
|
||||
- chrome://sandbox/ (Qt 5.11, Linux only)
|
||||
|
||||
QtWebEngine internals
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is mostly useful for qutebrowser maintainers to work around issues in Qt - if you don't understand it, don't worry, just ignore it.
|
||||
|
||||
The hierarchy of widgets when QtWebEngine is involved looks like this:
|
||||
|
||||
- qutebrowser has a `WebEngineTab` object, which is its abstraction over QtWebKit/QtWebEngine.
|
||||
- The `WebEngineTab` has a `_widget` attribute, which is the https://doc.qt.io/qt-5/qwebengineview.html[QWebEngineView]
|
||||
- That view has a https://doc.qt.io/qt-5/qwebenginepage.html[QWebEnginePage] for everything which doesn't require rendering.
|
||||
- The view also has a layout with exactly one element (which also is its `focusProxy()`)
|
||||
- That element is the http://code.qt.io/cgit/qt/qtwebengine.git/tree/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp[RenderWidgetHostViewQtDelegateWidget] (it inherits https://doc.qt.io/qt-5/qquickwidget.html[QQuickWidget]) - also often referred to as RWHV or RWHVQDW. It can be obtained via `sip.cast(tab._widget.focusProxy(), QQuickWidget)`.
|
||||
- Calling `rootObject()` on that gives us the https://doc.qt.io/qt-5/qquickitem.html[QQuickItem] where Chromium renders into (?). With it, we can do things like `.setRotation(20)`.
|
||||
|
||||
Style conventions
|
||||
-----------------
|
||||
|
||||
@@ -683,42 +611,63 @@ New Qt release
|
||||
https://bugreports.qt.io/issues/?jql=reporter%20%3D%20%22The%20Compiler%22%20ORDER%20BY%20fixVersion%20ASC[Qt bugtracker]
|
||||
and make sure all bugs marked as resolved are actually fixed.
|
||||
* Update own PKGBUILDs based on upstream Archlinux updates and rebuild.
|
||||
* Update recommended Qt version in `README`.
|
||||
* Update recommended Qt version in `README`
|
||||
* Grep for `WORKAROUND` in the code and test if fixed stuff works without the
|
||||
workaround.
|
||||
* Check relevant
|
||||
https://github.com/qutebrowser/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3Aqt[qutebrowser
|
||||
https://github.com/The-Compiler/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3Aqt[qutebrowser
|
||||
bugs] and check if they're fixed.
|
||||
* As soon as Homebrew updated, update the custom OS X bottle:
|
||||
- Update https://github.com/The-Compiler/homebrew-qt5-webkit/blob/master/Formula/qt5.rb[qt5.rb]
|
||||
- `brew install --build-from-source --build-bottle --verbose qt5.rb`
|
||||
- `brew bottle qt5.rb`
|
||||
- `brew install --build-from-source --build-bottle --verbose pyqt5`
|
||||
- `brew bottle pyqt5`
|
||||
- Upload bottles to github
|
||||
- Adjust `scripts/dev/ci/travis_install.sh`
|
||||
|
||||
New PyQt release
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* See above.
|
||||
* Update `tox.ini`/`.travis.yml`/`.appveyor.yml` to test new versions.
|
||||
* See above
|
||||
* Install new PyQt in Windows VM (32- and 64-bit)
|
||||
* Download new installer and update PyQt installer path in `ci_install.py`.
|
||||
|
||||
qutebrowser release
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Make sure there are no unstaged changes and the tests are green.
|
||||
* Make sure all issues with the related milestone are closed.
|
||||
* Run `x=... y=...` to set the respective shell variables.
|
||||
|
||||
* Update changelog (remove *(unreleased)*).
|
||||
* Add newest config to `tests/unit/config/old_configs` and update `test_upgrade_version`
|
||||
- `python -m qutebrowser --basedir conf :quit`
|
||||
- `sed '/^#/d' conf/config/qutebrowser.conf > tests/unit/config/old_configs/qutebrowser-v0.x.y.conf`
|
||||
- `rm -r conf`
|
||||
- commit
|
||||
* Adjust `__version_info__` in `qutebrowser/__init__.py`.
|
||||
* Commit.
|
||||
* Remove *(unreleased)* from changelog.
|
||||
* Run tests again
|
||||
* Run `asciidoc2html.py`.
|
||||
* Commit
|
||||
|
||||
* Create annotated git tag (`git tag -s "v1.$x.$y" -m "Release v1.$x.$y"`).
|
||||
* `git push origin`; `git push origin v1.$x.$y`.
|
||||
* Create annotated git tag (`git tag -s "v0.X.Y" -m "Release v0.X.Y"`)
|
||||
* If committing on minor branch, cherry-pick release commit to master.
|
||||
* Create release on github.
|
||||
* Mark the milestone at https://github.com/qutebrowser/qutebrowser/milestones
|
||||
* `git push origin`; `git push origin v0.X.Y`
|
||||
* Create release on github
|
||||
* Mark the milestone at https://github.com/The-Compiler/qutebrowser/milestones
|
||||
as closed.
|
||||
|
||||
* Linux: Run `git checkout v1.$x.$y && ./.venv/bin/python3 scripts/dev/build_release.py --upload v1.$x.$y`.
|
||||
* Windows: Run `git checkout v1.X.Y; py -3.6 scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py --upload v1.X.Y` (replace X/Y by hand).
|
||||
* macOS: Run `pyenv shell 3.6.6 && git checkout v1.X.Y && python3 scripts/dev/build_release.py --upload v1.X.Y` (replace X/Y by hand).
|
||||
* On server:
|
||||
- Run `python3 scripts/dev/download_release.py v1.X.Y` (replace X/Y by hand).
|
||||
- Run `git pull github master && sudo python3 scripts/asciidoc2html.py --website /srv/http/qutebrowser`
|
||||
* Update `qutebrowser-git` PKGBUILD if dependencies/install changed.
|
||||
* Announce to qutebrowser and qutebrowser-announce mailinglist.
|
||||
* Run `scripts/dev/build_release.py` on Linux to build an sdist
|
||||
* Upload to PyPI: `twine upload dist/foo{,.asc}`
|
||||
* Create Windows packages via `C:\Python34_x32\python scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py`
|
||||
* Upload to github
|
||||
|
||||
* Upload to qutebrowser.org with checksum/GPG
|
||||
- On server: `sudo mkdir -p /srv/http/qutebrowser/releases/v0.X.Y/windows`
|
||||
- `rsync -avPh dist/ tonks:`
|
||||
- On server: `sudo mv qutebrowser-0.X.Y.tar.gz* /srv/http/qutebrowser/releases/v0.X.Y`
|
||||
- Upload windows release:
|
||||
- `scp bb-win8:proj/qutebrowser/qutebrowser-0.X.Y-windows.zip .`
|
||||
- `aunpack qutebrowser-0.X.Y-windows.zip`
|
||||
- `sudo mv qutebrowser-0.X.Y-windows/* /srv/http/qutebrowser/releases/v0.X.Y/windows`
|
||||
* Update `qutebrowser-git` PKGBUILD if dependencies/install changed
|
||||
* Announce to qutebrowser mailinglist
|
||||
161
FAQ.asciidoc
Normal file
@@ -0,0 +1,161 @@
|
||||
Frequently asked questions
|
||||
==========================
|
||||
:title: Frequently asked questions
|
||||
The Compiler <mail@qutebrowser.org>
|
||||
|
||||
[qanda]
|
||||
What is qutebrowser based on?::
|
||||
qutebrowser uses http://www.python.org/[Python], http://qt.io/[Qt] and
|
||||
http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
+
|
||||
The concept of it is largely inspired by http://portix.bitbucket.org/dwb/[dwb]
|
||||
and http://www.vimperator.org/vimperator[Vimperator]. Many actions and
|
||||
key bindings are similar to dwb.
|
||||
|
||||
Why another browser?::
|
||||
It might be hard to believe, but I didn't find any browser which I was
|
||||
happy with, so I started to write my own. Also, I needed a project to get
|
||||
into writing GUI applications with Python and
|
||||
link:http://qt.io/[Qt]/link:http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
+
|
||||
Read the next few questions to find out why I was unhappy with existing
|
||||
software.
|
||||
|
||||
What's wrong with link:http://portix.bitbucket.org/dwb/[dwb]/link:http://sourceforge.net/projects/vimprobable/[vimprobable]/link:https://mason-larobina.github.io/luakit/[luakit]/link:http://pwmt.org/projects/jumanji/[jumanji]/... (projects based on WebKitGTK)?::
|
||||
Most of them are based on the http://webkitgtk.org/[WebKitGTK+]
|
||||
http://webkitgtk.org/reference/webkitgtk/stable/index.html[WebKit1] API,
|
||||
which causes a lot of crashes. As the GTK API using WebKit1 is
|
||||
https://lists.webkit.org/pipermail/webkit-gtk/2014-March/001821.html[deprecated],
|
||||
these bugs are never going to be fixed.
|
||||
+
|
||||
The newer http://webkitgtk.org/reference/webkit2gtk/stable/index.html[WebKit2
|
||||
API] seems to lack basic features like proxy support, and almost no projects
|
||||
seem to have started porting to WebKit2 (I only know of
|
||||
http://www.uzbl.org/[uzbl]).
|
||||
+
|
||||
qutebrowser uses http://qt.io/[Qt] and http://wiki.qt.io/QtWebKit[QtWebKit]
|
||||
instead, which suffers from far less such crashes. It might switch to
|
||||
http://wiki.qt.io/QtWebEngine[QtWebEngine] in the future, which is based on
|
||||
Google's https://en.wikipedia.org/wiki/Blink_(layout_engine)[Blink] rendering
|
||||
engine.
|
||||
|
||||
What's wrong with https://www.mozilla.org/en-US/firefox/new/[Firefox] and link:http://5digits.org/pentadactyl/[Pentadactyl]/link:http://www.vimperator.org/vimperator[Vimperator]?::
|
||||
Firefox likes to break compatibility with addons on each upgrade, gets
|
||||
slower and more bloated with every upgrade, and has some
|
||||
https://blog.mozilla.org/advancingcontent/2014/02/11/publisher-transformation-with-users-at-the-center/[horrible
|
||||
ideas] lately.
|
||||
+
|
||||
Also, developing addons for it is a nightmare.
|
||||
|
||||
What's wrong with http://www.chromium.org/Home[Chromium] and https://vimium.github.io/[Vimium]?::
|
||||
The Chrome plugin API doesn't seem to allow much freedom for plugin
|
||||
writers, which results in Vimium not really having all the features you'd
|
||||
expect from a proper minimal, vim-like browser.
|
||||
|
||||
Why Python?::
|
||||
I enjoy writing Python since 2011, which made it one of the possible
|
||||
choices. I wanted to use http://qt.io/[Qt] because of
|
||||
http://wiki.qt.io/QtWebKit[QtWebKit] so I didn't have
|
||||
http://wiki.qt.io/Category:LanguageBindings[many other choices]. I don't
|
||||
like C++ and can't write it very well, so that wasn't an alternative.
|
||||
|
||||
But isn't Python too slow for a browser?::
|
||||
http://www.infoworld.com/d/application-development/van-rossum-python-not-too-slow-188715[No.]
|
||||
I believe efficiency while coding is a lot more important than efficiency
|
||||
while running. Also, most of the heavy lifting of qutebrowser is done by Qt
|
||||
and WebKit in C++, with the
|
||||
https://wiki.python.org/moin/GlobalInterpreterLock[GIL] released.
|
||||
|
||||
Is there an adblocker?::
|
||||
There is a host-based adblocker which takes /etc/hosts-like lists. A "real"
|
||||
adblocker has a
|
||||
http://www.reddit.com/r/programming/comments/25j41u/adblock_pluss_effect_on_firefoxs_memory_usage/chhpomw[big
|
||||
impact] on browsing speed and
|
||||
https://blog.mozilla.org/nnethercote/2014/05/14/adblock-pluss-effect-on-firefoxs-memory-usage/[RAM
|
||||
usage], so implementing it properly might take some time and won't be done
|
||||
for v0.1 if at all.
|
||||
|
||||
How do I play Youtube videos with mpv?::
|
||||
You can easily add a key binding to play youtube videos inside a real video
|
||||
player - optionally even with hinting for links:
|
||||
+
|
||||
----
|
||||
:bind m spawn mpv {url}
|
||||
:bind M hint links spawn mpv {hint-url}
|
||||
----
|
||||
+
|
||||
Note that you might need an additional package (e.g.
|
||||
https://www.archlinux.org/packages/community/any/youtube-dl/[youtube-dl] on
|
||||
Archlinux) to play web videos with mpv.
|
||||
+
|
||||
There is a very useful script for mpv, which emulates "unique application"
|
||||
functionality. This way you can add links to the mpv playlist instead of
|
||||
playing them all at once.
|
||||
+
|
||||
You can find the script here: https://github.com/mpv-player/mpv/blob/master/TOOLS/umpv
|
||||
+
|
||||
It also works nicely with rapid hints:
|
||||
+
|
||||
----
|
||||
:bind m spawn umpv {url}
|
||||
:bind M hint links spawn umpv {hint-url}
|
||||
:bind ;M hint --rapid links spawn umpv {hint-url}
|
||||
----
|
||||
|
||||
How do I use qutebrowser with mutt?::
|
||||
Due to a Qt limitation, local files without `.html` extensions are
|
||||
"downloaded" instead of displayed, see
|
||||
https://github.com/The-Compiler/qutebrowser/issues/566[#566]. You can work
|
||||
around this by using this in your `mailcap`:
|
||||
+
|
||||
----
|
||||
text/html; mv %s %s.html && qutebrowser %s.html >/dev/null 2>/dev/null; needsterminal;
|
||||
----
|
||||
|
||||
== Troubleshooting
|
||||
|
||||
Configuration not saved after modifying config.::
|
||||
When editing your config file manually, qutebrowser must be exited completely.
|
||||
This can be done by issuing the command `:quit` or by pressing `Ctrl+q`.
|
||||
|
||||
Unable to view flash content.::
|
||||
If you have flash installed for on your system, it's necessary to enable plugins
|
||||
to use the flash plugin. Using the command `:set content allow-plugins true`
|
||||
in qutebrowser will enable plugins. Packages for flash should
|
||||
be provided for your platform or it can be obtained from
|
||||
http://get.adobe.com/flashplayer/[Adobe].
|
||||
|
||||
Experiencing freezing on sites like duckduckgo and youtube.::
|
||||
This issue could be caused by stale plugin files installed by `mozplugger`
|
||||
if mozplugger was subsequently removed.
|
||||
Try exiting qutebrowser and removing `~/.mozilla/plugins/mozplugger*.so`.
|
||||
See https://github.com/The-Compiler/qutebrowser/issues/357[Issue #357]
|
||||
for more details.
|
||||
|
||||
Experiencing segfaults (crashes) on Debian systems.::
|
||||
For Debian it's highly recommended to install the `gstreamer0.10-plugins-base` package.
|
||||
This is a workaround for a bug in Qt, it has been fixed upstream in Qt 5.4
|
||||
More details can be found
|
||||
https://bugs.webkit.org/show_bug.cgi?id=119951[here].
|
||||
|
||||
Segfaults on Facebook, Medium, Amazon, ...::
|
||||
If you are on a Debian or Ubuntu based system, you might experience some crashes
|
||||
visiting these sites. This is caused by various bugs in Qt which have been
|
||||
fixed in Qt 5.4. However Debian and Ubuntu are slow to adopt or upgrade
|
||||
some packages. On Debian Jessie, it's recommended to use the experimental
|
||||
repos as described in https://github.com/The-Compiler/qutebrowser/blob/master/INSTALL.asciidoc#on-debian--ubuntu[INSTALL].
|
||||
+
|
||||
Since Ubuntu Trusty (using Qt 5.2.1),
|
||||
https://bugreports.qt.io/browse/QTBUG-42417?jql=component%20%3D%20WebKit%20and%20resolution%20%3D%20Done%20and%20fixVersion%20in%20(5.3.0%2C%20%225.3.0%20Alpha%22%2C%20%225.3.0%20Beta1%22%2C%20%225.3.0%20RC1%22%2C%205.3.1%2C%205.3.2%2C%205.4.0%2C%20%225.4.0%20Alpha%22%2C%20%225.4.0%20Beta%22%2C%20%225.4.0%20RC%22)%20and%20priority%20in%20(%22P2%3A%20Important%22%2C%20%22P1%3A%20Critical%22%2C%20%22P0%3A%20Blocker%22)[over
|
||||
70 important bugs] have been fixed in QtWebKit. For Debian Jessie (using Qt 5.3.2)
|
||||
it's still
|
||||
https://bugreports.qt.io/browse/QTBUG-42417?jql=component%20%3D%20WebKit%20and%20resolution%20%3D%20Done%20and%20fixVersion%20in%20(5.4.0%2C%20%225.4.0%20Alpha%22%2C%20%225.4.0%20Beta%22%2C%20%225.4.0%20RC%22)%20and%20priority%20in%20(%22P2%3A%20Important%22%2C%20%22P1%3A%20Critical%22%2C%20%22P0%3A%20Blocker%22)[nearly
|
||||
20 important bugs].
|
||||
|
||||
My issue is not listed.::
|
||||
If you experience any segfaults or crashes, you can report the issue in
|
||||
https://github.com/The-Compiler/qutebrowser/issues[the issue tracker] or
|
||||
using the `:report` command.
|
||||
If you are reporting a segfault, make sure you read the
|
||||
link:doc/stacktrace.asciidoc[guide] on how to report them with all needed
|
||||
information.
|
||||
327
INSTALL.asciidoc
Normal file
@@ -0,0 +1,327 @@
|
||||
Installing qutebrowser
|
||||
======================
|
||||
|
||||
On Debian / Ubuntu
|
||||
------------------
|
||||
|
||||
qutebrowser should run on these systems:
|
||||
|
||||
* Debian jessie or newer
|
||||
* Ubuntu Trusty (14.04 LTS) or newer
|
||||
* Any other distribution based on these (e.g. Linux Mint 17+)
|
||||
|
||||
Unfortunately there is no Debian package in the official repos yet, but installing qutebrowser is
|
||||
still relatively easy!
|
||||
|
||||
You can use packages that are built for every release or build it yourself from git.
|
||||
|
||||
Using the packages
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Install the dependencies via apt-get:
|
||||
|
||||
----
|
||||
# apt-get install python3-lxml python-tox python3-pyqt5 python3-pyqt5.qtwebkit python3-sip python3-jinja2 python3-pygments python3-yaml
|
||||
----
|
||||
|
||||
Get the packages from the https://github.com/The-Compiler/qutebrowser/releases[release page]
|
||||
|
||||
Install the packages:
|
||||
|
||||
----
|
||||
# dpkg -i python3-pypeg2_*_all.deb
|
||||
# dpkg -i qutebrowser_*_all.deb
|
||||
----
|
||||
|
||||
Build it from git
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Install the dependencies via apt-get:
|
||||
|
||||
[NOTE]
|
||||
==========================
|
||||
On Debian, it's recommended to install the Qt packages from the
|
||||
https://wiki.debian.org/DebianExperimental[experimental] repository as those
|
||||
are a much newer version of Qt which is more stable.
|
||||
|
||||
Add the following line to your `/etc/apt/sources.list`:
|
||||
|
||||
----
|
||||
deb http://ftp.debian.org/debian experimental main
|
||||
----
|
||||
|
||||
Then install the packages like this:
|
||||
|
||||
----
|
||||
# apt-get update
|
||||
# apt-get install -t experimental python3-pyqt5 python3-pyqt5.qtwebkit python3-sip python3-dev
|
||||
# apt-get install python-tox
|
||||
----
|
||||
|
||||
It's also recommended to pin those packages to receive updates by creating a
|
||||
file `/etc/apt/preferences.d/qutebrowser` with the following contents:
|
||||
|
||||
----
|
||||
Package: python3-pyqt5* libqt5*
|
||||
Pin: release a=experimental
|
||||
Pin-Priority: 800
|
||||
----
|
||||
==========================
|
||||
|
||||
For distributions other than Debian or if you prefer to not use the
|
||||
experimental repo:
|
||||
|
||||
----
|
||||
# apt-get install python3-pyqt5 python3-pyqt5.qtwebkit python-tox python3-sip python3-dev
|
||||
----
|
||||
|
||||
To generate the documentation for the `:help` command, when using the git
|
||||
repository (rather than a release):
|
||||
|
||||
----
|
||||
# apt-get install asciidoc source-highlight
|
||||
$ python3 scripts/asciidoc2html.py
|
||||
----
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
|
||||
----
|
||||
# apt-get install gstreamer1.0-plugins-{bad,base,good,ugly}
|
||||
----
|
||||
|
||||
Then <<tox,install qutebrowser via tox>>.
|
||||
|
||||
On Fedora
|
||||
---------
|
||||
|
||||
qutebrowser is available in the official repositories for Fedora 22 and newer.
|
||||
|
||||
----
|
||||
# dnf install qutebrowser
|
||||
----
|
||||
|
||||
On Archlinux
|
||||
------------
|
||||
|
||||
qutebrowser is available in the official [community] repository.
|
||||
|
||||
----
|
||||
# pacman -S qutebrowser
|
||||
----
|
||||
|
||||
There is also a -git version available in the AUR:
|
||||
https://aur.archlinux.org/packages/qutebrowser-git/[qutebrowser-git].
|
||||
|
||||
You can install it using `makepkg` like this:
|
||||
|
||||
----
|
||||
$ git clone https://aur.archlinux.org/qutebrowser-git.git
|
||||
$ cd qutebrowser-git
|
||||
$ makepkg -si
|
||||
$ cd ..
|
||||
$ rm -r qutebrowser-git
|
||||
----
|
||||
|
||||
or you could use an AUR helper, e.g. `yaourt -S qutebrowser-git`.
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
|
||||
----
|
||||
# pacman -S gst-plugins-{base,good,bad,ugly} gst-libav
|
||||
----
|
||||
|
||||
On Gentoo
|
||||
---------
|
||||
|
||||
qutebrowser is available in the main repository and can be installed with:
|
||||
|
||||
----
|
||||
# emerge -av qutebrowser
|
||||
----
|
||||
|
||||
Make sure you have `python3_4` in your `PYTHON_TARGETS`
|
||||
(`/etc/portage/make.conf`) and rebuild your system (`emerge -uDNav @world`) if
|
||||
necessary.
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
|
||||
----
|
||||
# emerge -av gst-plugins-{base,good,bad,ugly,libav}
|
||||
----
|
||||
|
||||
|
||||
On Void Linux
|
||||
-------------
|
||||
|
||||
qutebrowser is available in the official repositories and can be installed
|
||||
with:
|
||||
|
||||
----
|
||||
# xbps-install qutebrowser
|
||||
----
|
||||
|
||||
On NixOS
|
||||
--------
|
||||
|
||||
Nixpkgs collection contains `pkgs.qutebrowser` since June 2015. You can install
|
||||
it with:
|
||||
|
||||
----
|
||||
$ nix-env -i qutebrowser
|
||||
----
|
||||
|
||||
On openSUSE
|
||||
-----------
|
||||
|
||||
There are prebuilt RPMs available for Tumbleweed and Leap 42.1:
|
||||
|
||||
http://software.opensuse.org/download.html?project=home%3Aarpraher&package=qutebrowser[One Click Install]
|
||||
|
||||
Or add the repo manually:
|
||||
|
||||
----
|
||||
# zypper addrepo http://download.opensuse.org/repositories/home:arpraher/openSUSE_Tumbleweed/home:arpraher.repo
|
||||
# zypper refresh
|
||||
# zypper install qutebrowser
|
||||
----
|
||||
|
||||
On Windows
|
||||
----------
|
||||
|
||||
There are different ways to install qutebrowser on Windows:
|
||||
|
||||
Prebuilt binaries
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Prebuilt standalone packages and MSI installers
|
||||
https://github.com/The-Compiler/qutebrowser/releases[are built] for every
|
||||
release.
|
||||
|
||||
https://chocolatey.org/packages/qutebrowser[Chocolatey package]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* PackageManagement PowerShell module
|
||||
----
|
||||
PS C:\> Install-Package qutebrowser
|
||||
----
|
||||
* Chocolatey's client
|
||||
----
|
||||
C:\> choco install qutebrowser
|
||||
----
|
||||
|
||||
Manual install
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* Use the installer from http://www.python.org/downloads[python.org] to get
|
||||
Python 3 (be sure to install pip).
|
||||
* Use the installer from
|
||||
http://www.riverbankcomputing.com/software/pyqt/download5[Riverbank computing]
|
||||
to get Qt and PyQt5.
|
||||
* Install https://testrun.org/tox/latest/index.html[tox] via
|
||||
https://pip.pypa.io/en/latest/[pip]:
|
||||
|
||||
----
|
||||
$ pip install tox
|
||||
----
|
||||
|
||||
Then <<tox,install qutebrowser via tox>>.
|
||||
|
||||
On OS X
|
||||
-------
|
||||
|
||||
The easiest way to install qutebrowser on OS X is to use the prebuilt `.app`
|
||||
files from the
|
||||
https://github.com/The-Compiler/qutebrowser/releases[release page].
|
||||
|
||||
Alternatively, you can install the dependencies via a package manager (like
|
||||
http://brew.sh/[Homebrew] or https://www.macports.org/[MacPorts]) and run
|
||||
qutebrowser from source.
|
||||
|
||||
For Homebrew, a few extra steps are necessary since Homebrew dropped QtWebKit
|
||||
from Qt 5.6 - however, some users reported this didn't work for them, so using
|
||||
the `.app` is strongly encouraged.
|
||||
|
||||
This installs a Qt 5.5 and symlinks it so PyQt5 will work with it instead of Qt
|
||||
5.6. This requires that `qt5` is not installed via Homebrew:
|
||||
|
||||
----
|
||||
$ brew install python3 d-bus mysql sip xz
|
||||
$ brew install homebrew/versions/qt55
|
||||
$ brew install --ignore-dependencies pyqt5
|
||||
$ ln -s /usr/local/opt/qt55 /usr/local/opt/qt5
|
||||
|
||||
$ pip3.5 install qutebrowser
|
||||
----
|
||||
|
||||
For MacPorts, run:
|
||||
|
||||
----
|
||||
$ sudo port install python34 py34-jinja2 asciidoc py34-pygments py34-pyqt5
|
||||
$ sudo pip3.4 install qutebrowser
|
||||
----
|
||||
|
||||
The preferences for qutebrowser are stored in
|
||||
`~/Library/Preferences/qutebrowser`, the application data is stored in
|
||||
`~/Library/Application Support/qutebrowser`.
|
||||
|
||||
Packagers
|
||||
---------
|
||||
|
||||
There are example .desktop and icon files provided. They would go in the
|
||||
standard location for your distro (`/usr/share/applications` and
|
||||
`/usr/share/pixmaps` for example).
|
||||
|
||||
The normal `setup.py install` doesn't install these files, so you'll have to do
|
||||
it as part of the packaging process.
|
||||
|
||||
[[tox]]
|
||||
Installing qutebrowser with tox
|
||||
-------------------------------
|
||||
|
||||
First of all, clone the repository using http://git-scm.org/[git] and switch
|
||||
into the repository folder:
|
||||
|
||||
----
|
||||
$ git clone https://github.com/The-Compiler/qutebrowser.git
|
||||
$ cd qutebrowser
|
||||
----
|
||||
|
||||
|
||||
Then run tox inside the qutebrowser repository to set up a
|
||||
https://docs.python.org/3/library/venv.html[virtual environment]:
|
||||
|
||||
----
|
||||
$ tox -e mkvenv
|
||||
----
|
||||
|
||||
This installs all needed Python dependencies in a `.venv` subfolder. The
|
||||
system-wide Qt5/PyQt5 installations are symlinked into the virtual environment.
|
||||
|
||||
You can then create a simple wrapper script to start qutebrowser somewhere in
|
||||
your `$PATH` (e.g. `/usr/local/bin/qutebrowser` or `~/bin/qutebrowser`):
|
||||
|
||||
----
|
||||
#!/bin/bash
|
||||
~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser "$@"
|
||||
----
|
||||
|
||||
If you are developing on qutebrowser, you may want to redirect it to a local
|
||||
config:
|
||||
|
||||
----
|
||||
#!/bin/bash
|
||||
~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser -c .qutebrowser-local "$@"
|
||||
----
|
||||
|
||||
Updating
|
||||
~~~~~~~~
|
||||
|
||||
When you updated your local copy of the code (e.g. by pulling the git repo, or
|
||||
extracting a new version), the virtualenv should automatically use the updated
|
||||
code. However, if dependencies got added, this won't be reflected in the
|
||||
virtualenv. Thus it's recommended to run the following command to recreate the
|
||||
virtualenv:
|
||||
|
||||
----
|
||||
$ tox -r -e mkvenv
|
||||
----
|
||||
34
MANIFEST.in
@@ -1,5 +1,6 @@
|
||||
recursive-include qutebrowser *.py
|
||||
recursive-include qutebrowser/img *.svg *.png
|
||||
recursive-include qutebrowser/test *.py
|
||||
recursive-include qutebrowser/javascript *.js
|
||||
graft qutebrowser/html
|
||||
graft qutebrowser/3rdparty
|
||||
@@ -7,37 +8,42 @@ graft icons
|
||||
graft doc/img
|
||||
graft misc/apparmor
|
||||
graft misc/userscripts
|
||||
graft misc/requirements
|
||||
recursive-include scripts *.py *.sh *.js
|
||||
recursive-include scripts *.py
|
||||
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/Makefile
|
||||
include COPYING doc/* README.asciidoc CONTRIBUTING.asciidoc FAQ.asciidoc INSTALL.asciidoc CHANGELOG.asciidoc
|
||||
include qutebrowser.desktop
|
||||
include requirements.txt
|
||||
include tox.ini
|
||||
include qutebrowser.py
|
||||
include misc/cheatsheet.svg
|
||||
include qutebrowser/config/configdata.yml
|
||||
|
||||
prune www
|
||||
prune scripts/dev
|
||||
prune scripts/testbrowser/cpp
|
||||
prune scripts/testbrowser_cpp
|
||||
prune .github
|
||||
exclude scripts/asciidoc2html.py
|
||||
exclude doc/notes
|
||||
recursive-exclude doc *.asciidoc
|
||||
include doc/qutebrowser.1.asciidoc
|
||||
include doc/changelog.asciidoc
|
||||
prune tests
|
||||
prune qutebrowser/3rdparty
|
||||
prune misc/requirements
|
||||
prune misc/docker
|
||||
exclude .editorconfig
|
||||
exclude pytest.ini
|
||||
exclude qutebrowser/javascript/.eslintrc.yaml
|
||||
exclude qutebrowser/javascript/.eslintignore
|
||||
exclude qutebrowser.rcc
|
||||
exclude .coveragerc
|
||||
exclude .pylintrc
|
||||
exclude .eslintrc
|
||||
exclude .eslintignore
|
||||
exclude doc/help
|
||||
exclude .*
|
||||
exclude .appveyor.yml
|
||||
exclude .travis.yml
|
||||
exclude codecov.yml
|
||||
exclude .pydocstylerc
|
||||
exclude misc/appveyor_install.py
|
||||
exclude misc/qutebrowser.spec
|
||||
exclude misc/qutebrowser.nsi
|
||||
exclude misc/qutebrowser.rcc
|
||||
exclude .flake8
|
||||
|
||||
global-exclude __pycache__ *.pyc *.pyo
|
||||
|
||||
287
README.asciidoc
@@ -1,23 +1,26 @@
|
||||
// If you are reading this in plaintext or on PyPi:
|
||||
//
|
||||
// A rendered version is available at:
|
||||
// https://github.com/qutebrowser/qutebrowser/blob/master/README.asciidoc
|
||||
// https://github.com/The-Compiler/qutebrowser/blob/master/README.asciidoc
|
||||
|
||||
qutebrowser
|
||||
===========
|
||||
|
||||
// QUTE_WEB_HIDE
|
||||
image:icons/qutebrowser-64x64.png[qutebrowser logo] *A keyboard-driven, vim-like browser based on PyQt5 and Qt.*
|
||||
image:icons/qutebrowser-64x64.png[qutebrowser logo] *A keyboard-driven, vim-like browser based on PyQt5 and QtWebKit.*
|
||||
|
||||
image:https://travis-ci.org/qutebrowser/qutebrowser.svg?branch=master["Build Status", link="https://travis-ci.org/qutebrowser/qutebrowser"]
|
||||
image:https://ci.appveyor.com/api/projects/status/5pyauww2k68bbow2/branch/master?svg=true["AppVeyor build status", link="https://ci.appveyor.com/project/qutebrowser/qutebrowser"]
|
||||
image:https://codecov.io/github/qutebrowser/qutebrowser/coverage.svg?branch=master["coverage badge",link="https://codecov.io/github/qutebrowser/qutebrowser?branch=master"]
|
||||
image:https://img.shields.io/pypi/l/qutebrowser.svg?style=flat["license badge",link="https://github.com/The-Compiler/qutebrowser/blob/master/COPYING"]
|
||||
image:https://img.shields.io/pypi/v/qutebrowser.svg?style=flat["version badge",link="https://pypi.python.org/pypi/qutebrowser/"]
|
||||
image:https://requires.io/github/The-Compiler/qutebrowser/requirements.svg?branch=master["requirements badge",link="https://requires.io/github/The-Compiler/qutebrowser/requirements/?branch=master"]
|
||||
image:https://travis-ci.org/The-Compiler/qutebrowser.svg?branch=master["Build Status", link="https://travis-ci.org/The-Compiler/qutebrowser"]
|
||||
image:https://ci.appveyor.com/api/projects/status/9gmnuip6i1oq7046?svg=true["AppVeyor build status", link="https://ci.appveyor.com/project/The-Compiler/qutebrowser"]
|
||||
image:https://codecov.io/github/The-Compiler/qutebrowser/coverage.svg?branch=master["coverage badge",link="https://codecov.io/github/The-Compiler/qutebrowser?branch=master"]
|
||||
|
||||
link:https://www.qutebrowser.org[website] | link:https://blog.qutebrowser.org[blog] | https://github.com/qutebrowser/qutebrowser/blob/master/doc/faq.asciidoc[FAQ] | https://www.qutebrowser.org/doc/contributing.html[contributing] | link:https://github.com/qutebrowser/qutebrowser/releases[releases] | https://github.com/qutebrowser/qutebrowser/blob/master/doc/install.asciidoc[installing]
|
||||
link:http://www.qutebrowser.org[website] | link:http://blog.qutebrowser.org[blog] | link:https://github.com/The-Compiler/qutebrowser/releases[releases]
|
||||
// QUTE_WEB_HIDE_END
|
||||
|
||||
qutebrowser is a keyboard-focused browser with a minimal GUI. It's based
|
||||
on Python and PyQt5 and free software, licensed under the GPL.
|
||||
on Python, PyQt5 and QtWebKit and free software, licensed under the GPL.
|
||||
|
||||
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl.
|
||||
|
||||
@@ -32,9 +35,12 @@ image:doc/img/hints.png["screenshot 4",width=300,link="doc/img/hints.png"]
|
||||
Downloads
|
||||
---------
|
||||
|
||||
See the https://github.com/qutebrowser/qutebrowser/releases[github releases
|
||||
page] for available downloads and the link:doc/install.asciidoc[INSTALL] file for
|
||||
detailed instructions on how to get qutebrowser running on various platforms.
|
||||
See the https://github.com/The-Compiler/qutebrowser/releases[github releases
|
||||
page] for available downloads (currently a source archive, and standalone
|
||||
packages as well as MSI installers for Windows).
|
||||
|
||||
See link:INSTALL.asciidoc[INSTALL] for detailed instructions on how to get
|
||||
qutebrowser running for various platforms.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@@ -42,15 +48,13 @@ Documentation
|
||||
In addition to the topics mentioned in this README, the following documents are
|
||||
available:
|
||||
|
||||
* https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png[Key binding cheatsheet]: +
|
||||
image:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png"]
|
||||
* A http://qutebrowser.org/img/cheatsheet-big.png[key binding cheatsheet]: +
|
||||
image:http://qutebrowser.org/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="http://qutebrowser.org/img/cheatsheet-big.png"]
|
||||
* link:doc/quickstart.asciidoc[Quick start guide]
|
||||
* https://www.shortcutfoo.com/app/dojos/qutebrowser[Free training course] to remember those key bindings
|
||||
* link:doc/faq.asciidoc[Frequently asked questions]
|
||||
* link:doc/help/configuring.asciidoc[Configuring qutebrowser]
|
||||
* link:doc/contributing.asciidoc[Contributing to qutebrowser]
|
||||
* link:doc/install.asciidoc[Installing qutebrowser]
|
||||
* link:doc/changelog.asciidoc[Change Log]
|
||||
* link:FAQ.asciidoc[Frequently asked questions]
|
||||
* link:CONTRIBUTING.asciidoc[Contributing to qutebrowser]
|
||||
* link:INSTALL.asciidoc[INSTALL]
|
||||
* link:CHANGELOG.asciidoc[Change Log]
|
||||
* link:doc/stacktrace.asciidoc[Reporting segfaults]
|
||||
* link:doc/userscripts.asciidoc[How to write userscripts]
|
||||
|
||||
@@ -65,18 +69,11 @@ message to the
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
|
||||
mailto:qutebrowser@lists.qutebrowser.org[].
|
||||
|
||||
There's also an https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[announce-only mailinglist]
|
||||
at mailto:qutebrowser-announce@lists.qutebrowser.org[] (the announcements also
|
||||
get sent to the general qutebrowser@ list).
|
||||
|
||||
If you're a reddit user, there's a
|
||||
https://www.reddit.com/r/qutebrowser/[/r/qutebrowser] subreddit there.
|
||||
|
||||
Contributions / Bugs
|
||||
--------------------
|
||||
|
||||
You want to contribute to qutebrowser? Awesome! Please read
|
||||
link:doc/contributing.asciidoc[the contribution guidelines] for details and
|
||||
link:CONTRIBUTING.asciidoc[the contribution guidelines] for details and
|
||||
useful hints.
|
||||
|
||||
If you found a bug or have a feature request, you can report it in several
|
||||
@@ -89,43 +86,36 @@ https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
|
||||
mailto:qutebrowser@lists.qutebrowser.org[].
|
||||
|
||||
For security bugs, please contact me directly at mail@qutebrowser.org, GPG ID
|
||||
https://www.the-compiler.org/pubkey.asc[0x916eb0c8fd55a072].
|
||||
http://www.the-compiler.org/pubkey.asc[0xFD55A072].
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
The following software and libraries are required to run qutebrowser:
|
||||
|
||||
* http://www.python.org/[Python] 3.5 or newer (3.6 recommended)
|
||||
* http://qt.io/[Qt] 5.7.1 or newer (5.11.1 recommended) with the following modules:
|
||||
- QtCore / qtbase
|
||||
- QtQuick (part of qtbase in some distributions)
|
||||
- QtSQL (part of qtbase in some distributions)
|
||||
- QtOpenGL
|
||||
- QtWebEngine, or
|
||||
- QtWebKit - only the
|
||||
link:https://github.com/annulen/webkit/wiki[updated fork] (5.212) is
|
||||
supported
|
||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.7.0 or newer
|
||||
(5.11.2 recommended) for Python 3
|
||||
* http://www.python.org/[Python] 3.4 or newer
|
||||
* http://qt.io/[Qt] 5.2.0 or newer (5.5.1 recommended)
|
||||
* QtWebKit
|
||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.2.0 or newer
|
||||
(5.5.1 recommended) for Python 3
|
||||
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
||||
* http://fdik.org/pyPEG/[pyPEG2]
|
||||
* http://jinja.pocoo.org/[jinja2]
|
||||
* http://pygments.org/[pygments]
|
||||
* https://github.com/yaml/pyyaml[PyYAML]
|
||||
* http://www.attrs.org/[attrs]
|
||||
* http://pyyaml.org/wiki/PyYAML[PyYAML]
|
||||
|
||||
The following libraries are optional:
|
||||
The following libraries are optional and provide a better user experience:
|
||||
|
||||
* http://cthedot.de/cssutils/[cssutils] (for an improved `:download --mhtml`
|
||||
with QtWebKit).
|
||||
* On Windows, https://pypi.python.org/pypi/colorama/[colorama] for colored log
|
||||
output.
|
||||
* http://asciidoc.org/[asciidoc] to generate the documentation for the `:help`
|
||||
command, when using the git repository (rather than a release).
|
||||
* http://cthedot.de/cssutils/[cssutils]
|
||||
|
||||
See link:doc/install.asciidoc[the documentation] for directions on how to
|
||||
install qutebrowser and its dependencies.
|
||||
To generate the documentation for the `:help` command, when using the git
|
||||
repository (rather than a release), http://asciidoc.org/[asciidoc] is needed.
|
||||
|
||||
On Windows, https://pypi.python.org/pypi/colorama/[colorama] is needed to
|
||||
display colored log output.
|
||||
|
||||
See link:INSTALL.asciidoc[INSTALL] for directions on how to install qutebrowser
|
||||
and its dependencies.
|
||||
|
||||
Donating
|
||||
--------
|
||||
@@ -146,63 +136,170 @@ get in touch!
|
||||
Authors
|
||||
-------
|
||||
|
||||
qutebrowser's primary author is Florian Bruhin (The Compiler), but qutebrowser
|
||||
wouldn't be what it is without the help of
|
||||
https://github.com/qutebrowser/qutebrowser/graphs/contributors[hundreds of contributors]!
|
||||
Contributors, sorted by the number of commits in descending order:
|
||||
|
||||
Additionally, the following people have contributed graphics:
|
||||
// QUTE_AUTHORS_START
|
||||
* Florian Bruhin
|
||||
* Daniel Schadt
|
||||
* Ryan Roden-Corrent
|
||||
* Antoni Boucher
|
||||
* Lamar Pavel
|
||||
* Bruno Oliveira
|
||||
* Alexander Cogneau
|
||||
* Felix Van der Jeugt
|
||||
* Martin Tournoij
|
||||
* Jakub Klinkovský
|
||||
* Raphael Pierzina
|
||||
* Joel Torstensson
|
||||
* Jan Verbeek
|
||||
* Tarcisio Fedrizzi
|
||||
* Patric Schmitz
|
||||
* Claude
|
||||
* Corentin Julé
|
||||
* meles5
|
||||
* Philipp Hansch
|
||||
* Panagiotis Ktistakis
|
||||
* Artur Shaik
|
||||
* Nathan Isom
|
||||
* Thorsten Wißmann
|
||||
* Kevin Velghe
|
||||
* Austin Anderson
|
||||
* Jimmy
|
||||
* Marshall Lochbaum
|
||||
* Alexey "Averrin" Nabrodov
|
||||
* avk
|
||||
* ZDarian
|
||||
* Milan Svoboda
|
||||
* John ShaggyTwoDope Jenkins
|
||||
* Peter Vilim
|
||||
* Clayton Craft
|
||||
* Oliver Caldwell
|
||||
* Jonas Schürmann
|
||||
* error800
|
||||
* Liam BEGUIN
|
||||
* skinnay
|
||||
* Zach-Button
|
||||
* Tomasz Kramkowski
|
||||
* Ismail S
|
||||
* Halfwit
|
||||
* David Vogt
|
||||
* rikn00
|
||||
* kanikaa1234
|
||||
* haitaka
|
||||
* Nick Ginther
|
||||
* Michał Góral
|
||||
* Michael Ilsaas
|
||||
* Martin Zimmermann
|
||||
* Fritz Reichwald
|
||||
* Brian Jackson
|
||||
* sbinix
|
||||
* neeasade
|
||||
* jnphilipp
|
||||
* Tobias Patzl
|
||||
* Stefan Tatschner
|
||||
* Samuel Loury
|
||||
* Peter Michely
|
||||
* Panashe M. Fundira
|
||||
* Link
|
||||
* Larry Hynes
|
||||
* Johannes Altmanninger
|
||||
* Jeremy Kaplan
|
||||
* Ismail
|
||||
* Edgar Hipp
|
||||
* Daryl Finlay
|
||||
* adam
|
||||
* Samir Benmendil
|
||||
* Regina Hug
|
||||
* Mathias Fussenegger
|
||||
* Marcelo Santos
|
||||
* Jean-Louis Fuchs
|
||||
* Fritz V155 Reichwald
|
||||
* Franz Fellner
|
||||
* zwarag
|
||||
* xd1le
|
||||
* oniondreams
|
||||
* issue
|
||||
* haxwithaxe
|
||||
* evan
|
||||
* dylan araps
|
||||
* Xitian9
|
||||
* Tomas Orsava
|
||||
* Tobias Werth
|
||||
* Tim Harder
|
||||
* Thiago Barroso Perrotta
|
||||
* Sorokin Alexei
|
||||
* Noah Huesser
|
||||
* Matthias Lisin
|
||||
* Marcel Schilling
|
||||
* Johannes Martinsson
|
||||
* Jean-Christophe Petkovich
|
||||
* Jay Kamat
|
||||
* Helen Sherwood-Taylor
|
||||
* HalosGhost
|
||||
* Gregor Pohl
|
||||
* Eivind Uggedal
|
||||
* Dietrich Daroch
|
||||
* Daniel Lu
|
||||
* Arseniy Seroka
|
||||
* Andy Balaam
|
||||
* Andreas Fischer
|
||||
// QUTE_AUTHORS_END
|
||||
|
||||
The following people have contributed graphics:
|
||||
|
||||
* Jad/link:http://yelostudio.com[yelo] (new icon)
|
||||
* WOFall (original icon)
|
||||
* regines (key binding cheatsheet)
|
||||
|
||||
Also, thanks to everyone who contributed to one of qutebrowser's
|
||||
link:doc/backers.asciidoc[crowdfunding campaigns]!
|
||||
Thanks / Similar projects
|
||||
-------------------------
|
||||
|
||||
Similar projects
|
||||
----------------
|
||||
Many projects with a similar goal as qutebrowser exist:
|
||||
|
||||
* http://portix.bitbucket.org/dwb/[dwb] (C, GTK+ with WebKit1, currently
|
||||
http://www.reddit.com/r/linux/comments/2huqbc/dwb_abandoned/[unmaintained] -
|
||||
main inspiration for qutebrowser)
|
||||
* https://github.com/fanglingsu/vimb[vimb] (C, GTK+ with WebKit1, active)
|
||||
* http://sourceforge.net/p/vimprobable/wiki/Home/[vimprobable] (C, GTK+ with
|
||||
WebKit1, dead)
|
||||
* http://surf.suckless.org/[surf] (C, GTK+ with WebKit1, active)
|
||||
* https://mason-larobina.github.io/luakit/[luakit] (C/Lua, GTK+ with
|
||||
WebKit1, not very active)
|
||||
* http://pwmt.org/projects/jumanji/[jumanji] (C, GTK+ with WebKit1, not very
|
||||
active)
|
||||
* http://www.uzbl.org/[uzbl] (C, GTK+ with WebKit1/WebKit2, active)
|
||||
* http://conkeror.org/[conkeror] (Javascript, Emacs-like, XULRunner/Gecko,
|
||||
active)
|
||||
* https://github.com/AeroNotix/lispkit[lispkit] (quite new, lisp, GTK+ with
|
||||
WebKit, active)
|
||||
* http://www.vimperator.org/[Vimperator] (Firefox addon)
|
||||
* http://5digits.org/pentadactyl/[Pentadactyl] (Firefox addon)
|
||||
* https://github.com/akhodakivskiy/VimFx[VimFx] (Firefox addon)
|
||||
* https://github.com/1995eaton/chromium-vim[cVim] (Chrome/Chromium addon)
|
||||
* http://vimium.github.io/[vimium] (Chrome/Chromium addon)
|
||||
* https://chrome.google.com/webstore/detail/vichrome/gghkfhpblkcmlkmpcpgaajbbiikbhpdi?hl=en[ViChrome] (Chrome/Chromium addon)
|
||||
* https://github.com/jinzhu/vrome[Vrome] (Chrome/Chromium addon)
|
||||
|
||||
Many projects with a similar goal as qutebrowser exist.
|
||||
Most of them were inspirations for qutebrowser in some way, thanks for that!
|
||||
|
||||
Active
|
||||
~~~~~~
|
||||
Thanks as well to the following projects and people for helping me with
|
||||
problems and helpful hints:
|
||||
|
||||
* https://fanglingsu.github.io/vimb/[vimb] (C, GTK+ with WebKit2)
|
||||
* https://luakit.github.io/luakit/[luakit] (C/Lua, GTK+ with WebKit2)
|
||||
* http://surf.suckless.org/[surf] (C, GTK+ with WebKit1/WebKit2)
|
||||
* http://www.uzbl.org/[uzbl] (C, GTK+ with WebKit1/WebKit2)
|
||||
* Chrome/Chromium addons:
|
||||
https://github.com/1995eaton/chromium-vim[cVim],
|
||||
http://vimium.github.io/[Vimium],
|
||||
https://github.com/brookhong/Surfingkeys[Surfingkeys],
|
||||
https://key.saka.io/[Saka Key]
|
||||
* Firefox addons (based on WebExtensions):
|
||||
https://addons.mozilla.org/en-GB/firefox/addon/vimium-ff/[Vimium-FF] (experimental),
|
||||
https://key.saka.io[Saka Key],
|
||||
https://github.com/ueokande/vim-vixen[Vim Vixen],
|
||||
https://github.com/shinglyu/QuantumVim[QuantumVim],
|
||||
https://github.com/cmcaine/tridactyl[Tridactyl] (working
|
||||
on a https://bugzilla.mozilla.org/show_bug.cgi?id=1215061[better API] for
|
||||
keyboard integration in Firefox).
|
||||
* http://eric-ide.python-projects.org/[eric5] / Detlev Offenbach
|
||||
* https://code.google.com/p/devicenzo/[devicenzo]
|
||||
* portix
|
||||
* seir
|
||||
* nitroxleecher
|
||||
|
||||
Inactive
|
||||
~~~~~~~~
|
||||
Also, thanks to:
|
||||
|
||||
* https://bitbucket.org/portix/dwb[dwb] (C, GTK+ with WebKit1,
|
||||
https://bitbucket.org/portix/dwb/pull-requests/22/several-cleanups-to-increase-portability/diff[unmaintained] -
|
||||
main inspiration for qutebrowser)
|
||||
* http://sourceforge.net/p/vimprobable/wiki/Home/[vimprobable] (C, GTK+ with
|
||||
WebKit1)
|
||||
* http://pwmt.org/projects/jumanji/[jumanji] (C, GTK+ with WebKit1)
|
||||
* http://conkeror.org/[conkeror] (Javascript, Emacs-like, XULRunner/Gecko)
|
||||
* Firefox addons (not based on WebExtensions or no recent activity):
|
||||
http://www.vimperator.org/[Vimperator],
|
||||
http://5digits.org/pentadactyl/[Pentadactyl],
|
||||
https://github.com/akhodakivskiy/VimFx[VimFx],
|
||||
* Chrome/Chromium addons:
|
||||
https://chrome.google.com/webstore/detail/vichrome/gghkfhpblkcmlkmpcpgaajbbiikbhpdi?hl=en[ViChrome],
|
||||
https://github.com/jinzhu/vrome[Vrome]
|
||||
* Everyone contributing to the link:doc/backers.asciidoc[crowdfunding].
|
||||
* Everyone who had the patience to test qutebrowser before v0.1.
|
||||
* Everyone triaging/fixing my bugs in the
|
||||
https://bugreports.qt.io/secure/Dashboard.jspa[Qt bugtracker]
|
||||
* Everyone answering my questions on http://stackoverflow.com/[Stack Overflow]
|
||||
and in IRC.
|
||||
* All the projects which were a great help while developing qutebrowser.
|
||||
|
||||
License
|
||||
-------
|
||||
@@ -218,7 +315,7 @@ 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 this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pdf.js
|
||||
------
|
||||
|
||||
9
codecov.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
status:
|
||||
project:
|
||||
enabled: no
|
||||
patch:
|
||||
enabled: no
|
||||
changes:
|
||||
enabled: no
|
||||
|
||||
comment: off
|
||||
@@ -1,186 +1,13 @@
|
||||
Crowdfunding backers
|
||||
====================
|
||||
|
||||
2017
|
||||
----
|
||||
|
||||
Mid-2017, qutebrowser had its
|
||||
https://www.kickstarter.com/projects/the-compiler/qutebrowser-v10-with-per-domain-settings[second crowdfunding]
|
||||
with the goal of implementing the new config system and releasing v1.0.
|
||||
|
||||
Thanks a lot to the following people who contributed to it:
|
||||
|
||||
Gold sponsors
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
- Iggy
|
||||
- zwitschi
|
||||
- 2x Anonymous
|
||||
|
||||
Silver sponsors
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
- https://benary.org[benaryorg]
|
||||
- https://scratchbook.ch[Claude]
|
||||
- Martin Tournoij
|
||||
- http://supported.elsensohn.ch[Thomas Elsensohn]
|
||||
- Christian Helbling
|
||||
- Gavin Troy
|
||||
- Chris King-Parra
|
||||
- Tim Das Mool Wegener
|
||||
|
||||
Other sponsors
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
- 7scan
|
||||
- AMD1212
|
||||
- Alex
|
||||
- Alex Suykov
|
||||
- Alexey Zhikhartsev
|
||||
- Allan Nordhøy
|
||||
- Anirudh Sanjeev
|
||||
- Anssi Puustinen
|
||||
- Anton Grensjö
|
||||
- Aristaeus
|
||||
- Armin Fisslthaler
|
||||
- Ashley Hauck
|
||||
- Benedikt Steindorf
|
||||
- Bernardo Kuri
|
||||
- Blaise Duszynski
|
||||
- Bostan
|
||||
- Bruno Oliveira
|
||||
- BunnyApocalypse
|
||||
- Christian Kellermann
|
||||
- Colin Jacobs
|
||||
- Daniel Andersson
|
||||
- Daniel Nelson
|
||||
- Daniel P. Schmidt
|
||||
- Daniel Salby
|
||||
- Danilo
|
||||
- David Beley
|
||||
- David Hollings
|
||||
- David Keijser
|
||||
- David Parrish
|
||||
- Derin Yarsuvat
|
||||
- Dmytro Kostiuchenko
|
||||
- Eero Kari
|
||||
- Epictek
|
||||
- Eric
|
||||
- Faure Hu
|
||||
- Ferus
|
||||
- Frederik Thorøe
|
||||
- G4v4g4i
|
||||
- Granitosaurus
|
||||
- Gyula Teleki
|
||||
- H
|
||||
- Heinz Bruhin
|
||||
- Hosaka
|
||||
- Ihor Radchenko
|
||||
- Iordanis Grigoriou
|
||||
- Isaac Sandaljian
|
||||
- Jakub Podeszwik
|
||||
- Jamie Anderson
|
||||
- Jasper Woudenberg
|
||||
- Jay Kamat
|
||||
- Jens Højgaard
|
||||
- Johannes
|
||||
- John Baber-Lucero
|
||||
- Jonas Schürmann
|
||||
- Kenichiro Ito
|
||||
- Kenny Low
|
||||
- Lars Ivar Igesund
|
||||
- Leulas
|
||||
- Lucas Aride Moulin
|
||||
- Ludovic Chabant
|
||||
- Lukas Gierth
|
||||
- Magnus Lindström
|
||||
- Marulkan
|
||||
- Matthew Chun-Lum
|
||||
- Matthew Cronen
|
||||
- Matthew Quigley
|
||||
- Michael Schönwälder
|
||||
- Mika Kutila
|
||||
- Mitchell Stokes
|
||||
- Nathan Howell
|
||||
- Nathan Schlehlein
|
||||
- Noël Zindel
|
||||
- Obri
|
||||
- Patrik Peng
|
||||
- Peter DiMarco
|
||||
- Peter Rice
|
||||
- Philipp Middendorf
|
||||
- Pkill9
|
||||
- PluMGMK
|
||||
- Prescott
|
||||
- ProXicT
|
||||
- Ram-Z
|
||||
- Robotichead
|
||||
- Roshless
|
||||
- Ryan Ellis
|
||||
- Ryan P Deslandes
|
||||
- Sam Doshi
|
||||
- Sam Stone
|
||||
- Sean Herman
|
||||
- Sebastian Frysztak
|
||||
- Shelby Cruver
|
||||
- Simon Désaulniers
|
||||
- SirCmpwn
|
||||
- Soham Pal
|
||||
- Stephan Jauernick
|
||||
- Stewart Webb
|
||||
- Sven Reinecke
|
||||
- Timothée Floure
|
||||
- Tom Bass
|
||||
- Tom Kirchner
|
||||
- Tomas Slusny
|
||||
- Tomasz Kramkowski
|
||||
- Tommy Thomas
|
||||
- Tuscan
|
||||
- Ulrich Pötter
|
||||
- Vasilij Schneidermann
|
||||
- Vlaaaaaaad
|
||||
- XTaran
|
||||
- Z2h-A6n
|
||||
- ayekat
|
||||
- beanieuptop
|
||||
- cee
|
||||
- craftyguy
|
||||
- demure
|
||||
- dlangevi
|
||||
- epon
|
||||
- evenorbert
|
||||
- fishss
|
||||
- gsnewmark
|
||||
- guillermohs9
|
||||
- hernani
|
||||
- hubcaps
|
||||
- jnphilipp
|
||||
- lobachevsky
|
||||
- neodarz
|
||||
- nihlaeth
|
||||
- notbenh
|
||||
- nyctea
|
||||
- ongy
|
||||
- patrick suwanvithaya
|
||||
- pyratebeard
|
||||
- p≡p foundation
|
||||
- randm_dave
|
||||
- sabreman
|
||||
- toml
|
||||
- vimja
|
||||
- wiz
|
||||
- 48 Anonymous
|
||||
|
||||
2016
|
||||
----
|
||||
|
||||
Mid-2016, qutebrowser did run a http://igg.me/at/qutebrowser[crowdfunding] for
|
||||
QtWebEngine support in qutebrowser.
|
||||
|
||||
Thanks a lot to the following people who contributed to it:
|
||||
|
||||
Gold sponsors
|
||||
~~~~~~~~~~~~~
|
||||
-------------
|
||||
|
||||
- Chris Salzberg
|
||||
- Clayton Craft
|
||||
@@ -189,7 +16,7 @@ Gold sponsors
|
||||
- 1 Anonymous
|
||||
|
||||
Day sponsors
|
||||
~~~~~~~~~~~~
|
||||
------------
|
||||
|
||||
- Agent 42
|
||||
- Iggy Jackson
|
||||
@@ -201,7 +28,7 @@ Day sponsors
|
||||
- 4 Anonymous
|
||||
|
||||
Other sponsors
|
||||
~~~~~~~~~~~~~~
|
||||
--------------
|
||||
|
||||
- AP M
|
||||
- Alessandro Balzano
|
||||
@@ -263,7 +90,6 @@ Other sponsors
|
||||
- Julie Engel
|
||||
- Jörg Behrmann
|
||||
- Jørgen Skancke
|
||||
- Kevin Kainan Li
|
||||
- Kevin Velghe
|
||||
- Konstantin Shmelkov
|
||||
- Kyle Frazer
|
||||
|
||||
304
doc/faq.asciidoc
@@ -1,304 +0,0 @@
|
||||
Frequently asked questions
|
||||
==========================
|
||||
:title: Frequently asked questions
|
||||
The Compiler <mail@qutebrowser.org>
|
||||
|
||||
[qanda]
|
||||
What is qutebrowser based on?::
|
||||
qutebrowser uses http://www.python.org/[Python], http://qt.io/[Qt] and
|
||||
http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
+
|
||||
The concept of it is largely inspired by http://portix.bitbucket.org/dwb/[dwb]
|
||||
and http://www.vimperator.org/vimperator[Vimperator]. Many actions and
|
||||
key bindings are similar to dwb.
|
||||
|
||||
Why another browser?::
|
||||
It might be hard to believe, but I didn't find any browser which I was
|
||||
happy with, so I started to write my own. Also, I needed a project to get
|
||||
into writing GUI applications with Python and
|
||||
link:http://qt.io/[Qt]/link:http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
+
|
||||
Read the next few questions to find out why I was unhappy with existing
|
||||
software.
|
||||
|
||||
What's wrong with link:http://portix.bitbucket.org/dwb/[dwb]/link:http://sourceforge.net/projects/vimprobable/[vimprobable]/link:https://mason-larobina.github.io/luakit/[luakit]/link:http://pwmt.org/projects/jumanji/[jumanji]/... (projects based on WebKitGTK)?::
|
||||
Most of them are based on the http://webkitgtk.org/[WebKitGTK+]
|
||||
http://webkitgtk.org/reference/webkitgtk/stable/index.html[WebKit1] API,
|
||||
which causes a lot of crashes. As the GTK API using WebKit1 is
|
||||
https://lists.webkit.org/pipermail/webkit-gtk/2014-March/001821.html[deprecated],
|
||||
these bugs are never going to be fixed.
|
||||
+
|
||||
When qutebrowser was created, the newer
|
||||
http://webkitgtk.org/reference/webkit2gtk/stable/index.html[WebKit2 API] lacked
|
||||
basic features like proxy support, and almost no projects have started porting
|
||||
to WebKit2. In the meantime, this situation has improved a bit, but there are
|
||||
still only a few projects which have some kind of WebKit2 support (see the
|
||||
https://github.com/qutebrowser/qutebrowser#similar-projects[list of
|
||||
alternatives]).
|
||||
+
|
||||
qutebrowser uses http://qt.io/[Qt] and
|
||||
http://wiki.qt.io/QtWebEngine[QtWebEngine] by default (and supports
|
||||
http://wiki.qt.io/QtWebKit[QtWebKit] optionally). QtWebEngine is based on
|
||||
Google's https://www.chromium.org/Home[Chromium]. With an up-to-date Qt, it has
|
||||
much more man-power behind it than WebKitGTK+ has, and thus supports more modern
|
||||
web features - it's also arguably more secure.
|
||||
|
||||
What's wrong with https://www.mozilla.org/en-US/firefox/new/[Firefox] and link:http://5digits.org/pentadactyl/[Pentadactyl]/link:http://www.vimperator.org/vimperator[Vimperator]?::
|
||||
Firefox likes to break compatibility with addons on each upgrade, gets
|
||||
slower and more bloated with every upgrade, and has some
|
||||
https://blog.mozilla.org/advancingcontent/2014/02/11/publisher-transformation-with-users-at-the-center/[horrible
|
||||
ideas] lately.
|
||||
+
|
||||
Also, developing addons for it is a nightmare.
|
||||
|
||||
What's wrong with http://www.chromium.org/Home[Chromium] and https://vimium.github.io/[Vimium]?::
|
||||
The Chrome plugin API doesn't seem to allow much freedom for plugin
|
||||
writers, which results in Vimium not really having all the features you'd
|
||||
expect from a proper minimal, vim-like browser.
|
||||
|
||||
Why Python?::
|
||||
I enjoy writing Python since 2011, which made it one of the possible
|
||||
choices. I wanted to use http://qt.io/[Qt] because of
|
||||
http://wiki.qt.io/QtWebKit[QtWebKit] so I didn't have
|
||||
http://wiki.qt.io/Category:LanguageBindings[many other choices]. I don't
|
||||
like C++ and can't write it very well, so that wasn't an alternative.
|
||||
|
||||
But isn't Python too slow for a browser?::
|
||||
http://www.infoworld.com/d/application-development/van-rossum-python-not-too-slow-188715[No.]
|
||||
I believe efficiency while coding is a lot more important than efficiency
|
||||
while running. Also, most of the heavy lifting of qutebrowser is done by Qt
|
||||
and WebKit in C++, with the
|
||||
https://wiki.python.org/moin/GlobalInterpreterLock[GIL] released.
|
||||
|
||||
Is qutebrowser secure?::
|
||||
Most security issues are in the backend (which handles networking,
|
||||
rendering, JavaScript, etc.) and not qutebrowser itself.
|
||||
+
|
||||
qutebrowser uses http://wiki.qt.io/QtWebEngine[QtWebEngine] by default.
|
||||
QtWebEngine is based on Google's https://www.chromium.org/Home[Chromium]. While
|
||||
Qt only updates to a new Chromium release on every minor Qt release (all ~6
|
||||
months), every patch release backports security fixes from newer Chromium
|
||||
versions. In other words: As long as you're using an up-to-date Qt, you should
|
||||
be recieving security updates on a regular basis, without qutebrowser having to
|
||||
do anything. Chromium's process isolation and
|
||||
https://chromium.googlesource.com/chromium/src/+/master/docs/design/sandbox.md[sandboxing]
|
||||
features are also enabled as a second line of defense.
|
||||
+
|
||||
http://wiki.qt.io/QtWebKit[QtWebKit] is also supported as an alternative
|
||||
backend, but hasn't seen new releases
|
||||
https://github.com/annulen/webkit/releases[in a while]. It also doesn't have any
|
||||
process isolation or sandboxing.
|
||||
+
|
||||
Security issues in qutebrowser's code happen very rarely (as per March 2018,
|
||||
there has been one security issue caused by qutebrowser in over four years) and
|
||||
are fixed timely. To report security bugs, please contact me directly at
|
||||
mail@qutebrowser.org, GPG ID
|
||||
https://www.the-compiler.org/pubkey.asc[0x916eb0c8fd55a072].
|
||||
|
||||
Is there an adblocker?::
|
||||
There is a host-based adblocker which takes /etc/hosts-like lists. A "real"
|
||||
adblocker has a
|
||||
http://www.reddit.com/r/programming/comments/25j41u/adblock_pluss_effect_on_firefoxs_memory_usage/chhpomw[big
|
||||
impact] on browsing speed and
|
||||
https://blog.mozilla.org/nnethercote/2014/05/14/adblock-pluss-effect-on-firefoxs-memory-usage/[RAM
|
||||
usage], so implementing support for AdBlockPlus-like lists is currently not
|
||||
a priority.
|
||||
|
||||
How do I play Youtube videos with mpv?::
|
||||
You can easily add a key binding to play youtube videos inside a real video
|
||||
player - optionally even with hinting for links:
|
||||
+
|
||||
----
|
||||
:bind m spawn mpv {url}
|
||||
:bind M hint links spawn mpv {hint-url}
|
||||
----
|
||||
+
|
||||
Note that you might need an additional package (e.g.
|
||||
https://www.archlinux.org/packages/community/any/youtube-dl/[youtube-dl] on
|
||||
Archlinux) to play web videos with mpv.
|
||||
+
|
||||
There is a very useful script for mpv, which emulates "unique application"
|
||||
functionality. This way you can add links to the mpv playlist instead of
|
||||
playing them all at once.
|
||||
+
|
||||
You can find the script here: https://github.com/mpv-player/mpv/blob/master/TOOLS/umpv
|
||||
+
|
||||
It also works nicely with rapid hints:
|
||||
+
|
||||
----
|
||||
:bind m spawn umpv {url}
|
||||
:bind M hint links spawn umpv {hint-url}
|
||||
:bind ;M hint --rapid links spawn umpv {hint-url}
|
||||
----
|
||||
|
||||
How do I use qutebrowser with mutt?::
|
||||
Due to a Qt limitation, local files without `.html` extensions are
|
||||
"downloaded" instead of displayed, see
|
||||
https://github.com/qutebrowser/qutebrowser/issues/566[#566]. You can work
|
||||
around this by using this in your `mailcap`:
|
||||
+
|
||||
----
|
||||
text/html; mv %s %s.html && qutebrowser %s.html >/dev/null 2>/dev/null; needsterminal;
|
||||
----
|
||||
|
||||
What is the difference between bookmarks and quickmarks?::
|
||||
Bookmarks will always use the title of the website as their name, but with quickmarks
|
||||
you can set your own title.
|
||||
+
|
||||
For example, if you bookmark multiple food recipe websites and use `:open`,
|
||||
you have to type the title or address of the website.
|
||||
+
|
||||
When using quickmark, you can give them all names, like
|
||||
`foodrecipes1`, `foodrecipes2` and so on. When you type
|
||||
`:open foodrecipes`, you will see a list of all the food recipe sites,
|
||||
without having to remember the exact website title or address.
|
||||
|
||||
How do I use spell checking?::
|
||||
Configuring spell checking in qutebrowser depends on the backend in use
|
||||
(see https://github.com/qutebrowser/qutebrowser/issues/700[#700] for
|
||||
a more detailed discussion).
|
||||
+
|
||||
For QtWebKit:
|
||||
|
||||
. Install https://github.com/QupZilla/qtwebkit-plugins[qtwebkit-plugins].
|
||||
. Note: with QtWebKit reloaded you may experience some issues. See
|
||||
https://github.com/QupZilla/qtwebkit-plugins/issues/10[#10].
|
||||
. The dictionary to use is taken from the `DICTIONARY` environment variable.
|
||||
The default is `en_US`. For example to use Dutch spell check set `DICTIONARY`
|
||||
to `nl_NL`; you can't use multiple dictionaries or change them at runtime at
|
||||
the moment.
|
||||
(also see the README file for `qtwebkit-plugins`).
|
||||
. Remember to install the hunspell dictionaries if you don't have them already
|
||||
(most distros should have packages for this).
|
||||
|
||||
+
|
||||
For QtWebEngine:
|
||||
|
||||
. Make sure your versions of PyQt and Qt are 5.8 or higher.
|
||||
. Use `dictcli.py` script to install dictionaries.
|
||||
Run the script with `-h` for the parameter description.
|
||||
. Set `spellcheck.languages` to the desired list of languages, e.g.:
|
||||
`:set spellcheck.languages "['en-US', 'pl-PL']"`
|
||||
|
||||
How do I use Tor with qutebrowser?::
|
||||
Start tor on your machine, and do `:set content.proxy socks://localhost:9050/`
|
||||
in qutebrowser. Note this won't give you the same amount of fingerprinting
|
||||
protection that the Tor Browser does, but it's useful to be able to access
|
||||
`.onion` sites.
|
||||
|
||||
Why does J move to the next (right) tab, and K to the previous (left) one?::
|
||||
One reason is because https://bitbucket.org/portix/dwb[dwb] did it that way,
|
||||
and qutebrowser's keybindings are designed to be compatible with dwb's.
|
||||
The rationale behind it is that J is "down" in vim, and K is "up", which
|
||||
corresponds nicely to "next"/"previous". It also makes much more sense with
|
||||
vertical tabs (e.g. `:set tabs.position left`).
|
||||
|
||||
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.
|
||||
|
||||
Why takes it longer to open an URL in qutebrowser than in chromium?::
|
||||
When opening an URL in an existing instance the normal qutebrowser
|
||||
Python script is started and a few PyQt libraries need to be
|
||||
loaded until it is detected that there is an instance running
|
||||
where the URL is then passed to. This takes some time.
|
||||
One workaround is to use this
|
||||
https://github.com/qutebrowser/qutebrowser/blob/master/scripts/open_url_in_instance.sh[script]
|
||||
and place it in your $PATH with the name "qutebrowser". This
|
||||
script passes the URL via an unix socket to qutebrowser (if its
|
||||
running already) using socat which is much faster and starts a new
|
||||
qutebrowser if it is not running already. Also check if you want
|
||||
to use webengine as backend in line 17 and change it to your
|
||||
needs.
|
||||
|
||||
How do I make qutebrowser use greasemonkey scripts?::
|
||||
There is currently no UI elements to handle managing greasemonkey scripts.
|
||||
All management of what scripts are installed or disabled is done in the
|
||||
filesystem by you. qutebrowser reads all files that have an extension of
|
||||
`.js` from the `<data>/greasemonkey/` folder and attempts to load them.
|
||||
Where `<data>` is the qutebrowser data directory shown in the `Paths`
|
||||
section of the page displayed by `:version`. If you want to disable a
|
||||
script just rename it, for example, to have `.disabled` on the end, after
|
||||
the `.js` extension. To reload scripts from that directory run the command
|
||||
`:greasemonkey-reload`.
|
||||
+
|
||||
Troubleshooting: to check that your script is being loaded when
|
||||
`:greasemonkey-reload` runs you can start qutebrowser with the arguments
|
||||
`--debug --logfilter greasemonkey,js` and check the messages on the
|
||||
program's standard output for errors parsing or loading your script.
|
||||
You may also see javascript errors if your script is expecting an environment
|
||||
that we fail to provide.
|
||||
+
|
||||
Note that there are some missing features which you may run into:
|
||||
|
||||
. Some scripts expect `GM_xmlhttpRequest` to ignore Cross Origin Resource
|
||||
Sharing restrictions, this is currently not supported, so scripts making
|
||||
requests to third party sites will often fail to function correctly.
|
||||
. If your backend is a QtWebEngine version 5.8, 5.9 or 5.10 then regular
|
||||
expressions are not supported in `@include` or `@exclude` rules. If your
|
||||
script uses them you can re-write them to use glob expressions or convert
|
||||
them to `@match` rules.
|
||||
See https://wiki.greasespot.net/Metadata_Block[the wiki] for more info.
|
||||
. Any greasemonkey API function to do with adding UI elements is not currently
|
||||
supported. That means context menu extentensions and background pages.
|
||||
|
||||
== Troubleshooting
|
||||
|
||||
Unable to view flash content.::
|
||||
If you have flash installed for on your system, it's necessary to enable plugins
|
||||
to use the flash plugin. Using the command `:set content.plugins true`
|
||||
in qutebrowser will enable plugins. Packages for flash should
|
||||
be provided for your platform or it can be obtained from
|
||||
http://get.adobe.com/flashplayer/[Adobe].
|
||||
|
||||
Experiencing freezing on sites like duckduckgo and youtube.::
|
||||
This issue could be caused by stale plugin files installed by `mozplugger`
|
||||
if mozplugger was subsequently removed.
|
||||
Try exiting qutebrowser and removing `~/.mozilla/plugins/mozplugger*.so`.
|
||||
See https://github.com/qutebrowser/qutebrowser/issues/357[Issue #357]
|
||||
for more details.
|
||||
|
||||
When using QtWebEngine, qutebrowser reports "Render Process Crashed" and the console prints a traceback on Gentoo Linux or another Source-Based Distro::
|
||||
As stated in https://gcc.gnu.org/gcc-6/changes.html[GCC's Website] GCC 6 has introduced some optimizations that could break non-conforming codebases, like QtWebEngine. +
|
||||
As a workaround, you can disable the nullpointer check optimization by adding the -fno-delete-null-pointer-checks flag while compiling. +
|
||||
On gentoo, you just need to add it into your make.conf, like this: +
|
||||
|
||||
CFLAGS="... -fno-delete-null-pointer-checks"
|
||||
CXXFLAGS="... -fno-delete-null-pointer-checks"
|
||||
+
|
||||
And then re-emerging qtwebengine with: +
|
||||
|
||||
emerge -1 qtwebengine
|
||||
|
||||
Unable to view DRM content (Netflix, Spotify, etc.).::
|
||||
You will need to install `widevine` and set `qt.args` to point to it.
|
||||
Qt 5.9 currently only supports widevine up to Chrome version 61.
|
||||
+
|
||||
On Arch, simply install `qt5-webengine-widevine` from the AUR and run:
|
||||
+
|
||||
----
|
||||
:set qt.args '["ppapi-widevine-path=/usr/lib/qt/plugins/ppapi/libwidevinecdmadapter.so"]'
|
||||
:restart
|
||||
----
|
||||
+
|
||||
For other distributions, download the chromium tarball and widevine-cdm zip from
|
||||
https://aur.archlinux.org/packages/qt5-webengine-widevine/[the AUR page],
|
||||
extract `libwidevinecdmadapter.so` and `libwidevinecdm.so` files, respectively,
|
||||
and move them to the `ppapi` plugin directory in your Qt library directory (create it if it does not exist).
|
||||
+
|
||||
Lastly, set your `qt.args` to point to that directory and restart qutebrowser:
|
||||
+
|
||||
----
|
||||
:set qt.args '["ppapi-widevine-path=/usr/lib64/qt5/plugins/ppapi/libwidevinecdmadapter.so"]'
|
||||
:restart
|
||||
----
|
||||
|
||||
My issue is not listed.::
|
||||
If you experience any segfaults or crashes, you can report the issue in
|
||||
https://github.com/qutebrowser/qutebrowser/issues[the issue tracker] or
|
||||
using the `:report` command.
|
||||
If you are reporting a segfault, make sure you read the
|
||||
link:stacktrace.asciidoc[guide] on how to report them with all needed
|
||||
information.
|
||||
@@ -1,458 +0,0 @@
|
||||
Configuring qutebrowser
|
||||
=======================
|
||||
|
||||
IMPORTANT: qutebrowser's configuration system was completely rewritten in
|
||||
September 2017. This information is not applicable to older releases, and older
|
||||
information elsewhere might be outdated.
|
||||
|
||||
qutebrowser's config files
|
||||
--------------------------
|
||||
|
||||
qutebrowser releases before v1.0.0 had a `qutebrowser.conf` and `keys.conf`
|
||||
file. Those are not used anymore since that release - see
|
||||
<<migrating,"Migrating older configurations">> for information on how to
|
||||
migrate to the new config.
|
||||
|
||||
When using `:set` and `:bind`, changes are saved to an `autoconfig.yml` file
|
||||
automatically. If you don't want to have a config file which is curated by
|
||||
hand, you can simply use those - see
|
||||
<<autoconfig,"Configuring qutebrowser via the user interface">> for details.
|
||||
|
||||
For more advanced configuration, you can write a `config.py` file - see
|
||||
<<configpy,"Configuring qutebrowser via config.py">>. As soon as a `config.py`
|
||||
exists, the `autoconfig.yml` file **is not read anymore** by default. You need
|
||||
to <<configpy-autoconfig,load it by hand>> if you want settings done via
|
||||
`:set`/`:bind` to still persist.
|
||||
|
||||
[[autoconfig]]
|
||||
Configuring qutebrowser via the user interface
|
||||
----------------------------------------------
|
||||
|
||||
The easy (but less flexible) way to configure qutebrowser is using its user
|
||||
interface or command line. Changes you make this way are immediately active
|
||||
(with the exception of a few settings, where this is pointed out in the
|
||||
documentation) and are persisted in an `autoconfig.yml` file.
|
||||
|
||||
The `autoconfig.yml` file is located in the "config" folder listed on the
|
||||
link:qute://version[] page. On macOS, the "auto config" folder is used, which is
|
||||
different from where hand-written config files are kept.
|
||||
|
||||
However, **do not** edit `autoconfig.yml` by hand. Instead, see the next
|
||||
section.
|
||||
|
||||
If you want to customize many settings, you can open the link:qute://settings[]
|
||||
page by running `:set` without any arguments, where all settings are listed and
|
||||
customizable.
|
||||
|
||||
Using the link:commands.html#set[`:set`] command and command completion, you
|
||||
can quickly set settings interactively, for example `:set tabs.position left`.
|
||||
|
||||
Some settings are also customizable for a given
|
||||
https://developer.chrome.com/apps/match_patterns[URL pattern] by doing e.g.
|
||||
`:set --pattern=*://example.com/ content.images false`.
|
||||
|
||||
To get more help about a setting, use e.g. `:help tabs.position`.
|
||||
|
||||
To bind and unbind keys, you can use the link:commands.html#bind[`:bind`] and
|
||||
link:commands.html#unbind[`:unbind`] commands:
|
||||
|
||||
- Binding the key chain `,v` to the `:spawn mpv {url}` command:
|
||||
`:bind ,v spawn mpv {url}`
|
||||
- Unbinding the same key chain: `:unbind ,v`
|
||||
|
||||
Key chains starting with a comma are ideal for custom bindings, as the comma key
|
||||
will never be used in a default keybinding.
|
||||
|
||||
See the help pages linked above (or `:help :bind`, `:help :unbind`) for more
|
||||
information.
|
||||
|
||||
Other useful commands for config manipulation are
|
||||
link:commands.html#config-unset[`:config-unset`] to reset a value to its default,
|
||||
link:commands.html#config-clear[`:config-clear`] to reset the entire configuration,
|
||||
and link:commands.html#config-cycle[`:config-cycle`] to cycle a setting between
|
||||
different values.
|
||||
|
||||
[[configpy]]
|
||||
Configuring qutebrowser via config.py
|
||||
-------------------------------------
|
||||
|
||||
For more powerful configuration possibilities, you can create a `config.py`
|
||||
file. Since it's a Python file, you have much more flexibility for
|
||||
configuration. Note that qutebrowser will never touch this file - this means
|
||||
you'll be responsible for updating it when upgrading to a newer qutebrowser
|
||||
version.
|
||||
|
||||
You can run `:config-edit` inside qutebrowser to open the file in your editor,
|
||||
`:config-source` to reload the file (`:config-edit` does this automatically), or
|
||||
`:config-write-py --defaults` to write a template file to work with.
|
||||
|
||||
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.
|
||||
|
||||
Two global objects are pre-defined when running `config.py`: `c` and `config`.
|
||||
|
||||
Changing settings
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
While you can set settings using the `config.set()` method (which is explained
|
||||
in the next section), it's easier to use the `c` shorthand object to easily set
|
||||
settings like this:
|
||||
|
||||
.config.py:
|
||||
[source,python]
|
||||
----
|
||||
c.tabs.position = "left"
|
||||
c.completion.shrink = True
|
||||
----
|
||||
|
||||
Note that qutebrowser does some Python magic so it's able to warn you about
|
||||
mistyped config settings. As an example, if you do `c.tabs.possition = "left"`,
|
||||
you'll get an error when starting.
|
||||
|
||||
See the link:settings.html[settings help page] for all available settings. The
|
||||
accepted values depend on the type of the option. Commonly used are:
|
||||
|
||||
- Strings: `c.tabs.position = "left"`
|
||||
- Booleans: `c.completion.shrink = True`
|
||||
- Integers: `c.messages.timeout = 5000`
|
||||
- Dictionaries:
|
||||
* `c.headers.custom = {'X-Hello': 'World', 'X-Awesome': 'yes'}` to override
|
||||
any other values in the dictionary.
|
||||
* `c.aliases['foo'] = 'message-info foo'` to add a single value.
|
||||
- Lists:
|
||||
* `c.url.start_pages = ["https://www.qutebrowser.org/"]` to override any
|
||||
previous elements.
|
||||
* `c.url.start_pages.append("https://www.python.org/")` to add a new value.
|
||||
|
||||
Any other config types (e.g. a color) are specified as a string. The only
|
||||
exception is the `Regex` type, which can take either a string (with an `r`
|
||||
prefix to preserve backslashes) or a Python regex object:
|
||||
|
||||
- `c.hints.next_regexes.append(r'\bvor\b')`
|
||||
- `c.hints.prev_regexes.append(re.compile(r'\bzurück\b'))`
|
||||
|
||||
If you want to read a setting, you can use the `c` object to do so as well:
|
||||
`c.colors.tabs.even.bg = c.colors.tabs.odd.bg`.
|
||||
|
||||
Using strings for setting names
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to set settings based on their name as a string, use the
|
||||
`config.set` method:
|
||||
|
||||
.config.py:
|
||||
[source,python]
|
||||
----
|
||||
# Equivalent to:
|
||||
# c.content.javascript.enabled = False
|
||||
config.set('content.javascript.enabled', False)
|
||||
----
|
||||
|
||||
To read a setting, use the `config.get` method:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
# Equivalent to:
|
||||
# color = c.colors.completion.fg
|
||||
color = config.get('colors.completion.fg')
|
||||
----
|
||||
|
||||
Per-domain settings
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Using `config.set`, some settings are also customizable for a given
|
||||
https://developer.chrome.com/apps/match_patterns[URL pattern]:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
config.set('content.images', False, '*://example.com/')
|
||||
----
|
||||
|
||||
Alternatively, you can use `with config.pattern(...) as p:` to get a shortcut
|
||||
similar to `c.` which is scoped to the given domain:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
with config.pattern('*://example.com/') as p:
|
||||
p.content.images = False
|
||||
----
|
||||
|
||||
Binding keys
|
||||
~~~~~~~~~~~~
|
||||
|
||||
While it's possible to change the `bindings.commands` setting to bind keys, it's
|
||||
preferred to use the `config.bind` command. Doing so ensures the commands are
|
||||
valid and normalizes different expressions which map to the same key.
|
||||
|
||||
For details on how to specify keys and the available modes, see the
|
||||
link:settings.html#bindings.commands[documentation] for the `bindings.commands`
|
||||
setting.
|
||||
|
||||
To bind a key:
|
||||
|
||||
.config.py:
|
||||
[source,python]
|
||||
----
|
||||
config.bind('<Ctrl-v>', 'spawn mpv {url}')
|
||||
----
|
||||
|
||||
To bind a key in a mode other than `'normal'`, add a `mode` argument:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
config.bind('<Ctrl-y>', 'prompt-yes', mode='prompt')
|
||||
----
|
||||
|
||||
To unbind a key (either a key which has been bound before, or a default binding):
|
||||
|
||||
[source,python]
|
||||
----
|
||||
config.unbind('<Ctrl-v>', mode='normal')
|
||||
----
|
||||
|
||||
To bind keys without modifiers, specify a key chain to bind as a string. Key
|
||||
chains starting with a comma are ideal for custom bindings, as the comma key
|
||||
will never be used in a default keybinding.
|
||||
|
||||
[source,python]
|
||||
----
|
||||
config.bind(',v', 'spawn mpv {url}')
|
||||
----
|
||||
|
||||
To suppress loading of any default keybindings, you can set
|
||||
`c.bindings.default = {}`.
|
||||
|
||||
[[configpy-autoconfig]]
|
||||
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:
|
||||
|
||||
.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`.
|
||||
|
||||
Importing other modules
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can import any module from the
|
||||
https://docs.python.org/3/library/index.html[Python standard library] (e.g.
|
||||
`import os.path`), as well as any module installed in the environment
|
||||
qutebrowser is run with.
|
||||
|
||||
If you have an `utils.py` file in your qutebrowser config folder, you can import
|
||||
that via `import utils` as well.
|
||||
|
||||
While it's in some cases possible to import code from the qutebrowser
|
||||
installation, doing so is unsupported and discouraged.
|
||||
|
||||
To read config data from a different file with `c` and `config` available, you
|
||||
can use `config.source('otherfile.py')` in your `config.py`.
|
||||
|
||||
Getting the config directory
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you need to get the qutebrowser config directory, you can do so by reading
|
||||
`config.configdir`. Similarly, you can get the qutebrowser data directory via
|
||||
`config.datadir`.
|
||||
|
||||
This gives you a https://docs.python.org/3/library/pathlib.html[`pathlib.Path`
|
||||
object], on which you can use `/` to add more directory parts, or `str(...)` to
|
||||
get a string:
|
||||
|
||||
.config.py:
|
||||
[source,python]
|
||||
----
|
||||
print(str(config.configdir / 'config.py'))
|
||||
----
|
||||
|
||||
Handling errors
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
If there are errors in your `config.py`, qutebrowser will try to apply as much
|
||||
of it as possible, and show an error dialog before starting.
|
||||
|
||||
qutebrowser tries to display errors which are easy to understand even for people
|
||||
who are not used to writing Python. If you see a config error which you find
|
||||
confusing or you think qutebrowser could handle better, please
|
||||
https://github.com/qutebrowser/qutebrowser/issues[open an issue]!
|
||||
|
||||
Recipes
|
||||
~~~~~~~
|
||||
|
||||
Reading a YAML file
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To read a YAML config like this:
|
||||
|
||||
.config.yml:
|
||||
----
|
||||
tabs.position: left
|
||||
tabs.show: switching
|
||||
----
|
||||
|
||||
You can use:
|
||||
|
||||
.config.py:
|
||||
[source,python]
|
||||
----
|
||||
import yaml
|
||||
|
||||
with (config.configdir / 'config.yml').open() as f:
|
||||
yaml_data = yaml.load(f)
|
||||
|
||||
for k, v in yaml_data.items():
|
||||
config.set(k, v)
|
||||
----
|
||||
|
||||
Reading a nested YAML file
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To read a YAML file with nested values like this:
|
||||
|
||||
.colors.yml:
|
||||
----
|
||||
colors:
|
||||
statusbar:
|
||||
normal:
|
||||
bg: lime
|
||||
fg: black
|
||||
url:
|
||||
fg: red
|
||||
----
|
||||
|
||||
You can use:
|
||||
|
||||
.config.py:
|
||||
[source,python]
|
||||
----
|
||||
import yaml
|
||||
|
||||
with (config.configdir / 'colors.yml').open() as f:
|
||||
yaml_data = yaml.load(f)
|
||||
|
||||
def dict_attrs(obj, path=''):
|
||||
if isinstance(obj, dict):
|
||||
for k, v in obj.items():
|
||||
yield from dict_attrs(v, '{}.{}'.format(path, k) if path else k)
|
||||
else:
|
||||
yield path, obj
|
||||
|
||||
for k, v in dict_attrs(yaml_data):
|
||||
config.set(k, v)
|
||||
----
|
||||
|
||||
Note that this won't work for values which are dictionaries.
|
||||
|
||||
Binding chained commands
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you have a lot of chained commands you want to bind, you can write a helper
|
||||
to do so:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
def bind_chained(key, *commands):
|
||||
config.bind(key, ' ;; '.join(commands))
|
||||
|
||||
bind_chained('<Escape>', 'clear-keychain', 'search')
|
||||
----
|
||||
|
||||
Reading colors from Xresources
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can use something like this to read colors from an `~/.Xresources` file:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
import subprocess
|
||||
|
||||
def read_xresources(prefix):
|
||||
props = {}
|
||||
x = subprocess.run(['xrdb', '-query'], stdout=subprocess.PIPE)
|
||||
lines = x.stdout.decode().split('\n')
|
||||
for line in filter(lambda l : l.startswith(prefix), lines):
|
||||
prop, _, value = line.partition(':\t')
|
||||
props[prop] = value
|
||||
return props
|
||||
|
||||
xresources = read_xresources('*')
|
||||
c.colors.statusbar.normal.bg = xresources['*.background']
|
||||
----
|
||||
|
||||
Pre-built colorschemes
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- A collection of https://github.com/chriskempson/base16[base16] color-schemes can be found in https://github.com/theova/base16-qutebrowser[base16-qutebrowser] and used with https://github.com/AuditeMarlow/base16-manager[base16-manager].
|
||||
- Two implementations of the https://github.com/arcticicestudio/nord[Nord] colorscheme for qutebrowser exist: https://github.com/Linuus/nord-qutebrowser[Linuus], https://github.com/KnownAsDon/QuteBrowser-Nord-Theme[KnownAsDon]
|
||||
|
||||
Avoiding flake8 errors
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you use an editor with flake8 and pylint integration, it may have some
|
||||
complaints about invalid names, undefined variables, or missing docstrings.
|
||||
You can silence those with:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
# pylint: disable=C0111
|
||||
c = c # noqa: F821 pylint: disable=E0602,C0103
|
||||
config = config # noqa: F821 pylint: disable=E0602,C0103
|
||||
----
|
||||
|
||||
For type annotation support (note that those imports aren't guaranteed to be
|
||||
stable across qutebrowser versions):
|
||||
|
||||
[source,python]
|
||||
----
|
||||
# pylint: disable=C0111
|
||||
from qutebrowser.config.configfiles import ConfigAPI # noqa: F401
|
||||
from qutebrowser.config.config import ConfigContainer # noqa: F401
|
||||
config = config # type: ConfigAPI # noqa: F821 pylint: disable=E0602,C0103
|
||||
c = c # type: ConfigContainer # noqa: F821 pylint: disable=E0602,C0103
|
||||
----
|
||||
|
||||
[[migrating]]
|
||||
Migrating older configurations
|
||||
------------------------------
|
||||
|
||||
qutebrowser does no automatic migration for the new configuration. However,
|
||||
there's a special link:qute://configdiff/old[configdiff] page
|
||||
(`qute://configdiff/old`) in qutebrowser, which will show you the changes you
|
||||
did in your old configuration, compared to the old defaults.
|
||||
|
||||
Other changes in default settings:
|
||||
|
||||
- In v1.1.x and newer, `<Up>` and `<Down>` navigate through command history
|
||||
if no text was entered yet.
|
||||
With v1.0.x, they always navigate through command history instead of selecting
|
||||
completion items. Use `<Tab>`/`<Shift-Tab>` to cycle through the completion
|
||||
instead.
|
||||
You can get back the old behavior by doing:
|
||||
+
|
||||
----
|
||||
:bind -m command <Up> completion-item-focus prev
|
||||
:bind -m command <Down> completion-item-focus next
|
||||
----
|
||||
+
|
||||
or always navigate through command history with
|
||||
+
|
||||
----
|
||||
:bind -m command <Up> command-history-prev
|
||||
:bind -m command <Down> command-history-next
|
||||
----
|
||||
|
||||
- The default for `completion.web_history_max_items` is now set to `-1`, showing
|
||||
an unlimited number of items in the completion for `:open` as the new
|
||||
sqlite-based completion is much faster. If the `:open` completion is too slow
|
||||
on your machine, set an appropriate limit again.
|
||||
@@ -6,14 +6,13 @@ Documentation
|
||||
|
||||
The following help pages are currently available:
|
||||
|
||||
* link:../quickstart.html[Quick start guide]
|
||||
* link:../faq.html[Frequently asked questions]
|
||||
* link:../changelog.html[Change Log]
|
||||
* link:quickstart.html[Quick start guide]
|
||||
* link:FAQ.html[Frequently asked questions]
|
||||
* link:CHANGELOG.html[Change Log]
|
||||
* link:commands.html[Documentation of commands]
|
||||
* link:configuring.html[Configuring qutebrowser]
|
||||
* link:settings.html[Documentation of settings]
|
||||
* link:../userscripts.html[How to write userscripts]
|
||||
* link:../contributing.html[Contributing to qutebrowser]
|
||||
* link:userscripts.html[How to write userscripts]
|
||||
* link:CONTRIBUTING.html[Contributing to qutebrowser]
|
||||
|
||||
Getting help
|
||||
------------
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 989 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 32 KiB |
BIN
doc/img/main.png
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 26 KiB |
@@ -1,471 +0,0 @@
|
||||
Installing qutebrowser
|
||||
======================
|
||||
|
||||
toc::[]
|
||||
|
||||
NOTE: qutebrowser recently had some bigger dependency changes for v1.0.0, which
|
||||
means those instructions might be out of date in some places.
|
||||
https://github.com/qutebrowser/qutebrowser/blob/master/doc/contributing.asciidoc[Please help]
|
||||
updating them if you notice something being broken!
|
||||
|
||||
On Debian / Ubuntu
|
||||
------------------
|
||||
|
||||
How to install qutebrowser depends a lot on the version of Debian/Ubuntu you're
|
||||
running.
|
||||
|
||||
Debian Jessie / Ubuntu 14.04 LTS / Linux Mint < 18
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Those distributions only have Python 3.4 and a too old Qt version available,
|
||||
while qutebrowser requires Python 3.5 and Qt 5.7.1 or newer.
|
||||
|
||||
It should be possible to install Python 3.5 e.g. from the
|
||||
https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa[deadsnakes PPA] or via
|
||||
https://github.com/pyenv/pyenv[pyenv], but nobody tried that yet.
|
||||
|
||||
If you get qutebrowser running on those distributions, please
|
||||
https://github.com/qutebrowser/qutebrowser/blob/master/doc/contributing.asciidoc[contribute]
|
||||
to update this documentation!
|
||||
|
||||
Ubuntu 16.04 LTS / Linux Mint 18
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Ubuntu 16.04 doesn't come with an up-to-date engine (a new enough QtWebKit, or
|
||||
QtWebEngine). However, it comes with Python 3.5, so you can
|
||||
<<tox,install qutebrowser via tox>>.
|
||||
|
||||
You'll need some basic libraries to use the tox-installed PyQt:
|
||||
|
||||
----
|
||||
# apt install libglib2.0-0 libgl1 libfontconfig1 libx11-xcb1 libxi6 libxrender1 libdbus-1-3
|
||||
----
|
||||
|
||||
Debian Stretch / Ubuntu 17.04 and 17.10
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Those versions come with QtWebEngine in the repositories. This makes it possible
|
||||
to install qutebrowser via the Debian package.
|
||||
|
||||
You'll need to download three packages:
|
||||
|
||||
- https://packages.debian.org/sid/all/python3-pypeg2/download[PyPEG2] (a library
|
||||
used by qutebrowser which is not in the earlier repositories)
|
||||
- https://packages.debian.org/sid/all/qutebrowser/download[qutebrowser] itself
|
||||
- Either https://packages.debian.org/sid/all/qutebrowser-qtwebengine/download[qutebrowser-qtwebengine]
|
||||
or https://packages.debian.org/sid/all/qutebrowser-qtwebkit/download[qutebrowser-qtwebkit]
|
||||
(or both) depending on the backend you want to use. QtWebEngine is the
|
||||
default/recommended choice.
|
||||
|
||||
After downloading, install the packages (make sure to install all the
|
||||
downloaded qutebrowser deb files in one apt command):
|
||||
|
||||
----
|
||||
# apt install ./python3-pypeg2_*_all.deb
|
||||
# apt install ./qutebrowser*.deb
|
||||
----
|
||||
|
||||
For an update after the initial install, you only need to download/install the
|
||||
qutebrowser package.
|
||||
|
||||
Debian Testing / Ubuntu 18.04
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On Debian Testing, qutebrowser is in the official repositories, and you can
|
||||
install it with apt:
|
||||
|
||||
----
|
||||
# apt install qutebrowser
|
||||
----
|
||||
|
||||
Additional hints
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- Alternatively, you can <<tox,install qutebrowser via tox>> to get a newer
|
||||
QtWebEngine version.
|
||||
- If running from git, run the following to generate the documentation for the
|
||||
`:help` command:
|
||||
+
|
||||
----
|
||||
# apt install --no-install-recommends asciidoc source-highlight
|
||||
$ python3 scripts/asciidoc2html.py
|
||||
----
|
||||
|
||||
- If you prefer using QtWebKit, there's an up-to-date version available in
|
||||
https://packages.debian.org/buster/libqt5webkit5[Debian Testing].
|
||||
- If video or sound don't work with QtWebKit, try installing the gstreamer plugins:
|
||||
+
|
||||
----
|
||||
# apt install gstreamer1.0-plugins-{bad,base,good,ugly}
|
||||
----
|
||||
|
||||
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
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Fedora only ships free software in the repositories.
|
||||
To be able to play videos with proprietary codecs with QtWebEngine, you will
|
||||
need to install an additional package from the RPM Fusion Free repository.
|
||||
For more information see https://rpmfusion.org/Configuration.
|
||||
|
||||
-----
|
||||
# dnf install qt5-qtwebengine-freeworld
|
||||
-----
|
||||
|
||||
On Archlinux
|
||||
------------
|
||||
|
||||
qutebrowser is available in the official [community] repository.
|
||||
|
||||
----
|
||||
# pacman -S qutebrowser
|
||||
----
|
||||
|
||||
There is also a -git version available in the AUR:
|
||||
https://aur.archlinux.org/packages/qutebrowser-git/[qutebrowser-git].
|
||||
|
||||
You can install it using `makepkg` like this:
|
||||
|
||||
----
|
||||
$ git clone https://aur.archlinux.org/qutebrowser-git.git
|
||||
$ cd qutebrowser-git
|
||||
$ makepkg -si
|
||||
$ cd ..
|
||||
$ rm -r qutebrowser-git
|
||||
----
|
||||
|
||||
or you could use an AUR helper, e.g. `yaourt -S qutebrowser-git`.
|
||||
|
||||
If video or sound don't work with QtWebKit, try installing the gstreamer plugins:
|
||||
|
||||
----
|
||||
# pacman -S gst-plugins-{base,good,bad,ugly} gst-libav
|
||||
----
|
||||
|
||||
On Gentoo
|
||||
---------
|
||||
|
||||
NOTE: Gentoo's packages used to be severely outdated for a long time, but are
|
||||
now (October 2017) maintained and up-to-date again.
|
||||
|
||||
qutebrowser is available in the main repository and can be installed with:
|
||||
|
||||
----
|
||||
# emerge -av qutebrowser
|
||||
----
|
||||
|
||||
To use QtWebKit instead of QtWebEngine, you'll need a newer QtWebKit using
|
||||
https://gist.github.com/annulen/309569fb61e5d64a703c055c1e726f71[this ebuild].
|
||||
|
||||
If video or sound don't work with QtWebKit, try installing the gstreamer
|
||||
plugins:
|
||||
|
||||
----
|
||||
# emerge -av gst-plugins-{base,good,bad,ugly,libav}
|
||||
----
|
||||
|
||||
To be able to play videos with proprietary codecs with QtWebEngine, you will
|
||||
need to turn off the `bindist` flag for `dev-qt/qtwebengine`.
|
||||
|
||||
See the https://wiki.gentoo.org/wiki/Qutebrowser#USE_flags[Gentoo Wiki] for
|
||||
more information.
|
||||
|
||||
On Void Linux
|
||||
-------------
|
||||
|
||||
qutebrowser is available in the official repositories and can be installed
|
||||
with:
|
||||
|
||||
----
|
||||
# xbps-install qutebrowser
|
||||
----
|
||||
|
||||
It's currently recommended to install `python3-PyQt5-webengine` and
|
||||
`python3-PyQt5-opengl`, then start with `--backend webengine` to use the new
|
||||
backend.
|
||||
|
||||
Since the v1.0 release, qutebrowser uses QtWebEngine by default.
|
||||
|
||||
On NixOS
|
||||
--------
|
||||
|
||||
Nixpkgs collection contains `pkgs.qutebrowser` since June 2015. You can install
|
||||
it with:
|
||||
|
||||
----
|
||||
$ nix-env -i qutebrowser
|
||||
----
|
||||
|
||||
It's recommended to install `qt5.qtwebengine` and start with
|
||||
`--backend webengine` to use the new backend.
|
||||
|
||||
Since the v1.0 release, qutebrowser uses QtWebEngine by default.
|
||||
|
||||
On openSUSE
|
||||
-----------
|
||||
|
||||
There are prebuilt RPMs available at https://software.opensuse.org/download.html?project=network&package=qutebrowser[OBS].
|
||||
|
||||
To use the QtWebEngine backend, install `libqt5-qtwebengine`.
|
||||
|
||||
On OpenBSD
|
||||
----------
|
||||
|
||||
WARNING: OpenBSD only packages a legacy unmaintained version of QtWebKit (for
|
||||
which support was dropped in qutebrowser v1.0). It's advised to not use
|
||||
qutebrowser from OpenBSD ports for untrusted websites.
|
||||
|
||||
qutebrowser is in http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/www/qutebrowser/[OpenBSD ports].
|
||||
|
||||
Install the package:
|
||||
|
||||
----
|
||||
# pkg_add qutebrowser
|
||||
----
|
||||
|
||||
Or alternatively, use the ports system :
|
||||
|
||||
----
|
||||
# cd /usr/ports/www/qutebrowser
|
||||
# make install
|
||||
----
|
||||
|
||||
On FreeBSD
|
||||
----------
|
||||
|
||||
qutebrowser is in https://www.freshports.org/www/qutebrowser/[FreeBSD ports].
|
||||
|
||||
It can be installed with:
|
||||
|
||||
----
|
||||
# cd /usr/ports/www/qutebrowser
|
||||
# make install clean
|
||||
----
|
||||
|
||||
At present, precompiled packages are not available for this port,
|
||||
and QtWebEngine backend is also not available.
|
||||
|
||||
On Windows
|
||||
----------
|
||||
|
||||
There are different ways to install qutebrowser on Windows:
|
||||
|
||||
Prebuilt binaries
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Prebuilt standalone packages and installers
|
||||
https://github.com/qutebrowser/qutebrowser/releases[are built] for every
|
||||
release.
|
||||
|
||||
Note that you'll need to upgrade to new versions manually (subscribe to the
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[qutebrowser-announce
|
||||
mailinglist] to get notified on new releases). You can install a newer version
|
||||
without uninstalling the older one.
|
||||
|
||||
The binary release ships with a QtWebEngine built without proprietary codec
|
||||
support. To get support for e.g. h264/h265 videos, you'll need to build
|
||||
QtWebEngine from source yourself with support for that enabled.
|
||||
|
||||
https://chocolatey.org/packages/qutebrowser[Chocolatey package]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* PackageManagement PowerShell module
|
||||
----
|
||||
PS C:\> Install-Package qutebrowser
|
||||
----
|
||||
* Chocolatey's client
|
||||
----
|
||||
C:\> choco install qutebrowser
|
||||
----
|
||||
* Scoop's client
|
||||
----
|
||||
C:\> scoop bucket add extras
|
||||
C:\> scoop install qutebrowser
|
||||
----
|
||||
|
||||
Manual install
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* Use the installer from http://www.python.org/downloads[python.org] to get
|
||||
Python 3 (be sure to install pip).
|
||||
* Install https://testrun.org/tox/latest/index.html[tox] via
|
||||
https://pip.pypa.io/en/latest/[pip]:
|
||||
|
||||
----
|
||||
$ pip install tox
|
||||
----
|
||||
|
||||
Then <<tox,install qutebrowser via tox>>.
|
||||
|
||||
On macOS
|
||||
--------
|
||||
|
||||
Prebuilt binary
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The easiest way to install qutebrowser on macOS is to use the prebuilt `.app`
|
||||
files from the
|
||||
https://github.com/qutebrowser/qutebrowser/releases[release page].
|
||||
|
||||
Note that you'll need to upgrade to new versions manually (subscribe to the
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[qutebrowser-announce
|
||||
mailinglist] to get notified on new releases).
|
||||
|
||||
The binary release ships with a QtWebEngine built without proprietary codec
|
||||
support. To get support for e.g. h264/h265 videos, you'll need to build
|
||||
QtWebEngine from source yourself with support for that enabled.
|
||||
|
||||
This binary is also available through the
|
||||
https://caskroom.github.io/[Homebrew Cask] package manager:
|
||||
|
||||
----
|
||||
$ brew cask install qutebrowser
|
||||
----
|
||||
|
||||
Manual Install
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Alternatively, you can install the dependencies via a package manager (like
|
||||
http://brew.sh/[Homebrew] or https://www.macports.org/[MacPorts]) and run
|
||||
qutebrowser from source.
|
||||
|
||||
==== Homebrew
|
||||
|
||||
----
|
||||
$ brew install qt5
|
||||
$ pip3 install qutebrowser
|
||||
----
|
||||
|
||||
Since the v1.0 release, qutebrowser uses QtWebEngine by default.
|
||||
|
||||
Homebrew's builds of Qt and PyQt don't come with QtWebKit (and `--with-qtwebkit`
|
||||
uses an old version of QtWebKit which qutebrowser doesn't support anymore). If
|
||||
you want QtWebKit support, you'll need to build an up-to-date QtWebKit
|
||||
https://github.com/annulen/webkit/wiki/Building-QtWebKit-on-OS-X[manually].
|
||||
|
||||
Packagers
|
||||
---------
|
||||
|
||||
There are example .desktop and icon files provided. They would go in the
|
||||
standard location for your distro (`/usr/share/applications` and
|
||||
`/usr/share/pixmaps` for example).
|
||||
|
||||
The normal `setup.py install` doesn't install these files, so you'll have to do
|
||||
it as part of the packaging process.
|
||||
|
||||
[[tox]]
|
||||
Installing qutebrowser with tox
|
||||
-------------------------------
|
||||
|
||||
Getting the repository
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
First of all, clone the repository using http://git-scm.org/[git] and switch
|
||||
into the repository folder:
|
||||
|
||||
----
|
||||
$ git clone https://github.com/qutebrowser/qutebrowser.git
|
||||
$ cd qutebrowser
|
||||
----
|
||||
|
||||
Installing dependencies (including Qt)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Then run tox inside the qutebrowser repository to set up a
|
||||
https://docs.python.org/3/library/venv.html[virtual environment]:
|
||||
|
||||
----
|
||||
$ tox -e mkvenv-pypi
|
||||
----
|
||||
|
||||
If your system comes with Python 3.5.3 or older (such as Ubuntu 16.04 LTS), use
|
||||
`tox -e mkvenv-pypi-old` instead. This installs an older Qt version (5.10) due
|
||||
to bugs in newer versions.
|
||||
|
||||
This installs all needed Python dependencies in a `.venv` subfolder.
|
||||
|
||||
This comes with an up-to-date Qt/PyQt including QtWebEngine, but has a few
|
||||
caveats:
|
||||
|
||||
- Make sure your `python3` is Python 3.5 or newer, otherwise you'll get a "No
|
||||
matching distribution found" error. Note that qutebrowser itself also requires
|
||||
this.
|
||||
- It only works on 64-bit x86 systems, with other architectures you'll get the
|
||||
same error.
|
||||
- If your distribution uses OpenSSL 1.1 (like Debian Stretch or Archlinux),
|
||||
you'll need to set `LD_LIBRARY_PATH` to the OpenSSL 1.0 directory
|
||||
(`export LD_LIBRARY_PATH=/usr/lib/openssl-1.0` on Archlinux) before starting
|
||||
qutebrowser if you want SSL to work in certain downloads (e.g. for
|
||||
`:adblock-update` or `:download`).
|
||||
- It comes with a QtWebEngine compiled without proprietary codec support (such
|
||||
as h.264).
|
||||
|
||||
See the next section for an alternative.
|
||||
|
||||
Installing dependencies (system-wide Qt)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Alternatively, you can use `tox -e mkvenv` (without `-pypi`) to symlink your
|
||||
local Qt install instead of installing PyQt in the virtualenv. However, unless
|
||||
you have a new QtWebKit or QtWebEngine available, qutebrowser will not work. It
|
||||
also typically means you'll be using an older release of QtWebEngine.
|
||||
|
||||
On Windows, run `set PYTHON=C:\path\to\python.exe` (CMD) or ``$Env:PYTHON =
|
||||
"..."` (Powershell) first.
|
||||
|
||||
Creating a wrapper script
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Running `tox` does not install a system-wide `qutebrowser` script. You can
|
||||
launch qutebrowser by doing:
|
||||
|
||||
----
|
||||
.venv/bin/python3 -m qutebrowser
|
||||
----
|
||||
|
||||
You can create a simple wrapper script to start qutebrowser somewhere in your
|
||||
`$PATH` (e.g. `/usr/local/bin/qutebrowser` or `~/bin/qutebrowser`):
|
||||
|
||||
----
|
||||
#!/bin/bash
|
||||
~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser "$@"
|
||||
----
|
||||
|
||||
Building the docs
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
To build the documentation, install `asciidoc` (note that LaTeX which comes as
|
||||
optional/recommended dependency with some distributions is not required).
|
||||
|
||||
Then, run:
|
||||
|
||||
----
|
||||
$ python3 scripts/asciidoc2html.py
|
||||
----
|
||||
|
||||
Updating
|
||||
~~~~~~~~
|
||||
|
||||
When you updated your local copy of the code (e.g. by pulling the git repo, or
|
||||
extracting a new version), the virtualenv should automatically use the updated
|
||||
code. However, if dependencies got added, this won't be reflected in the
|
||||
virtualenv. Thus it's recommended to run the following command to recreate the
|
||||
virtualenv:
|
||||
|
||||
----
|
||||
$ tox -r -e mkvenv-pypi
|
||||
----
|
||||
196
doc/notes
Normal file
@@ -0,0 +1,196 @@
|
||||
henk's thoughts
|
||||
===============
|
||||
|
||||
1. Power to the user! Protect privacy!
|
||||
Things the browser should only do with explicit consent from the user, if
|
||||
applicable the user should be able to choose which protocol/host/port triplets
|
||||
to white/blacklist:
|
||||
|
||||
- load/run executable code, like js, flash, java applets, ... (think NoScript)
|
||||
- requests to other domains, ports or using a different protocol than what the
|
||||
user requested (think RequestPolicy)
|
||||
- accept cookies
|
||||
- storing/saving/caching things, e.g. open tabs ("session"), cookies, page
|
||||
contents, browsing/download history, form data, ...
|
||||
- send referrer
|
||||
- disclose any (presence, type, version, settings, capabilities, etc.)
|
||||
information about OS, browser, installed fonts, plugins, addons, etc.
|
||||
|
||||
2. Be efficient!
|
||||
I tend to leave a lot of tabs open and nobody can deny that some websites
|
||||
simply suck, so the browser should, unless told otherwise by the user:
|
||||
|
||||
- load tabs only when needed
|
||||
- run code in tabs only when needed, i.e. when the tab is currently being
|
||||
used/viewed (background tabs doing some JS magic even when they are not being
|
||||
used can create a lot of unnecessary load on the machine)
|
||||
- finish requests to the domain the user requested (e.g. www.example.org)
|
||||
before doing any requests to other subdomains (e.g. images.example.org) and
|
||||
finish those before doing requests to thirdparty domains (e.g. example.com)
|
||||
|
||||
3. Be stable!
|
||||
- one site should not make the complete browser crash, only that site's tab
|
||||
|
||||
|
||||
Upstream Bugs
|
||||
=============
|
||||
|
||||
- Web inspector is blank unless .hide()/.show() is called.
|
||||
Asked on SO: http://stackoverflow.com/q/23499159/2085149
|
||||
TODO: Report to PyQt/Qt
|
||||
|
||||
- Report some other crashes
|
||||
|
||||
|
||||
/u/angelic_sedition's thoughts
|
||||
==============================
|
||||
|
||||
Well support for greasemonkey scripts and bookmarklets/js (which was mentioned
|
||||
in the arch forum post) would be a big addition. What I've usually missed when
|
||||
using other vim-like browsers is things that allow for different settings and
|
||||
key bindings for different contexts. With that implemented I think I could
|
||||
switch to a lightweight browser (and believe me, I'd like to) for the most part
|
||||
and only use firefox when I needed downthemall or something.
|
||||
|
||||
For example, I have different bindings based on tab position that are reloaded
|
||||
with a pentadactyl autocmd so that <space><homerow keys> will take me to tab
|
||||
1-10 if I'm in that range or 2-20 if I'm in that range. I have an autocmd that
|
||||
will run on completed downloads that passes the file path to a script that will
|
||||
open ranger in a floating window with that file cut (this is basically like
|
||||
using ranger to save files instead of the crappy gui popup).
|
||||
|
||||
I also have a few bindings based on tabgroups. Tabgroups are a firefox feature,
|
||||
but I find them very useful for sorting things by topic so that only the tabs
|
||||
I'm interested at the moment are visible.
|
||||
|
||||
Pentadactyl has a feature it calls groups. You can create a group that will
|
||||
activate for sites/urls that match a pattern with some regex support. This
|
||||
allows me, for example, to set up different (more convenient) bindings for
|
||||
zooming only on images. I'll never need use the equivalent of vim n (next text
|
||||
search match), so I can bind that to zoom. This allows setting up custom
|
||||
quickmarks/gotos using the same keys for different websites. For example, on
|
||||
reddit I have different g(some key) bindings to go to different subreddits.
|
||||
This can also be used to pass certain keys directly to the site (e.g. for use
|
||||
with RES). For sites that don't have modifiable bindings, I can use this with
|
||||
pentadactyl's feedkeys or xdotool to create my own custom bindings. I even have
|
||||
a binding that will call out to bash script with different arguments depending
|
||||
on the site to download an image or an image gallery depending on the site (in
|
||||
some cases passing the url to some cli program).
|
||||
|
||||
I've also noticed the lack of completion. For example, on "o" pentadactyl will
|
||||
show sites (e.g. from history) that can be completed. I think I've been spoiled
|
||||
by pentadactyl having completion for just about everything.
|
||||
|
||||
|
||||
suckless surf ML post
|
||||
=====================
|
||||
|
||||
From: Ben Woolley <tautolog_AT_gmail.com>
|
||||
Date: Wed, 7 Jan 2015 18:29:25 -0800
|
||||
|
||||
Hi all,
|
||||
|
||||
This patch is a bit of a beast for surf. It is intended to be applied after
|
||||
the disk cache patch. It breaks some internal interfaces, so it could
|
||||
conflict with other patches.
|
||||
|
||||
I have been wanting a browser to implement a complete same-origin policy,
|
||||
and have been investigating how to do this in various browsers for many
|
||||
months. When I saw how surf opened new windows in a separate process, and
|
||||
was so simple, I knew I could do it quickly. Over the last two weeks, I
|
||||
have been developing this implementation on surf.
|
||||
|
||||
The basic idea is to prevent browser-based tracking as you browse from site
|
||||
to site, or origin to origin. By "origin" domain, I mean the "first-party"
|
||||
domain, the domain normally in the location bar (of the typical browser
|
||||
interface). Each origin domain effectively gets its own browser profile,
|
||||
and a browser process only ever deals with one origin domain at a time.
|
||||
This isolates origins vertically, preventing cookies, disk cache, memory
|
||||
cache, and window.name vulnerabilities. Basically, all known
|
||||
vulnerabilities that google and Mozilla cite as counter-examples when they
|
||||
explain why they haven't disabled third-party cookies yet.
|
||||
|
||||
When you are on msnbc.com, the tracking pixels will be stored in a cookie
|
||||
file for msnbc.com. When you go to cnn.com, the tracking pixels will be
|
||||
stored in a cookie file for cnn.com. You will not be tracked between them.
|
||||
However, third-party cookies, and the caching of third party resources will
|
||||
still work, but they will be isolated between origin domains. Instead of
|
||||
blocking cookies and cache entries, they are "double-keyed", or *also*
|
||||
keyed by origin.
|
||||
|
||||
There is a unidirectional communication channel, however, from one origin
|
||||
to the next, through navigation from one origin to the next. That is, the
|
||||
query string is passed from one origin to the next, and may embed
|
||||
identifiers. One example is an affiliate link that identifies where the
|
||||
lead came from. I have implemented what I call "horizontal isolation", in
|
||||
the form of an "Origin Crossing Gate".
|
||||
|
||||
Whenever you follow a link to a new domain, or even are just redirected to
|
||||
a new domain, a new window/tab is opened, and passed the referring origin
|
||||
via -R. The page passed to -O, for example -O originprompt.html, is an HTML
|
||||
page that is loaded in the new origin's context. That page tells you the
|
||||
origin you were on, the new origin, and the full link, and you can decide
|
||||
to go just to the new origin, or go to the full URL, after reviewing it for
|
||||
tracking data.
|
||||
|
||||
Also, you may click links that store your trust of that relationship with
|
||||
various expiration times, the same way you would trust geolocation requests
|
||||
for a particular origin for a period of time. The database used is actually
|
||||
the new origin's cookie file. Since the origin prompt is loaded in the new
|
||||
origin's context, I can set a cookie on behalf of the new origin. The
|
||||
expiration time of the trust is the expiration time of the cookie. The
|
||||
cookie implementation in webkit automatically expires the trust as part of
|
||||
how cookies work. Each time you cross an origin, the origin crossing page
|
||||
checks the cookie to see if trust is still established. If so, it will use
|
||||
window.location.replace() to continue on automatically. The initial page
|
||||
renders blank until the trust is invalidated, in which case the content of
|
||||
the gate is made visible.
|
||||
|
||||
However, the new origin is technically able to mess with those cookies, so
|
||||
a website could set trust for an origin crossing. I have addressed that by
|
||||
hashing the key with a salt, and setting the real expiration time as the
|
||||
value, along with an HMAC to verify the contents of the value. If the
|
||||
cookie is messed with in any way, the trust will be disabled, and the
|
||||
prompt will appear again. So it has a fail-safe function.
|
||||
|
||||
I know it seems a bit convoluted, but it just started out as a nice little
|
||||
rabbit hole, and I just wanted to get something workable. At first I
|
||||
thought using the cookie expiration time was convenient, but then when I
|
||||
realized that I needed to protect the cookie, things got a bit hairy. But
|
||||
it works.
|
||||
|
||||
Each profile is, by default, stored in ~/.surf/origins/$origin/
|
||||
The interesting side effect is that if there is a problem where a website
|
||||
relies on the cross-site cookie vulnerability to make a connection, you can
|
||||
simply make a symbolic link from one origin folder to another, and they
|
||||
will share the same profile. And if you want to delete cookies and/or cache
|
||||
for a particular origin, you just rm -rf the origin's profile folder, and
|
||||
don't have to interfere with your other sites that are working just fine.
|
||||
|
||||
One thing I don't handle are cross-origins POSTs. They just end up as GET
|
||||
requests right now. I intend to do something about that, but I haven't
|
||||
figured that out yet.
|
||||
|
||||
I have only been using this functionality for a few days myself, so I have
|
||||
absolutely no feedback yet. I wanted to provide the first implementation of
|
||||
the management of identity as a system resource the same way that things
|
||||
like geolocation, camera, and microphone resources are managed in browsers
|
||||
and mobile apps.
|
||||
|
||||
Currently, Mozilla and Tor have are working on third-party tracking issues
|
||||
in Firefox.
|
||||
https://blog.mozilla.org/privacy/2014/11/10/introducing-polaris-privacy-initiative-to-accelerate-user-focused-privacy-online/
|
||||
|
||||
Up to this point, Tor has provided a patch that double-keys cookies with
|
||||
the origin domain, but no other progress is visible. I have seen no
|
||||
discussion of how horizontal isolation is supposed to happen, and I wanted
|
||||
to show people that it can be done, and this is one way it can be done, and
|
||||
to compel the other browser makers to catch up, and hopefully the community
|
||||
can work toward a standard *without* the tracking loopholes, by showing
|
||||
people what a *complete* solution looks like.
|
||||
|
||||
Thank you,
|
||||
|
||||
Ben Woolley
|
||||
|
||||
Patch: http://lists.suckless.org/dev/att-25070/0005-same-origin-policy.patch
|
||||
@@ -9,8 +9,7 @@ Basic keybindings to get you started
|
||||
------------------------------------
|
||||
|
||||
* Use the arrow keys or `hjkl` to move around a webpage (vim-like syntax is used in quite a few places)
|
||||
* To go to a new webpage, press `o`, then type a url, then press Enter (Use `O` to open the url in a new tab, `go` to edit the current URL)
|
||||
* If what you've typed isn't a url, then a search engine will be used instead (DuckDuckGo, by default)
|
||||
* To go to a new webpage, press `o`, then type a url, then press Enter (Use `O` to open the url in a new tab). If what you've typed isn't a url, then a search engine will be used instead (DuckDuckGo, by default)
|
||||
* To switch between tabs, use `J` (next tab) and `K` (previous tab), or press `<Alt-num>`, where `num` is the position of the tab to switch to
|
||||
* To close the current tab, press `d` (and press `u` to undo closing a tab)
|
||||
* Use `H` and `L` to go back and forth in the history
|
||||
@@ -22,19 +21,16 @@ Basic keybindings to get you started
|
||||
What to do now
|
||||
--------------
|
||||
|
||||
* View the link:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png[key binding cheatsheet]
|
||||
* View the link:http://qutebrowser.org/img/cheatsheet-big.png[key binding cheatsheet]
|
||||
to make yourself familiar with the key bindings: +
|
||||
image:https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png"]
|
||||
* There's also a https://www.shortcutfoo.com/app/dojos/qutebrowser[free training
|
||||
course] on shortcutfoo for the keybindings - note that you need to be in
|
||||
insert mode (i) for it to work.
|
||||
image:http://qutebrowser.org/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="http://qutebrowser.org/img/cheatsheet-big.png"]
|
||||
* Run `:adblock-update` to download adblock lists and activate adblocking.
|
||||
* If you just cloned the repository, you'll need to run
|
||||
`scripts/asciidoc2html.py` to generate the documentation.
|
||||
* Go to the link:qute://settings[settings page] to set up qutebrowser the way you want it.
|
||||
* Subscribe to
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist] or
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[the announce-only mailinglist].
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist]
|
||||
where there are weekly "what's new in qutebrowser" posts.
|
||||
* Let me know what features you are missing or things that need (even small!)
|
||||
improvements.
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
:man source: qutebrowser
|
||||
:man manual: qutebrowser manpage
|
||||
:toc:
|
||||
:homepage: https://www.qutebrowser.org/
|
||||
:homepage: http://www.qutebrowser.org/
|
||||
|
||||
== NAME
|
||||
qutebrowser - a keyboard-driven, vim-like browser based on PyQt5.
|
||||
qutebrowser - a keyboard-driven, vim-like browser based on PyQt5 and QtWebKit.
|
||||
|
||||
== SYNOPSIS
|
||||
*qutebrowser* ['-OPTION' ['...']] [':COMMAND' ['...']] ['URL' ['...']]
|
||||
|
||||
== DESCRIPTION
|
||||
qutebrowser is a keyboard-focused browser with a minimal GUI. It's based
|
||||
on Python and Qt5 and is free software, licensed under the GPL.
|
||||
on Python, PyQt5 and QtWebKit and free software, licensed under the GPL.
|
||||
|
||||
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl.
|
||||
|
||||
@@ -38,13 +38,22 @@ show it.
|
||||
*-h*, *--help*::
|
||||
show this help message and exit
|
||||
|
||||
*-B* 'BASEDIR', *--basedir* 'BASEDIR'::
|
||||
Base directory for all storage.
|
||||
*-c* 'CONFDIR', *--confdir* 'CONFDIR'::
|
||||
Set config directory (empty for no config storage).
|
||||
|
||||
*--datadir* 'DATADIR'::
|
||||
Set data directory (empty for no data storage).
|
||||
|
||||
*--cachedir* 'CACHEDIR'::
|
||||
Set cache directory (empty for no cache storage).
|
||||
|
||||
*--basedir* 'BASEDIR'::
|
||||
Base directory for all storage. Other --*dir arguments are ignored if this is given.
|
||||
|
||||
*-V*, *--version*::
|
||||
Show version and quit.
|
||||
|
||||
*-s* 'OPTION' 'VALUE', *--set* 'OPTION' 'VALUE'::
|
||||
*-s* 'SECTION' 'OPTION' 'VALUE', *--set* 'SECTION' 'OPTION' 'VALUE'::
|
||||
Set a temporary setting for this session.
|
||||
|
||||
*-r* 'SESSION', *--restore* 'SESSION'::
|
||||
@@ -57,10 +66,7 @@ show it.
|
||||
How URLs should be opened if there is already a qutebrowser instance running.
|
||||
|
||||
*--backend* '{webkit,webengine}'::
|
||||
Which backend to use.
|
||||
|
||||
*--enable-webengine-inspector*::
|
||||
Enable the web inspector for QtWebEngine. Note that this is a SECURITY RISK and you should not visit untrusted websites with the inspector turned on. See https://bugreports.qt.io/browse/QTBUG-50725 for more details. This is not needed anymore since Qt 5.11 where the inspector is always enabled and secure.
|
||||
Which backend to use (webengine backend is EXPERIMENTAL!).
|
||||
|
||||
=== debug arguments
|
||||
*-l* '{critical,error,warning,info,debug,vdebug}', *--loglevel* '{critical,error,warning,info,debug,vdebug}'::
|
||||
@@ -72,7 +78,7 @@ show it.
|
||||
*--loglines* 'LOGLINES'::
|
||||
How many lines of the debug log to keep in RAM (-1: unlimited).
|
||||
|
||||
*-d*, *--debug*::
|
||||
*--debug*::
|
||||
Turn on debugging options.
|
||||
|
||||
*--json-logging*::
|
||||
@@ -84,30 +90,51 @@ show it.
|
||||
*--force-color*::
|
||||
Force colored logging
|
||||
|
||||
*--harfbuzz* '{old,new,system,auto}'::
|
||||
HarfBuzz engine version to use. Default: auto.
|
||||
|
||||
*--relaxed-config*::
|
||||
Silently remove unknown config options.
|
||||
|
||||
*--nowindow*::
|
||||
Don't show the main window.
|
||||
|
||||
*-T*, *--temp-basedir*::
|
||||
*--debug-exit*::
|
||||
Turn on debugging of late exit.
|
||||
|
||||
*--pdb-postmortem*::
|
||||
Drop into pdb on exceptions.
|
||||
|
||||
*--temp-basedir*::
|
||||
Use a temporary basedir.
|
||||
|
||||
*--no-err-windows*::
|
||||
Don't show any error windows (used for tests/smoke.py).
|
||||
|
||||
*--qt-arg* 'NAME' 'VALUE'::
|
||||
Pass an argument with a value to Qt. For example, you can do `--qt-arg geometry 650x555+200+300` to set the window geometry.
|
||||
*--qt-name* 'NAME'::
|
||||
Set the window name.
|
||||
|
||||
*--qt-flag* 'QT_FLAG'::
|
||||
Pass an argument to Qt as flag.
|
||||
*--qt-style* 'STYLE'::
|
||||
Set the Qt GUI style to use.
|
||||
|
||||
*-D* 'DEBUG_FLAGS', *--debug-flag* 'DEBUG_FLAGS'::
|
||||
Pass name of debugging feature to be turned on.
|
||||
*--qt-stylesheet* 'STYLESHEET'::
|
||||
Override the Qt application stylesheet.
|
||||
|
||||
*--qt-widgetcount*::
|
||||
Print debug message at the end about number of widgets left undestroyed and maximum number of widgets existed at the same time.
|
||||
|
||||
*--qt-reverse*::
|
||||
Set the application's layout direction to right-to-left.
|
||||
|
||||
*--qt-qmljsdebugger* 'port:PORT[,block]'::
|
||||
Activate the QML/JS debugger with a specified port. 'block' is optional and will make the application wait until a debugger connects to it.
|
||||
// QUTE_OPTIONS_END
|
||||
|
||||
== FILES
|
||||
|
||||
- '~/.config/qutebrowser/config.py': Configuration file.
|
||||
- '~/.config/qutebrowser/autoconfig.yml': Configuration done via the GUI.
|
||||
- '~/.config/qutebrowser/qutebrowser.conf': Main config file.
|
||||
- '~/.config/qutebrowser/quickmarks': Saved quickmarks.
|
||||
- '~/.config/qutebrowser/keys.conf': Defined key bindings.
|
||||
- '~/.local/share/qutebrowser/': Various state information.
|
||||
- '~/.cache/qutebrowser/': Temporary data.
|
||||
|
||||
@@ -118,7 +145,7 @@ defaults.
|
||||
|
||||
== BUGS
|
||||
Bugs are tracked in the Github issue tracker at
|
||||
https://github.com/qutebrowser/qutebrowser/issues.
|
||||
https://github.com/The-Compiler/qutebrowser/issues.
|
||||
|
||||
If you found a bug, use the built-in ':report' command to create a bug report
|
||||
with all information needed.
|
||||
@@ -128,7 +155,7 @@ https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
|
||||
mailto:qutebrowser@lists.qutebrowser.org[] instead.
|
||||
|
||||
For security bugs, please contact me directly at me@the-compiler.org, GPG ID
|
||||
https://www.the-compiler.org/pubkey.asc[0xFD55A072].
|
||||
http://www.the-compiler.org/pubkey.asc[0xFD55A072].
|
||||
|
||||
== COPYRIGHT
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
@@ -144,14 +171,12 @@ You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
== RESOURCES
|
||||
* Website: https://www.qutebrowser.org/
|
||||
* Website: http://www.qutebrowser.org/
|
||||
* Mailinglist: mailto:qutebrowser@lists.qutebrowser.org[] /
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser
|
||||
* Announce-only mailinglist: mailto:qutebrowser-announce@lists.qutebrowser.org[] /
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce
|
||||
* IRC: irc://irc.freenode.org/#qutebrowser[`#qutebrowser`] on
|
||||
http://freenode.net/[Freenode]
|
||||
* Github: https://github.com/qutebrowser/qutebrowser
|
||||
* Github: https://github.com/The-Compiler/qutebrowser
|
||||
|
||||
== AUTHOR
|
||||
*qutebrowser* was written by Florian Bruhin. All contributors can be found in
|
||||
|
||||
@@ -37,12 +37,25 @@ is available in the repositories:
|
||||
Archlinux
|
||||
^^^^^^^^^
|
||||
|
||||
For Archlinux, no debug information is provided. You can either compile Qt
|
||||
For Archlinux, no debug informations are provided. You can either compile Qt
|
||||
yourself (which will take a few hours even on a modern machine) or use
|
||||
debugging symbols compiled/packaged by me (x86_64 only).
|
||||
|
||||
To install my pre-built packages
|
||||
++++++++++++++++++++++++++++++++
|
||||
.To compile by yourself
|
||||
|
||||
----
|
||||
$ git clone https://github.com/The-Compiler/qt-debug-pkgbuild.git
|
||||
$ cd qt-debug-pkgbuild
|
||||
$ git checkout symbols
|
||||
$ export DEBUG_CFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
|
||||
$ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
|
||||
$ cd qt5
|
||||
$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug,qt5-webengine-debug
|
||||
$ cd ../pyqt5
|
||||
$ makepkg -si --pkg pyqt5-common-debug,python-pyqt5-debug
|
||||
----
|
||||
|
||||
.To install my pre-built packages
|
||||
|
||||
First download and sign the key:
|
||||
|
||||
@@ -57,36 +70,18 @@ Then edit your `/etc/pacman.conf` to add the repository to the bottom:
|
||||
|
||||
----
|
||||
[qt-debug]
|
||||
Server = https://qutebrowser.org/qt-debug/$arch
|
||||
Server = http://qutebrowser.org/qt-debug/$arch
|
||||
----
|
||||
|
||||
Then install the packages:
|
||||
|
||||
----
|
||||
# pacman -Suy pyqt5-common-debug python-pyqt5-debug qt5-base-debug qt5-webkit-debug qt5-webengine-debug
|
||||
# pacman -Suy pyqt5-common-debug python-pyqt5-debug qt5-base-debug qt5-webkit-debug,qt5-webengine-debug
|
||||
----
|
||||
|
||||
The `-debug` packages conflict with the non-debug variants - it's safe to
|
||||
remove them.
|
||||
|
||||
To compile by yourself
|
||||
++++++++++++++++++++++
|
||||
|
||||
Note that building Qt will likely take multiple hours, even on a recent system.
|
||||
I'd also expect it to take around 6 GB of RAM and 30 GB of disk space for a
|
||||
successful compile run.
|
||||
|
||||
----
|
||||
$ git clone https://github.com/qutebrowser/qt-debug-pkgbuild.git
|
||||
$ cd qt-debug-pkgbuild
|
||||
$ export DEBUG_CFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
|
||||
$ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
|
||||
$ cd qt5
|
||||
$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug,qt5-webengine-debug
|
||||
$ cd ../pyqt5
|
||||
$ makepkg -si --pkg pyqt5-common-debug,python-pyqt5-debug
|
||||
----
|
||||
|
||||
Getting the stack trace
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -174,7 +169,7 @@ When you see the _qutebrowser.exe has stopped working_ window, do not click
|
||||
file displayed there.
|
||||
|
||||
Now install
|
||||
https://www.microsoft.com/en-us/download/details.aspx?id=49924[DebugDiag] from
|
||||
http://www.microsoft.com/en-us/download/details.aspx?id=42933[DebugDiag] from
|
||||
Microsoft, then run the *DebugDiag 2 Analysis* tool. There, check
|
||||
*CrashHangAnalysis* and add your crash dump via *Add Data files*. Then click
|
||||
*Start analysis*.
|
||||
|
||||
@@ -22,8 +22,6 @@ To call a userscript, it needs to be stored in your data directory under
|
||||
`userscripts` (for example: `~/.local/share/qutebrowser/userscripts/myscript`),
|
||||
or just use an absolute path.
|
||||
|
||||
NOTE: On Windows, only userscripts with `com`, `bat`, or `exe` extensions will be launched.
|
||||
|
||||
Getting information
|
||||
-------------------
|
||||
|
||||
@@ -31,20 +29,20 @@ The following environment variables will be set when a userscript is launched:
|
||||
|
||||
- `QUTE_MODE`: Either `hints` (started via hints) or `command` (started via
|
||||
command or key binding).
|
||||
- `QUTE_USER_AGENT`: The currently set user agent, if customized.
|
||||
- `QUTE_USER_AGENT`: The currently set user agent.
|
||||
- `QUTE_FIFO`: The FIFO or file to write commands to.
|
||||
- `QUTE_HTML`: Path of a file containing the HTML source of the current page.
|
||||
- `QUTE_TEXT`: Path of a file containing the plaintext of the current page.
|
||||
- `QUTE_CONFIG_DIR`: Path of the directory containing qutebrowser's configuration.
|
||||
- `QUTE_DATA_DIR`: Path of the directory containing qutebrowser's data.
|
||||
- `QUTE_DOWNLOAD_DIR`: Path of the downloads directory.
|
||||
- `QUTE_COMMANDLINE_TEXT`: Text currently in qutebrowser's command line.
|
||||
|
||||
In `command` mode:
|
||||
|
||||
- `QUTE_URL`: The current URL.
|
||||
- `QUTE_TITLE`: The title of the current page.
|
||||
- `QUTE_SELECTED_TEXT`: The text currently selected on the page.
|
||||
- `QUTE_SELECTED_HTML` The HTML currently selected on the page.
|
||||
|
||||
In `hints` mode:
|
||||
|
||||
@@ -58,7 +56,7 @@ Sending commands
|
||||
Normal qutebrowser commands can be written to `$QUTE_FIFO` and will be
|
||||
executed.
|
||||
|
||||
On Unix/macOS, this is a named pipe and commands written to it will get executed
|
||||
On Unix/OS X, this is a named pipe and commands written to it will get executed
|
||||
immediately.
|
||||
|
||||
On Windows, this is a regular file, and the commands in it will be executed as
|
||||
@@ -76,11 +74,3 @@ Opening the currently selected word on http://www.dict.cc/[dict.cc]:
|
||||
|
||||
echo "open -t http://www.dict.cc/?s=$QUTE_SELECTED_TEXT" >> "$QUTE_FIFO"
|
||||
----
|
||||
|
||||
Libraries
|
||||
---------
|
||||
|
||||
Some third-party libraries are available to make writing userscripts easier:
|
||||
|
||||
- Python: https://github.com/hiway/python-qutescript[python-qutescript]
|
||||
- Node.js: https://www.npmjs.com/package/qutejs[qutejs]
|
||||
|
||||
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 45 KiB |
@@ -1,302 +1,207 @@
|
||||
/* XPM */
|
||||
static char * qutebrowser_xpm[] = {
|
||||
"32 32 267 2",
|
||||
" c None",
|
||||
". c #9FD4FD",
|
||||
"+ c #99CBFE",
|
||||
"@ c #90C3FE",
|
||||
"# c #89BFFE",
|
||||
"$ c #81BCFF",
|
||||
"% c #80BBFF",
|
||||
"& c #9BCAFD",
|
||||
"* c #A9DBFB",
|
||||
"= c #88D3FB",
|
||||
"- c #98CBFE",
|
||||
"; c #81BBFF",
|
||||
"> c #7EBAFF",
|
||||
", c #84BDFF",
|
||||
"' c #8DC2FF",
|
||||
") c #96C7FE",
|
||||
"! c #A0CCFE",
|
||||
"~ c #A9D1FE",
|
||||
"{ c #CEE5FD",
|
||||
"] c #C7E3FC",
|
||||
"^ c #8AD3FB",
|
||||
"/ c #9DCFFD",
|
||||
"( c #C3DFFD",
|
||||
"_ c #CDE4FD",
|
||||
": c #A3CEFE",
|
||||
"< c #94C6FE",
|
||||
"[ c #CAE5FC",
|
||||
"} c #7DD0FB",
|
||||
"| c #9ECDFD",
|
||||
"1 c #A1CDFE",
|
||||
"2 c #8BC1FF",
|
||||
"3 c #87BFFF",
|
||||
"4 c #ADD4FE",
|
||||
"5 c #C6E1FD",
|
||||
"6 c #CCE3FC",
|
||||
"7 c #A7DAFB",
|
||||
"8 c #9DCBFE",
|
||||
"9 c #78AFF1",
|
||||
"0 c #6096D4",
|
||||
"a c #4B82C0",
|
||||
"b c #5A84B3",
|
||||
"c c #6589B1",
|
||||
"d c #6F92B9",
|
||||
"e c #90AED0",
|
||||
"f c #C4DBF5",
|
||||
"g c #6286AE",
|
||||
"h c #7D9EC2",
|
||||
"i c #BADFFC",
|
||||
"j c #85BDFE",
|
||||
"k c #78B4F8",
|
||||
"l c #4C83C0",
|
||||
"m c #1E4F87",
|
||||
"n c #0A396E",
|
||||
"o c #345D8D",
|
||||
"p c #CDE4FC",
|
||||
"q c #88A7CA",
|
||||
"r c #1D497C",
|
||||
"s c #799BBF",
|
||||
"t c #8AC1FD",
|
||||
"u c #5E97D7",
|
||||
"v c #14457B",
|
||||
"w c #4F76A0",
|
||||
"x c #A9D5FC",
|
||||
"y c #95C9FD",
|
||||
"z c #4C82C1",
|
||||
"A c #0A3A6F",
|
||||
"B c #C9E3FD",
|
||||
"C c #95CCFC",
|
||||
"D c #629BDB",
|
||||
"E c #0B3A6F",
|
||||
"F c #0C3B6F",
|
||||
"G c #4E749F",
|
||||
"H c #8CACCE",
|
||||
"I c #6185AD",
|
||||
"J c #CBE4FD",
|
||||
"K c #89C0FF",
|
||||
"L c #98CDFA",
|
||||
"M c #27558A",
|
||||
"N c #144175",
|
||||
"O c #9BB8D8",
|
||||
"P c #335D8C",
|
||||
"Q c #AFC9E6",
|
||||
"R c #AFD4FE",
|
||||
"S c #91C7FD",
|
||||
"T c #A0C0DE",
|
||||
"U c #194779",
|
||||
"V c #80A1C5",
|
||||
"W c #C8E1F9",
|
||||
"X c #9CB9D8",
|
||||
"Y c #7799BE",
|
||||
"Z c #6489B0",
|
||||
"` c #7092B9",
|
||||
" . c #6E9DCF",
|
||||
".. c #79B5F9",
|
||||
"+. c #83BDFE",
|
||||
"@. c #7395BA",
|
||||
"#. c #315C8B",
|
||||
"$. c #7C9EC2",
|
||||
"%. c #C0D9F3",
|
||||
"&. c #7294BA",
|
||||
"*. c #5C94D4",
|
||||
"=. c #91CCFC",
|
||||
"-. c #88CBFA",
|
||||
";. c #5179A3",
|
||||
">. c #6E91B7",
|
||||
",. c #6084AC",
|
||||
"'. c #96B3D4",
|
||||
"). c #275283",
|
||||
"!. c #0C3C71",
|
||||
"~. c #629CDC",
|
||||
"{. c #94C6FD",
|
||||
"]. c #A7D2FC",
|
||||
"^. c #36659A",
|
||||
"/. c #2C5788",
|
||||
"(. c #9DBAD9",
|
||||
"_. c #B4CEEA",
|
||||
":. c #476E9A",
|
||||
"<. c #7EB9FE",
|
||||
"[. c #8DC3FD",
|
||||
"}. c #8CC2FE",
|
||||
"|. c #2F619B",
|
||||
"1. c #87A6C9",
|
||||
"2. c #7A9BC0",
|
||||
"3. c #CBE2FB",
|
||||
"4. c #C7DFF8",
|
||||
"5. c #6C8FB5",
|
||||
"6. c #113F73",
|
||||
"7. c #0F3D71",
|
||||
"8. c #547AA4",
|
||||
"9. c #9CBAD9",
|
||||
"0. c #B9D3EE",
|
||||
"a. c #A3C0DE",
|
||||
"b. c #31629A",
|
||||
"c. c #659EE0",
|
||||
"d. c #87BFFE",
|
||||
"e. c #C3E0FD",
|
||||
"f. c #4371A4",
|
||||
"g. c #7496BB",
|
||||
"h. c #90AFD1",
|
||||
"i. c #245081",
|
||||
"j. c #416A96",
|
||||
"k. c #B0CBE7",
|
||||
"l. c #CCE4FD",
|
||||
"m. c #7DB8FD",
|
||||
"n. c #1E5088",
|
||||
"o. c #497EBC",
|
||||
"p. c #C9E3FC",
|
||||
"q. c #7193B9",
|
||||
"r. c #C6E0FB",
|
||||
"s. c #A2CDFE",
|
||||
"t. c #97C8FE",
|
||||
"u. c #A7D0FE",
|
||||
"v. c #BDDCFD",
|
||||
"w. c #9EC2E8",
|
||||
"x. c #416996",
|
||||
"y. c #366AA6",
|
||||
"z. c #C0DEFC",
|
||||
"A. c #A2BFDD",
|
||||
"B. c #326299",
|
||||
"C. c #649DDF",
|
||||
"D. c #71ABED",
|
||||
"E. c #3569A4",
|
||||
"F. c #0D3C71",
|
||||
"G. c #6998CD",
|
||||
"H. c #30639D",
|
||||
"I. c #A8D3F8",
|
||||
"J. c #2B5686",
|
||||
"K. c #3A679B",
|
||||
"L. c #ADCAEA",
|
||||
"M. c #85A6C9",
|
||||
"N. c #33639B",
|
||||
"O. c #9CCBFD",
|
||||
"P. c #86C2F7",
|
||||
"Q. c #0E3C71",
|
||||
"R. c #1B4C83",
|
||||
"S. c #5D95D5",
|
||||
"T. c #557BA5",
|
||||
"U. c #85C0F6",
|
||||
"V. c #55A8EF",
|
||||
"W. c #94B3D3",
|
||||
"X. c #1C497C",
|
||||
"Y. c #13437A",
|
||||
"Z. c #487DBB",
|
||||
"`. c #7BB7FB",
|
||||
" + c #76B1F5",
|
||||
".+ c #4E85C3",
|
||||
"++ c #ACD3FE",
|
||||
"@+ c #2F5989",
|
||||
"#+ c #7597BC",
|
||||
"$+ c #53A7EF",
|
||||
"%+ c #C6E1FC",
|
||||
"&+ c #B6D5F7",
|
||||
"*+ c #5890D0",
|
||||
"=+ c #4076B2",
|
||||
"-+ c #619ADB",
|
||||
";+ c #7CB7FC",
|
||||
">+ c #7DB9FE",
|
||||
",+ c #5087C6",
|
||||
"'+ c #134479",
|
||||
")+ c #23548D",
|
||||
"!+ c #24558D",
|
||||
"~+ c #8AAACC",
|
||||
"{+ c #A2C1E1",
|
||||
"]+ c #86C1F5",
|
||||
"^+ c #B4D7FE",
|
||||
"/+ c #6CA5E8",
|
||||
"(+ c #22548C",
|
||||
"_+ c #6D94BF",
|
||||
":+ c #98B6D6",
|
||||
"<+ c #134174",
|
||||
"[+ c #84BDF5",
|
||||
"}+ c #CAE4FC",
|
||||
"|+ c #CBE3FD",
|
||||
"1+ c #8FC3FF",
|
||||
"2+ c #3F72AD",
|
||||
"3+ c #49719C",
|
||||
"4+ c #0C3B70",
|
||||
"5+ c #9CBBDB",
|
||||
"6+ c #79B7F3",
|
||||
"7+ c #BFDCFD",
|
||||
"8+ c #7FBBFF",
|
||||
"9+ c #7E9FC3",
|
||||
"0+ c #77B6F3",
|
||||
"a+ c #A5CEF7",
|
||||
"b+ c #9FCBFE",
|
||||
"c+ c #3267A1",
|
||||
"d+ c #A4CDF7",
|
||||
"e+ c #B9D9FA",
|
||||
"f+ c #C7E1FD",
|
||||
"g+ c #90C3FF",
|
||||
"h+ c #15457C",
|
||||
"i+ c #558CCB",
|
||||
"j+ c #2E5889",
|
||||
"k+ c #7B9CC1",
|
||||
"l+ c #C4DDF6",
|
||||
"m+ c #BBDAFA",
|
||||
"n+ c #CDE5FD",
|
||||
"o+ c #B3D6FE",
|
||||
"p+ c #80BAFF",
|
||||
"q+ c #4E84C3",
|
||||
"r+ c #3E73AF",
|
||||
"s+ c #78B3F7",
|
||||
"t+ c #5991D1",
|
||||
"u+ c #477DBA",
|
||||
"v+ c #4075B2",
|
||||
"w+ c #5783B6",
|
||||
"x+ c #BDD6F0",
|
||||
"y+ c #A1CBF6",
|
||||
"z+ c #90C4FF",
|
||||
"A+ c #BCDBFD",
|
||||
"B+ c #73B0F1",
|
||||
"C+ c #C5E0FB",
|
||||
"D+ c #91C5FF",
|
||||
"E+ c #AED3FE",
|
||||
"F+ c #C9E2FC",
|
||||
"G+ c #76B2F2",
|
||||
"H+ c #8BBFF9",
|
||||
"I+ c #81BBFE",
|
||||
"J+ c #9ECBFE",
|
||||
"K+ c #84B8F3",
|
||||
"L+ c #79B4F4",
|
||||
"M+ c #88BEFA",
|
||||
"N+ c #83BCFE",
|
||||
"O+ c #A4CFFC",
|
||||
"P+ c #A6CDF6",
|
||||
"Q+ c #82B8F2",
|
||||
"R+ c #529BEC",
|
||||
" . + @ # $ % & * = ",
|
||||
" - ; > > , ' ) ! ~ { { { ] ^ ",
|
||||
" / ; > > > > ; ( _ : < { { { { { [ } ",
|
||||
" | 1 2 > > > 2 3 4 5 { { { { { 6 { { { 7 ",
|
||||
" 8 $ < 9 0 a b c d e { { { { f g h { { { { i ",
|
||||
" j k l m n n n n n n o { { p q r n s { { { { { i ",
|
||||
" t u v n n n n n n n n o { { w n n n s { { { { { { x ",
|
||||
" y z A n n n n n n n n n o { { o n n n s { { { { { { B C ",
|
||||
" D E n n n F G H I n n n o { { o n n n s { { { { { J K % ",
|
||||
" L M n n n N O { { s n n n o { { o n n P Q { { { { { R > > S ",
|
||||
" T n n n n H { { { s n n n o { { o U V 6 W X Y Z ` ...> > +. ",
|
||||
" @.n n n #.{ { { { s n n n o { { $.%.W &.U n n n n n v *.> > =.",
|
||||
"-.;.n n n >.{ { { { s n n n ,.{ { { '.).n n n n n n n n !.~.> {.",
|
||||
"].^.n n n q { { { { s n /.(.{ { _.:.n n n n n n n n n n n m <.[.",
|
||||
"}.|.n n n H { { { { 1.2.3.{ 4.5.6.n n n 7.8.9.0.a.b.n n n n c.d.",
|
||||
"e.f.n n n g.{ { { { { { { h.i.n n n n j.k.{ { { l.m.n.n n n o.$ ",
|
||||
"p.q.n n n /.r.s.t.u.v.w.x.n n n n i.h.{ { { { { { u.o.n n n y.$ ",
|
||||
"z.A.n n n n B.C.D.u E.F.n n n 6.5.4.{ 3.2.1.{ { { { G.n n n H.d.",
|
||||
"I.p J.n n n n n n n n n n n K.L.{ { (./.n s { { { { M.n n n N.O.",
|
||||
"P.{ (.Q.n n n n n n n n R.S.> K _ ,.n n n s { { { { 5.n n n T.U.",
|
||||
"V.{ { W.X.n n n n n Y.Z.`. +.+> ++o n n n s { { { { @+n n n #+$+",
|
||||
" %+{ { &+*+Z.=+a -+;+>+,+'+)+> > !+n n n s { { { ~+n n n n {+ ",
|
||||
" ]+{ { ^+> > > > > /+(+n n )+> > )+n n n _+{ { :+<+n n n o [+ ",
|
||||
" }+{ |+1+> > > > l n n n )+> > )+n n n 2+~+3+E n n n 4+5+ ",
|
||||
" 6+{ { 7+8+> > > l n n n )+> > )+n n n n n n n n n F 9+0+ ",
|
||||
" a+{ { b+> > > l n n n c+> > )+n n n n n n n n r O d+ ",
|
||||
" e+{ f+g+> > l n h+i+<.> > )+n n n n n E j+k+l+m+ ",
|
||||
" e+{ n+o+p+q+r+s+> > > > t+u+v+w+2.W.x+{ { e+ ",
|
||||
" y+{ { z+>+> > > > > > > > > A+{ { { { d+ ",
|
||||
" B+C+) > > > > > > > > D+E+{ { { F+G+ ",
|
||||
" H+I+> > > > > > J+{ { { C+K+ ",
|
||||
" L+M+# N+; 8+O+P+Q+R+ "};
|
||||
static char *qutebrowser[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"32 32 169 2 ",
|
||||
" c #0A396E",
|
||||
". c #0B3C72",
|
||||
"X c #0B4077",
|
||||
"o c #0C437B",
|
||||
"O c #134175",
|
||||
"+ c #15467C",
|
||||
"@ c #18477B",
|
||||
"# c #1A497D",
|
||||
"$ c #0D4B86",
|
||||
"% c #0F4E8D",
|
||||
"& c #124A80",
|
||||
"* c #1F4F83",
|
||||
"= c #0E518C",
|
||||
"- c #1F5084",
|
||||
"; c #11508C",
|
||||
": c #0F5193",
|
||||
"> c #115799",
|
||||
", c #115B9C",
|
||||
"< c #204F83",
|
||||
"1 c #245287",
|
||||
"2 c #2A598C",
|
||||
"3 c #325E8F",
|
||||
"4 c #11609F",
|
||||
"5 c #346496",
|
||||
"6 c #3B6898",
|
||||
"7 c #115CA1",
|
||||
"8 c #115EAC",
|
||||
"9 c #1263A3",
|
||||
"0 c #1260AD",
|
||||
"q c #136BAC",
|
||||
"w c #136BB2",
|
||||
"e c #1366BA",
|
||||
"r c #196BB2",
|
||||
"t c #157ABB",
|
||||
"y c #1577BB",
|
||||
"u c #2E6DB0",
|
||||
"i c #387FB1",
|
||||
"p c #456E9A",
|
||||
"a c #4873A1",
|
||||
"s c #4375AA",
|
||||
"d c #507AA6",
|
||||
"f c #597EA4",
|
||||
"g c #4D7EB3",
|
||||
"h c #156FCB",
|
||||
"j c #167AC5",
|
||||
"k c #1675CA",
|
||||
"l c #177BCE",
|
||||
"z c #1777D8",
|
||||
"x c #1476E4",
|
||||
"c c #167BE6",
|
||||
"v c #167DE8",
|
||||
"b c #197EEF",
|
||||
"n c #1A7FF0",
|
||||
"m c #1A80BE",
|
||||
"M c #5F87AF",
|
||||
"N c #5D8BBA",
|
||||
"B c #5A84B1",
|
||||
"V c #6C8FB3",
|
||||
"C c #6F96BE",
|
||||
"Z c #1886CC",
|
||||
"A c #1883D7",
|
||||
"S c #198DD5",
|
||||
"D c #1987D9",
|
||||
"F c #198ADC",
|
||||
"G c #1A96DC",
|
||||
"H c #3090D9",
|
||||
"J c #1682E9",
|
||||
"K c #1983ED",
|
||||
"L c #1689E9",
|
||||
"P c #1A8DEE",
|
||||
"I c #1B95ED",
|
||||
"U c #1C9EEA",
|
||||
"Y c #1B97E4",
|
||||
"T c #1A84F2",
|
||||
"R c #1A8BF2",
|
||||
"E c #1C94F4",
|
||||
"W c #1D9CF5",
|
||||
"Q c #3388E6",
|
||||
"! c #3D90E9",
|
||||
"~ c #228EF3",
|
||||
"^ c #229FF6",
|
||||
"/ c #3294F4",
|
||||
"( c #3D9FF6",
|
||||
") c #339CF4",
|
||||
"_ c #1CA2E5",
|
||||
"` c #1DABEE",
|
||||
"' c #1DA4F6",
|
||||
"] c #1EA9F7",
|
||||
"[ c #1EADF8",
|
||||
"{ c #1FB4F9",
|
||||
"} c #1FB9FA",
|
||||
"| c #20ACF8",
|
||||
" . c #27A4F6",
|
||||
".. c #3DA9F6",
|
||||
"X. c #20B9FA",
|
||||
"o. c #2EB6F9",
|
||||
"O. c #458DC9",
|
||||
"+. c #5C8DC1",
|
||||
"@. c #5795C6",
|
||||
"#. c #709DCB",
|
||||
"$. c #74A8DD",
|
||||
"%. c #4A97EA",
|
||||
"&. c #4896EA",
|
||||
"*. c #559EEA",
|
||||
"=. c #439AF5",
|
||||
"-. c #46A3F6",
|
||||
";. c #5FA9F6",
|
||||
":. c #5EA6F3",
|
||||
">. c #47BCF9",
|
||||
",. c #51B5F8",
|
||||
"<. c #58BDF8",
|
||||
"1. c #68ABEF",
|
||||
"2. c #7DB9E7",
|
||||
"3. c #63AEF7",
|
||||
"4. c #6FB1F7",
|
||||
"5. c #66B9F8",
|
||||
"6. c #61B2F6",
|
||||
"7. c #71B4F7",
|
||||
"8. c #78B7F4",
|
||||
"9. c #72BFF9",
|
||||
"0. c #3BC0FA",
|
||||
"q. c #6FCEFB",
|
||||
"w. c #6CC5FA",
|
||||
"e. c #7BCAF9",
|
||||
"r. c #89A7C3",
|
||||
"t. c #83A2C1",
|
||||
"y. c #98B6D3",
|
||||
"u. c #9DB9D3",
|
||||
"i. c #89B6E4",
|
||||
"p. c #83B6E9",
|
||||
"a. c #81BDF7",
|
||||
"s. c #83BFF8",
|
||||
"d. c #9EC4E9",
|
||||
"f. c #8CC2F9",
|
||||
"g. c #85CDFB",
|
||||
"h. c #87C4F9",
|
||||
"j. c #92C6F9",
|
||||
"k. c #95CAFA",
|
||||
"l. c #9CCBFA",
|
||||
"z. c #89D7FC",
|
||||
"x. c #91D9FC",
|
||||
"c. c #9CDEFD",
|
||||
"v. c #9ED2FB",
|
||||
"b. c #A7CAEC",
|
||||
"n. c #B5CEE3",
|
||||
"m. c #A1CEFA",
|
||||
"M. c #AED0F0",
|
||||
"N. c #ACD6FA",
|
||||
"B. c #A0DFFC",
|
||||
"V. c #AFD8FC",
|
||||
"C. c #B5D9FB",
|
||||
"Z. c #BCDDFC",
|
||||
"A. c #BFDCF5",
|
||||
"S. c #ACE3FD",
|
||||
"D. c #B5E5FE",
|
||||
"F. c #BBE2FC",
|
||||
"G. c #CFE5F5",
|
||||
"H. c #C3E1FC",
|
||||
"J. c #CAE6FD",
|
||||
"K. c #CCEBFD",
|
||||
"L. c #C4EBFE",
|
||||
"P. c #D6EDFE",
|
||||
"I. c #DAEEFD",
|
||||
"U. c #DEF1FE",
|
||||
"Y. c #D6F2FE",
|
||||
"T. c #E4F4FE",
|
||||
"R. c #E9F6FE",
|
||||
"E. c #EBF8FF",
|
||||
"W. c None",
|
||||
/* pixels */
|
||||
"W.W.W.W.W.W.W.W.W.W.W.c.S.L.Y.E.E.S.X.} W.W.W.W.W.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.W.W.D.T.E.E.T.L.D.c.z.} } X.} } W.W.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.B.T.T.R.T.R.U.0.X.z.S.} } } } { { X.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.x.x.K.T.T.T.L.P.q.o.{ } } ` _ { { { { { W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.c.P.D.G.u.r.i 9 Z _ { { G 4 X t { { { { { { W.W.W.W.W.",
|
||||
"W.W.W.W.K.U.n.f O { = t { { { { [ { { W.W.W.W.",
|
||||
"W.W.W.F.I.t.. ' t { { [ [ [ [ [ >.W.W.W.",
|
||||
"W.W.x.P.V ' X t ` [ [ [ [ [ [ o.e.W.W.",
|
||||
"W.W.J.y. X t S Y Z $ ' . y [ [ [ ] [ [ | Z.J.W.W.",
|
||||
"W.<.e.& , _ ] ] [ ] U . ' . y [ ' [ ] ] ] w.K.J.g.W.",
|
||||
"W.' S o ' ' [ ' [ ' ] o ' . y Y 9 = = 9 @.J.J.J.F.W.",
|
||||
"W.| , j ' ' ' ' ' ' ' o ' . $ p A.J.J.g.",
|
||||
"' .. G ' ' ' ' ' ' ' o ' . M H.H.h.",
|
||||
",.2. . W ' W ' ' ' ' W . ' . M.A.x.",
|
||||
"N.M.. . W W W ' W W W W .w 9 I U 0 #.Z.m.",
|
||||
" .9.O D W W W W ' W j $ % F W W W .5 d Z.C.",
|
||||
"W W ; 9 9.h.5...Q % o j W W W W W W O. 3 C.N.",
|
||||
"E W 7 B b.d.a . w E E W W W E W E A @ C.l.",
|
||||
"I E l u W E W E W E E E E A . - k.6.",
|
||||
"P E E 7 m.o E E E E E E E E l . = E P ",
|
||||
"L E E E > . O s.o E E E E E E E E 7 , E L ",
|
||||
"W.R E R ) #.5 1 6 N i.2 s.+ E E E E E E R L . k R W.",
|
||||
"W.L R E -.m.m.m.m.m.m.2 m.@ N m.m.s.( R R % X E J W.",
|
||||
"W.W.K R ~ a.m.l.l.l.l.2 s.+ < i.l.m.j.h % e K W.W.",
|
||||
"W.W.J R R / l.l.l.l.k.2 s.+ * 5 + 8 R J W.W.",
|
||||
"W.W.W.v T R 3.k.k.j.k.2 2 j.& . 8 R v W.W.W.",
|
||||
"W.W.W.W.J T ~ 7.j.j.j.g +.p.j.s.+. . . : z T v W.W.W.W.",
|
||||
"W.W.W.W.W.c T T =.f.j.j.s.j.j.j.j.$.g s u e h b T T v W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.c b n 4.f.f.s.m.s.s.s.j.s.j./ T n T b c W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.c x 1.s.s.s.s.s.s.s.s.4.=.n T n c c W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.W.W.&.*.1.a.s.s.s.s.3.n n v x x W.W.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.W.W.W.W.%.%.%.%.*.*.Q x x x W.W.W.W.W.W.W.W.W.W.W."
|
||||
};
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
PYTHON = python3
|
||||
PREFIX = /usr/local
|
||||
DESTDIR =
|
||||
ICONSIZES = 16 24 32 48 64 128 256 512
|
||||
|
||||
SETUPTOOLSOPTIONS =
|
||||
ifdef DESTDIR
|
||||
SETUPTOOLSOPTS = --root="$(DESTDIR)"
|
||||
endif
|
||||
|
||||
.PHONY: install
|
||||
|
||||
doc/qutebrowser.1.html:
|
||||
a2x -f manpage doc/qutebrowser.1.asciidoc
|
||||
|
||||
install: doc/qutebrowser.1.html
|
||||
$(PYTHON) setup.py install --prefix="$(PREFIX)" --optimize=1 $(SETUPTOOLSOPTS)
|
||||
install -Dm644 misc/qutebrowser.appdata.xml \
|
||||
"$(DESTDIR)$(PREFIX)/share/metainfo/qutebrowser.appdata.xml"
|
||||
install -Dm644 doc/qutebrowser.1 \
|
||||
"$(DESTDIR)$(PREFIX)/share/man/man1/qutebrowser.1"
|
||||
install -Dm644 misc/qutebrowser.desktop \
|
||||
"$(DESTDIR)$(PREFIX)/share/applications/qutebrowser.desktop"
|
||||
$(foreach i,$(ICONSIZES),install -Dm644 "icons/qutebrowser-$(i)x$(i).png" \
|
||||
"$(DESTDIR)$(PREFIX)/share/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";)
|
||||
install -Dm644 icons/qutebrowser.svg \
|
||||
"$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/qutebrowser.svg"
|
||||
install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/userscripts/" \
|
||||
$(wildcard misc/userscripts/*)
|
||||
install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/scripts/" \
|
||||
$(filter-out scripts/__init__.py scripts/__pycache__ scripts/dev \
|
||||
scripts/testbrowser scripts/asciidoc2html.py scripts/setupcommon.py \
|
||||
scripts/link_pyqt.py,$(wildcard scripts/*))
|
||||
4370
misc/cheatsheet.svg
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 137 KiB |
35
misc/docker/archlinux/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
FROM base/archlinux
|
||||
MAINTAINER Florian Bruhin <me@the-compiler.org>
|
||||
|
||||
RUN echo 'Server = http://mirror.de.leaseweb.net/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist
|
||||
RUN pacman-key --init && pacman-key --populate archlinux && pacman -Sy --noconfirm archlinux-keyring
|
||||
|
||||
RUN pacman -Suyy --noconfirm
|
||||
RUN pacman-db-upgrade
|
||||
|
||||
RUN pacman -S --noconfirm \
|
||||
git \
|
||||
python-tox \
|
||||
qt5-base \
|
||||
qt5-webkit \
|
||||
python-pyqt5 \
|
||||
xorg-xinit \
|
||||
herbstluftwm \
|
||||
xorg-server-xvfb
|
||||
|
||||
RUN echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && locale-gen
|
||||
|
||||
RUN useradd user && mkdir /home/user && chown -R user:users /home/user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
ENV DISPLAY=:0
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
CMD Xvfb -screen 0 800x600x24 :0 & \
|
||||
sleep 2 && \
|
||||
herbstluftwm & \
|
||||
git clone /outside qutebrowser.git && \
|
||||
cd qutebrowser.git && \
|
||||
tox -e py35
|
||||
35
misc/docker/debian-jessie/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
FROM debian:jessie
|
||||
MAINTAINER Florian Bruhin <me@the-compiler.org>
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y dist-upgrade && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
python3-pyqt5 \
|
||||
python3-pyqt5.qtwebkit \
|
||||
python-tox \
|
||||
python3-sip \
|
||||
xvfb \
|
||||
git \
|
||||
python3-setuptools \
|
||||
wget \
|
||||
herbstluftwm \
|
||||
locales \
|
||||
libjs-pdf
|
||||
RUN echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && locale-gen
|
||||
|
||||
RUN useradd user && mkdir /home/user && chown -R user:users /home/user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
ENV DISPLAY=:0
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
CMD Xvfb -screen 0 800x600x24 :0 & \
|
||||
sleep 2 && \
|
||||
herbstluftwm & \
|
||||
git clone /outside qutebrowser.git && \
|
||||
cd qutebrowser.git && \
|
||||
tox -e py34
|
||||
37
misc/docker/ubuntu-wily/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
FROM ubuntu:wily
|
||||
MAINTAINER Florian Bruhin <me@the-compiler.org>
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y dist-upgrade && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
python3-pyqt5 \
|
||||
python3-pyqt5.qtwebkit \
|
||||
python-tox \
|
||||
python3-sip \
|
||||
xvfb \
|
||||
git \
|
||||
python3-setuptools \
|
||||
wget \
|
||||
herbstluftwm \
|
||||
language-pack-en \
|
||||
libjs-pdf \
|
||||
dbus
|
||||
|
||||
RUN dbus-uuidgen --ensure
|
||||
|
||||
RUN useradd user && mkdir /home/user && chown -R user:users /home/user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
ENV DISPLAY=:0
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
CMD Xvfb -screen 0 800x600x24 :0 & \
|
||||
sleep 2 && \
|
||||
herbstluftwm & \
|
||||
git clone /outside qutebrowser.git && \
|
||||
cd qutebrowser.git && \
|
||||
tox -e py34
|
||||
37
misc/docker/ubuntu-xenial/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Florian Bruhin <me@the-compiler.org>
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y dist-upgrade && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
python3-pyqt5 \
|
||||
python3-pyqt5.qtwebkit \
|
||||
python-tox \
|
||||
python3-sip \
|
||||
xvfb \
|
||||
git \
|
||||
python3-setuptools \
|
||||
wget \
|
||||
herbstluftwm \
|
||||
language-pack-en \
|
||||
libjs-pdf \
|
||||
dbus
|
||||
|
||||
RUN dbus-uuidgen --ensure
|
||||
|
||||
RUN useradd user && mkdir /home/user && chown -R user:users /home/user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
ENV DISPLAY=:0
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
CMD Xvfb -screen 0 800x600x24 :0 & \
|
||||
sleep 2 && \
|
||||
herbstluftwm & \
|
||||
git clone /outside qutebrowser.git && \
|
||||
cd qutebrowser.git && \
|
||||
tox -e py35
|
||||
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2017 suve <veg@svgames.pl> -->
|
||||
<component type="desktop">
|
||||
<id>org.qutebrowser.qutebrowser</id>
|
||||
<metadata_license>CC-BY-SA-3.0</metadata_license>
|
||||
<project_license>GPL-3.0</project_license>
|
||||
<name>qutebrowser</name>
|
||||
<summary>A keyboard-driven web browser</summary>
|
||||
<description>
|
||||
<p>
|
||||
qutebrowser is a keyboard-focused browser with a minimal GUI.
|
||||
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl,
|
||||
and is based on Python and PyQt5.
|
||||
</p>
|
||||
</description>
|
||||
<categories>
|
||||
<category>Network</category>
|
||||
<category>WebBrowser</category>
|
||||
</categories>
|
||||
<provides>
|
||||
<binary>qutebrowser</binary>
|
||||
</provides>
|
||||
<launchable type="desktop-id">qutebrowser.desktop</launchable>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/main.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/downloads.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/completion.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/hints.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<url type="homepage">https://www.qutebrowser.org</url>
|
||||
<url type="faq">https://qutebrowser.org/doc/faq.html</url>
|
||||
<url type="help">https://qutebrowser.org/doc/help/</url>
|
||||
<url type="bugtracker">https://github.com/qutebrowser/qutebrowser/issues/</url>
|
||||
<url type="donation">https://github.com/qutebrowser/qutebrowser#donating</url>
|
||||
<releases>
|
||||
<release version="1.3.0" date="2018-05-04"/>
|
||||
<release version="1.2.1" date="2018-03-14"/>
|
||||
<release version="1.2.0" date="2018-03-09"/>
|
||||
</releases>
|
||||
</component>
|
||||
@@ -1,77 +0,0 @@
|
||||
Name "qutebrowser"
|
||||
|
||||
Unicode true
|
||||
RequestExecutionLevel admin
|
||||
SetCompressor /solid lzma
|
||||
|
||||
!ifdef X64
|
||||
OutFile "..\dist\qutebrowser-${VERSION}-amd64.exe"
|
||||
InstallDir "$ProgramFiles64\qutebrowser"
|
||||
!else
|
||||
OutFile "..\dist\qutebrowser-${VERSION}-win32.exe"
|
||||
InstallDir "$ProgramFiles\qutebrowser"
|
||||
!endif
|
||||
|
||||
;Default installation folder
|
||||
|
||||
!include "MUI2.nsh"
|
||||
;!include "MultiUser.nsh"
|
||||
|
||||
!define MUI_ABORTWARNING
|
||||
;!define MULTIUSER_MUI
|
||||
;!define MULTIUSER_INSTALLMODE_COMMANDLINE
|
||||
!define MUI_ICON "../icons/qutebrowser.ico"
|
||||
!define MUI_UNICON "../icons/qutebrowser.ico"
|
||||
|
||||
!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
; depends on admin status
|
||||
;SetShellVarContext current
|
||||
|
||||
|
||||
Section "Install"
|
||||
|
||||
; Uninstall old versions
|
||||
ExecWait 'MsiExec.exe /quiet /qn /norestart /X{633F41F9-FE9B-42D1-9CC4-718CBD01EE11}'
|
||||
ExecWait 'MsiExec.exe /quiet /qn /norestart /X{9331D947-AC86-4542-A755-A833429C6E69}'
|
||||
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
!ifdef X64
|
||||
file /r "..\dist\qutebrowser-${VERSION}-x64\*.*"
|
||||
!else
|
||||
file /r "..\dist\qutebrowser-${VERSION}-x86\*.*"
|
||||
!endif
|
||||
|
||||
SetShellVarContext all
|
||||
CreateShortCut "$SMPROGRAMS\qutebrowser.lnk" "$INSTDIR\qutebrowser.exe"
|
||||
|
||||
;Create uninstaller
|
||||
WriteUninstaller "$INSTDIR\uninst.exe"
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "DisplayName" "qutebrowser"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "UninstallString" '"$INSTDIR\uninst.exe"'
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "QuietUninstallString" '"$INSTDIR\uninst.exe" /S'
|
||||
|
||||
SectionEnd
|
||||
|
||||
;--------------------------------
|
||||
;Uninstaller Section
|
||||
|
||||
Section "Uninstall"
|
||||
|
||||
SetShellVarContext all
|
||||
Delete "$SMPROGRAMS\qutebrowser.lnk"
|
||||
|
||||
RMDir /r "$INSTDIR\*.*"
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser"
|
||||
|
||||
SectionEnd
|
||||
@@ -15,14 +15,13 @@ def get_data_files():
|
||||
('../qutebrowser/img', 'img'),
|
||||
('../qutebrowser/javascript', 'javascript'),
|
||||
('../qutebrowser/html/doc', 'html/doc'),
|
||||
('../qutebrowser/git-commit-id', '.'),
|
||||
('../qutebrowser/config/configdata.yml', 'config'),
|
||||
('../qutebrowser/git-commit-id', '')
|
||||
]
|
||||
|
||||
# if os.path.exists(os.path.join('qutebrowser', '3rdparty', 'pdfjs')):
|
||||
# data_files.append(('../qutebrowser/3rdparty/pdfjs', '3rdparty/pdfjs'))
|
||||
# else:
|
||||
# print("Warning: excluding pdfjs as it's not present!")
|
||||
if os.path.exists(os.path.join('qutebrowser', '3rdparty', 'pdfjs')):
|
||||
data_files.append(('../qutebrowser/3rdparty/pdfjs', '3rdparty/pdfjs'))
|
||||
else:
|
||||
print("Warning: excluding pdfjs as it's not present!")
|
||||
|
||||
return data_files
|
||||
|
||||
@@ -42,10 +41,10 @@ a = Analysis(['../qutebrowser/__main__.py'],
|
||||
pathex=['misc'],
|
||||
binaries=None,
|
||||
datas=get_data_files(),
|
||||
hiddenimports=['PyQt5.QtOpenGL', 'PyQt5._QOpenGLFunctions_2_0'],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=['tkinter'],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher)
|
||||
@@ -58,19 +57,18 @@ exe = EXE(pyz,
|
||||
icon=icon,
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=False,
|
||||
console=False,
|
||||
version='misc/file_version_info.txt')
|
||||
upx=True,
|
||||
console=False )
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=False,
|
||||
upx=True,
|
||||
name='qutebrowser')
|
||||
|
||||
app = BUNDLE(coll,
|
||||
name='qutebrowser.app',
|
||||
icon=icon,
|
||||
# https://github.com/pyinstaller/pyinstaller/blob/b78bfe530cdc2904f65ce098bdf2de08c9037abb/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py#L24
|
||||
bundle_identifier='org.qt-project.Qt.QtWebEngineCore')
|
||||
info_plist={'NSHighResolutionCapable': 'True'},
|
||||
bundle_identifier=None)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
This directory contains various `requirements` files which are used by `tox` to
|
||||
have reproducible tests with pinned versions.
|
||||
have reproducable tests with pinned versions.
|
||||
|
||||
The files are generated based on unpinned requirements in `*.txt-raw` files.
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
check-manifest==0.37
|
||||
check-manifest==0.31
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
certifi==2018.4.16
|
||||
chardet==3.0.4
|
||||
codecov==2.0.15
|
||||
coverage==4.5.1
|
||||
idna==2.7
|
||||
requests==2.19.1
|
||||
urllib3==1.22
|
||||
codecov==2.0.5
|
||||
coverage==4.1
|
||||
requests==2.10.0
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyQt5==5.11.2
|
||||
PyQt5-sip==4.19.11
|
||||
cx-Freeze==4.3.4
|
||||
1
misc/requirements/requirements-cxfreeze.txt-raw
Normal file
@@ -0,0 +1 @@
|
||||
cx_Freeze
|
||||
@@ -1,27 +1,26 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
attrs==18.1.0
|
||||
flake8==3.5.0
|
||||
flake8-bugbear==18.2.0
|
||||
flake8-builtins==1.4.1 # rq.filter: != 1.4.0
|
||||
flake8-comprehensions==1.4.1
|
||||
flake8==2.6.2 # rq.filter: < 3.0.0
|
||||
flake8-copyright==0.2.0
|
||||
flake8-debugger==3.1.0
|
||||
flake8-deprecated==1.3
|
||||
flake8-docstrings==1.3.0
|
||||
flake8-future-import==0.4.4
|
||||
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
|
||||
mccabe==0.6.1
|
||||
pathmatch==0.2.1
|
||||
pep8-naming==0.7.0
|
||||
pycodestyle==2.3.1 # rq.filter: < 2.4.0
|
||||
pydocstyle==2.1.1
|
||||
pyflakes==2.0.0
|
||||
six==1.11.0
|
||||
snowballstemmer==1.2.1
|
||||
typing==3.6.4
|
||||
flake8-debugger==1.4.0
|
||||
flake8-deprecated==1.0
|
||||
flake8-docstrings==0.2.8
|
||||
flake8-future-import==0.4.3
|
||||
flake8-mock==0.2
|
||||
flake8-pep3101==0.4
|
||||
flake8-putty==0.4.0
|
||||
flake8-string-format==0.2.2
|
||||
flake8-tidy-imports==1.0.2
|
||||
flake8-tuple==0.2.12
|
||||
hacking==0.11.0
|
||||
mccabe==0.5.0
|
||||
packaging==16.7
|
||||
pbr==1.10.0
|
||||
pep257==0.7.0 # still needed by flake8-docstrings but ignored
|
||||
pep8==1.7.0
|
||||
pep8-naming==0.4.1
|
||||
pycodestyle==2.0.0
|
||||
pydocstyle==1.0.0
|
||||
pyflakes==1.2.3
|
||||
pyparsing==2.1.5
|
||||
six==1.10.0
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
flake8
|
||||
flake8-bugbear
|
||||
flake8-builtins!=1.4.0
|
||||
flake8-comprehensions
|
||||
flake8<3.0.0
|
||||
flake8-copyright
|
||||
flake8-debugger
|
||||
flake8-deprecated
|
||||
flake8-docstrings
|
||||
flake8-future-import
|
||||
flake8-mock
|
||||
flake8-per-file-ignores
|
||||
flake8-pep3101
|
||||
flake8-putty
|
||||
flake8-string-format
|
||||
flake8-tidy-imports
|
||||
flake8-tuple
|
||||
hacking
|
||||
pep8-naming
|
||||
pydocstyle
|
||||
pyflakes
|
||||
|
||||
# https://github.com/PyCQA/pycodestyle/issues/741
|
||||
#@ filter: pycodestyle < 2.4.0
|
||||
pep8==1.7.0
|
||||
|
||||
# https://github.com/gforcada/flake8-builtins/issues/36
|
||||
#@ filter: flake8-builtins != 1.4.0
|
||||
#@ comment: pep257 still needed by flake8-docstrings but ignored
|
||||
|
||||
# Waiting until hacking/flake8-tuple are updated
|
||||
#@ filter: flake8 < 3.0.0
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
appdirs==1.4.3
|
||||
packaging==17.1
|
||||
pyparsing==2.2.0
|
||||
setuptools==40.0.0
|
||||
six==1.11.0
|
||||
wheel==0.31.1
|
||||
pip==8.1.2
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
altgraph==0.15
|
||||
future==0.16.0
|
||||
macholib==1.9
|
||||
pefile==2017.11.5
|
||||
PyInstaller==3.3.1
|
||||
PyInstaller==3.2
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
-e git+https://github.com/PyCQA/astroid.git#egg=astroid
|
||||
certifi==2018.4.16
|
||||
chardet==3.0.4
|
||||
github3.py==1.1.0
|
||||
idna==2.7
|
||||
isort==4.3.4
|
||||
lazy-object-proxy==1.3.1
|
||||
mccabe==0.6.1
|
||||
isort==4.2.5
|
||||
lazy-object-proxy==1.2.2
|
||||
mccabe==0.5.0
|
||||
-e git+https://github.com/PyCQA/pylint.git#egg=pylint
|
||||
python-dateutil==2.7.3
|
||||
./scripts/dev/pylint_checkers
|
||||
requests==2.19.1
|
||||
six==1.11.0
|
||||
uritemplate==3.0.0
|
||||
urllib3==1.22
|
||||
wrapt==1.10.11
|
||||
requests==2.10.0
|
||||
six==1.10.0
|
||||
wrapt==1.10.8
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
-e git+https://github.com/PyCQA/pylint.git#egg=pylint
|
||||
./scripts/dev/pylint_checkers
|
||||
requests
|
||||
github3.py
|
||||
|
||||
# https://github.com/PyCQA/pylint/issues/932
|
||||
mccabe==0.5.0
|
||||
|
||||
# remove @commit-id for scm installs
|
||||
#@ replace: @.*# #
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
astroid==1.6.5
|
||||
certifi==2018.4.16
|
||||
chardet==3.0.4
|
||||
github3.py==1.1.0
|
||||
idna==2.7
|
||||
isort==4.3.4
|
||||
lazy-object-proxy==1.3.1
|
||||
mccabe==0.6.1
|
||||
pylint==1.9.2
|
||||
python-dateutil==2.7.3
|
||||
astroid==1.4.7
|
||||
isort==4.2.5
|
||||
lazy-object-proxy==1.2.2
|
||||
mccabe==0.5.0
|
||||
pylint==1.6.4
|
||||
./scripts/dev/pylint_checkers
|
||||
requests==2.19.1
|
||||
six==1.11.0
|
||||
uritemplate==3.0.0
|
||||
urllib3==1.22
|
||||
wrapt==1.10.11
|
||||
requests==2.10.0
|
||||
six==1.10.0
|
||||
wrapt==1.10.8
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
pylint
|
||||
./scripts/dev/pylint_checkers
|
||||
requests
|
||||
github3.py
|
||||
|
||||
# fix qute-pylint location
|
||||
#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
PyQt5
|
||||
@@ -1,4 +1,4 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
docutils==0.14
|
||||
pyroma==2.3.1
|
||||
docutils==0.12
|
||||
pyroma==2.0.2
|
||||
|
||||
@@ -4,4 +4,3 @@ pyPEG2
|
||||
PyYAML
|
||||
colorama
|
||||
cssutils
|
||||
attrs
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
bzr+lp:beautifulsoup
|
||||
git+https://github.com/cherrypy/cheroot.git
|
||||
hg+https://bitbucket.org/ned/coveragepy
|
||||
git+https://github.com/micheles/decorator.git
|
||||
git+https://github.com/pallets/flask.git
|
||||
git+https://github.com/miracle2k/python-glob2.git
|
||||
git+https://github.com/HypothesisWorks/hypothesis-python.git
|
||||
git+https://github.com/pallets/itsdangerous.git
|
||||
git+https://bitbucket.org/zzzeek/mako.git
|
||||
git+https://github.com/r1chardj0n3s/parse.git
|
||||
git+https://github.com/jenisys/parse_type.git
|
||||
hg+https://bitbucket.org/pytest-dev/py
|
||||
git+https://github.com/pytest-dev/pytest.git@features
|
||||
git+https://github.com/pytest-dev/pytest-bdd.git
|
||||
git+https://github.com/pytest-dev/pytest-cov.git
|
||||
git+https://github.com/pytest-dev/pytest-faulthandler.git
|
||||
git+https://github.com/pytest-dev/pytest-instafail.git
|
||||
git+https://github.com/pytest-dev/pytest-mock.git
|
||||
git+https://github.com/pytest-dev/pytest-qt.git
|
||||
git+https://github.com/pytest-dev/pytest-repeat.git
|
||||
git+https://github.com/pytest-dev/pytest-rerunfailures.git
|
||||
git+https://github.com/abusalimov/pytest-travis-fold.git
|
||||
git+https://github.com/The-Compiler/pytest-xvfb.git
|
||||
hg+https://bitbucket.org/gutworth/six
|
||||
hg+https://bitbucket.org/jendrikseipp/vulture
|
||||
git+https://github.com/pallets/werkzeug.git
|
||||
|
||||
|
||||
## qutebrowser dependencies
|
||||
|
||||
git+https://github.com/tartley/colorama.git
|
||||
hg+https://bitbucket.org/cthedot/cssutils
|
||||
git+https://github.com/pallets/jinja.git
|
||||
git+https://github.com/pallets/markupsafe.git
|
||||
hg+http://bitbucket.org/birkenfeld/pygments-main
|
||||
hg+https://bitbucket.org/fdik/pypeg
|
||||
git+https://github.com/python-attrs/attrs.git
|
||||
git+https://github.com/yaml/pyyaml.git
|
||||
@@ -1,40 +1,32 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
attrs==18.1.0
|
||||
beautifulsoup4==4.6.0
|
||||
cheroot==6.3.2.post0
|
||||
click==6.7
|
||||
# colorama==0.3.9
|
||||
coverage==4.5.1
|
||||
EasyProcess==0.2.3
|
||||
fields==5.0.0
|
||||
Flask==1.0.2
|
||||
glob2==0.6
|
||||
hunter==2.0.2
|
||||
hypothesis==3.66.1
|
||||
beautifulsoup4==4.5.0
|
||||
CherryPy==7.1.0
|
||||
coverage==4.1
|
||||
decorator==4.0.10
|
||||
Flask==0.10.1 # rq.filter: < 0.11.0
|
||||
glob2==0.4.1
|
||||
httpbin==0.4.1
|
||||
hypothesis==3.4.2
|
||||
itsdangerous==0.24
|
||||
# Jinja2==2.10
|
||||
Mako==1.0.7
|
||||
# MarkupSafe==1.0
|
||||
more-itertools==4.2.0
|
||||
parse==1.8.4
|
||||
parse-type==0.4.2
|
||||
pluggy==0.6.0
|
||||
py==1.5.4
|
||||
py-cpuinfo==4.0.0
|
||||
pytest==3.6.3
|
||||
pytest-bdd==2.21.0
|
||||
pytest-benchmark==3.1.1
|
||||
pytest-cov==2.5.1
|
||||
pytest-faulthandler==1.5.0
|
||||
pytest-instafail==0.4.0
|
||||
pytest-mock==1.10.0
|
||||
pytest-qt==2.4.1
|
||||
pytest-repeat==0.4.1
|
||||
pytest-rerunfailures==4.1
|
||||
pytest-travis-fold==1.3.0
|
||||
pytest-xvfb==1.1.0
|
||||
PyVirtualDisplay==0.2.1
|
||||
six==1.11.0
|
||||
vulture==0.28
|
||||
Werkzeug==0.14.1
|
||||
# Jinja2==2.8
|
||||
Mako==1.0.4
|
||||
# MarkupSafe==0.23
|
||||
parse==1.6.6
|
||||
parse-type==0.3.4
|
||||
py==1.4.31
|
||||
pytest==2.9.2
|
||||
pytest-bdd==2.17.0
|
||||
pytest-catchlog==1.2.2
|
||||
pytest-cov==2.3.0
|
||||
pytest-faulthandler==1.3.0
|
||||
pytest-instafail==0.3.0
|
||||
pytest-mock==1.1
|
||||
pytest-qt==1.11.0
|
||||
pytest-repeat==0.3.0
|
||||
pytest-rerunfailures==2.0.0
|
||||
pytest-travis-fold==1.2.0
|
||||
pytest-xvfb==0.2.0
|
||||
six==1.10.0
|
||||
vulture==0.10
|
||||
Werkzeug==0.11.10
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
beautifulsoup4
|
||||
cheroot
|
||||
CherryPy
|
||||
coverage
|
||||
Flask
|
||||
hunter
|
||||
Flask==0.10.1
|
||||
httpbin
|
||||
hypothesis
|
||||
pytest
|
||||
pytest-bdd
|
||||
pytest-benchmark
|
||||
pytest-catchlog
|
||||
pytest-cov
|
||||
pytest-faulthandler
|
||||
pytest-instafail
|
||||
@@ -18,4 +18,5 @@ pytest-travis-fold
|
||||
pytest-xvfb
|
||||
vulture
|
||||
|
||||
#@ ignore: Jinja2, MarkupSafe, colorama
|
||||
#@ filter: Flask < 0.11.0
|
||||
#@ ignore: Jinja2, MarkupSafe
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
pluggy==0.6.0
|
||||
py==1.5.4
|
||||
six==1.11.0
|
||||
tox==3.1.1
|
||||
virtualenv==16.0.0
|
||||
pluggy==0.3.1
|
||||
py==1.4.31
|
||||
tox==2.3.1
|
||||
virtualenv==15.0.2
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
vulture==0.28
|
||||
vulture==0.10
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Behaviour
|
||||
# Userscript for qutebrowser which casts the url passed in $1 to the default
|
||||
# ChromeCast device in the network using the program `castnow`
|
||||
#
|
||||
# Usage
|
||||
# You can launch the script from qutebrowser as follows:
|
||||
# spawn --userscript ${PATH_TO_FILE} {url}
|
||||
#
|
||||
# Then, you can control the chromecast by launching the simple command
|
||||
# `castnow` in a shell which will connect to the running castnow instance.
|
||||
#
|
||||
# For stopping the script, issue the command `pkill -f castnow` which would
|
||||
# then let the rest of the userscript execute for cleaning temporary file.
|
||||
#
|
||||
# Thanks
|
||||
# This userscript borrows Thorsten Wißmann's javascript code from his `mpv`
|
||||
# userscript.
|
||||
#
|
||||
# Dependencies
|
||||
# - castnow, https://github.com/xat/castnow
|
||||
#
|
||||
# Author
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
|
||||
if [ -z "$QUTE_FIFO" ] ; then
|
||||
cat 1>&2 <<EOF
|
||||
Error: $0 can not be run as a standalone script.
|
||||
|
||||
It is a qutebrowser userscript. In order to use it, call it using
|
||||
'spawn --userscript' as described in qute://help/userscripts.html
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg() {
|
||||
local cmd="$1"
|
||||
shift
|
||||
local msg="$*"
|
||||
if [ -z "$QUTE_FIFO" ] ; then
|
||||
echo "$cmd: $msg" >&2
|
||||
else
|
||||
echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
|
||||
fi
|
||||
}
|
||||
|
||||
js() {
|
||||
cat <<EOF
|
||||
|
||||
function descendantOfTagName(child, ancestorTagName) {
|
||||
// tells whether child has some (proper) ancestor
|
||||
// with the tag name ancestorTagName
|
||||
while (child.parentNode != null) {
|
||||
child = child.parentNode;
|
||||
if (typeof child.tagName === 'undefined') break;
|
||||
if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var App = {};
|
||||
|
||||
var all_videos = [];
|
||||
all_videos.push.apply(all_videos, document.getElementsByTagName("video"));
|
||||
all_videos.push.apply(all_videos, document.getElementsByTagName("object"));
|
||||
all_videos.push.apply(all_videos, document.getElementsByTagName("embed"));
|
||||
App.backup_videos = Array();
|
||||
App.all_replacements = Array();
|
||||
for (i = 0; i < all_videos.length; i++) {
|
||||
var video = all_videos[i];
|
||||
if (descendantOfTagName(video, "object")) {
|
||||
// skip tags that are contained in an object, because we hide
|
||||
// the object anyway.
|
||||
continue;
|
||||
}
|
||||
var replacement = document.createElement("div");
|
||||
replacement.innerHTML = "
|
||||
<p style=\\"margin-bottom: 0.5em\\">
|
||||
The video is being cast on your ChromeCast device.
|
||||
</p>
|
||||
<p>
|
||||
In order to restore this particular video
|
||||
<a style=\\"font-weight: bold;
|
||||
color: white;
|
||||
background: transparent;
|
||||
\\"
|
||||
onClick=\\"restore_video(this, " + i + ");\\"
|
||||
href=\\"javascript: restore_video(this, " + i + ")\\"
|
||||
>click here</a>.
|
||||
</p>
|
||||
";
|
||||
replacement.style.position = "relative";
|
||||
replacement.style.zIndex = "100003000000";
|
||||
replacement.style.fontSize = "1rem";
|
||||
replacement.style.textAlign = "center";
|
||||
replacement.style.verticalAlign = "middle";
|
||||
replacement.style.height = "100%";
|
||||
replacement.style.background = "#101010";
|
||||
replacement.style.color = "white";
|
||||
replacement.style.border = "4px dashed #545454";
|
||||
replacement.style.padding = "2em";
|
||||
replacement.style.margin = "auto";
|
||||
App.all_replacements[i] = replacement;
|
||||
App.backup_videos[i] = video;
|
||||
video.parentNode.replaceChild(replacement, video);
|
||||
}
|
||||
|
||||
function restore_video(obj, index) {
|
||||
obj = App.all_replacements[index];
|
||||
video = App.backup_videos[index];
|
||||
console.log(video);
|
||||
obj.parentNode.replaceChild(video, obj);
|
||||
}
|
||||
|
||||
/** force repainting the video, thanks to:
|
||||
* http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/
|
||||
*/
|
||||
var siteHeader = document.getElementById('header');
|
||||
siteHeader.style.display='none';
|
||||
siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough
|
||||
siteHeader.style.display='block';
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
printjs() {
|
||||
js | sed 's,//.*$,,' | tr '\n' ' '
|
||||
}
|
||||
echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
file_to_cast=${tmpdir}/qutecast
|
||||
program_=$(command -v castnow)
|
||||
|
||||
if [[ "${program_}" == "" ]]; then
|
||||
msg error "castnow can't be found..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# kill any running instance of castnow
|
||||
pkill -f "${program_}"
|
||||
|
||||
# start youtube download in stream mode (-o -) into temporary file
|
||||
youtube-dl -qo - "$1" > "${file_to_cast}" &
|
||||
ytdl_pid=$!
|
||||
|
||||
msg info "Casting $1" >> "$QUTE_FIFO"
|
||||
# start castnow in stream mode to cast on ChromeCast
|
||||
tail -F "${file_to_cast}" | ${program_} -
|
||||
|
||||
# cleanup remaining background process and file on disk
|
||||
kill ${ytdl_pid}
|
||||
rm -rf "${tmpdir}"
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
|
||||
# Copyright 2015-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -23,7 +22,7 @@
|
||||
# If run from qutebrowser as a userscript, it runs :open on the URL
|
||||
# If not, it opens a new qutebrowser window at the URL
|
||||
#
|
||||
# Ideal for use with tabs_are_windows. Set a hotkey to launch this script, then:
|
||||
# Ideal for use with tabs-are-windows. Set a hotkey to launch this script, then:
|
||||
# :bind o spawn --userscript dmenu_qutebrowser
|
||||
#
|
||||
# Use the hotkey to open in new tab/window, press 'o' to open URL in current tab/window
|
||||
@@ -41,7 +40,7 @@
|
||||
[ -z "$QUTE_URL" ] && QUTE_URL='http://google.com'
|
||||
|
||||
url=$(echo "$QUTE_URL" | cat - "$QUTE_CONFIG_DIR/quickmarks" "$QUTE_DATA_DIR/history" | dmenu -l 15 -p qutebrowser)
|
||||
url=$(echo "$url" | sed -E 's/[^ ]+ +//g' | grep -E "https?:" || echo "$url")
|
||||
url=$(echo "$url" | sed -E 's/[^ ]+ +//g' | egrep "https?:" || echo "$url")
|
||||
|
||||
[ -z "${url// }" ] && exit
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -euo pipefail
|
||||
#
|
||||
# Behavior:
|
||||
# Userscript for qutebrowser which will take the raw JSON text of the current
|
||||
# page, format it using `jq`, will add syntax highlighting using `pygments`,
|
||||
# and open the syntax highlighted pretty printed html in a new tab. If the file
|
||||
# is larger than 10MB then this script will only indent the json and will forego
|
||||
# syntax highlighting using pygments.
|
||||
#
|
||||
# In order to use this script, just start it using `spawn --userscript` from
|
||||
# qutebrowser. I recommend using an alias, e.g. put this in the
|
||||
# [alias]-section of qutebrowser.conf:
|
||||
#
|
||||
# json = spawn --userscript /path/to/json_format
|
||||
#
|
||||
# Note that the color style defaults to monokai, but a different pygments style
|
||||
# can be passed as the first parameter to the script. A full list of the pygments
|
||||
# styles can be found at: https://help.farbox.com/pygments.html
|
||||
#
|
||||
# Bryan Gilbert, 2017
|
||||
|
||||
# do not run pygmentize on files larger than this amount of bytes
|
||||
MAX_SIZE_PRETTIFY=10485760 # 10 MB
|
||||
# default style to monokai if none is provided
|
||||
STYLE=${1:-monokai}
|
||||
|
||||
TEMP_FILE="$(mktemp)"
|
||||
jq . "$QUTE_TEXT" >"$TEMP_FILE"
|
||||
|
||||
# try GNU stat first and then OSX stat if the former fails
|
||||
FILE_SIZE=$(
|
||||
stat --printf="%s" "$TEMP_FILE" 2>/dev/null ||
|
||||
stat -f%z "$TEMP_FILE" 2>/dev/null
|
||||
)
|
||||
if [ "$FILE_SIZE" -lt "$MAX_SIZE_PRETTIFY" ]; then
|
||||
pygmentize -l json -f html -O full,style="$STYLE" <"$TEMP_FILE" >"${TEMP_FILE}_"
|
||||
mv -f "${TEMP_FILE}_" "$TEMP_FILE"
|
||||
fi
|
||||
|
||||
# send the command to qutebrowser to open the new file containing the formatted json
|
||||
echo "open -t file://$TEMP_FILE" >> "$QUTE_FIFO"
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Qutebrowser userscript scraping the current web page for DOIs and downloading
|
||||
corresponding bibtex information.
|
||||
|
||||
Set the environment variable 'QUTE_BIB_FILEPATH' to indicate the path to
|
||||
download to. Otherwise, bibtex information is downloaded to '/tmp' and hence
|
||||
deleted at reboot.
|
||||
|
||||
Installation: see qute://help/userscripts.html
|
||||
|
||||
Inspired by
|
||||
https://ocefpaf.github.io/python4oceanographers/blog/2014/05/19/doi2bibtex/
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import re
|
||||
from collections import Counter
|
||||
from urllib import parse as url_parse
|
||||
from urllib import request as url_request
|
||||
|
||||
|
||||
FIFO_PATH = os.getenv("QUTE_FIFO")
|
||||
|
||||
def message_fifo(message, level="warning"):
|
||||
"""Send message to qutebrowser FIFO. The level must be one of 'info',
|
||||
'warning' (default) or 'error'."""
|
||||
with open(FIFO_PATH, "w") as fifo:
|
||||
fifo.write("message-{} '{}'".format(level, message))
|
||||
|
||||
|
||||
source = os.getenv("QUTE_TEXT")
|
||||
with open(source) as f:
|
||||
text = f.read()
|
||||
|
||||
# find DOIs on page using regex
|
||||
dval = re.compile(r'(10\.(\d)+/([^(\s\>\"\<)])+)')
|
||||
# https://stackoverflow.com/a/10324802/3865876, too strict
|
||||
# dval = re.compile(r'\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+)\b')
|
||||
dois = dval.findall(text)
|
||||
dois = Counter(e[0] for e in dois)
|
||||
try:
|
||||
doi = dois.most_common(1)[0][0]
|
||||
except IndexError:
|
||||
message_fifo("No DOIs found on page")
|
||||
sys.exit()
|
||||
message_fifo("Found {} DOIs on page, selecting {}".format(len(dois), doi),
|
||||
level="info")
|
||||
|
||||
# get bibtex data corresponding to DOI
|
||||
url = "http://dx.doi.org/" + url_parse.quote(doi)
|
||||
headers = dict(Accept='text/bibliography; style=bibtex')
|
||||
request = url_request.Request(url, headers=headers)
|
||||
response = url_request.urlopen(request)
|
||||
status_code = response.getcode()
|
||||
if status_code >= 400:
|
||||
message_fifo("Request returned {}".format(status_code))
|
||||
sys.exit()
|
||||
|
||||
# obtain content and format it
|
||||
bibtex = response.read().decode("utf-8").strip()
|
||||
bibtex = bibtex.replace(" ", "\n ", 1).\
|
||||
replace("}, ", "},\n ").replace("}}", "}\n}")
|
||||
|
||||
# append to file
|
||||
bib_filepath = os.getenv("QUTE_BIB_FILEPATH", "/tmp/qute.bib")
|
||||
with open(bib_filepath, "a") as f:
|
||||
f.write(bibtex + "\n\n")
|
||||
@@ -12,7 +12,7 @@
|
||||
# - rofi (in a recent version)
|
||||
# - xdg-open and xdg-mime
|
||||
# - You should configure qutebrowser to download files to a single directory
|
||||
# - It comes in handy if you enable downloads.remove_finished. If you want to
|
||||
# - It comes in handy if you enable remove-finished-downloads. If you want to
|
||||
# see the recent downloads, just press "sd".
|
||||
#
|
||||
# Thorsten Wißmann, 2015 (thorsten` on freenode)
|
||||
@@ -52,7 +52,7 @@ die() {
|
||||
if ! [ -d "$DOWNLOAD_DIR" ] ; then
|
||||
die "Download directory »$DOWNLOAD_DIR« not found!"
|
||||
fi
|
||||
if ! command -v "${ROFI_CMD}" > /dev/null ; then
|
||||
if ! which "${ROFI_CMD}" > /dev/null ; then
|
||||
die "Rofi command »${ROFI_CMD}« not found in PATH!"
|
||||
fi
|
||||
|
||||
@@ -76,7 +76,6 @@ crop-first-column() {
|
||||
ls-files() {
|
||||
# add the slash at the end of the download dir enforces to follow the
|
||||
# symlink, if the DOWNLOAD_DIR itself is a symlink
|
||||
# shellcheck disable=SC2010
|
||||
ls -Q --quoting-style escape -h -o -1 -A -t "${DOWNLOAD_DIR}/" \
|
||||
| grep '^[-]' \
|
||||
| cut -d' ' -f3- \
|
||||
@@ -92,10 +91,10 @@ if [ "${#entries[@]}" -eq 0 ] ; then
|
||||
die "Download directory »${DOWNLOAD_DIR}« empty"
|
||||
fi
|
||||
|
||||
line=$(printf '%s\n' "${entries[@]}" \
|
||||
line=$(printf "%s\n" "${entries[@]}" \
|
||||
| crop-first-column 55 \
|
||||
| column -s $'\t' -t \
|
||||
| $ROFI_CMD "${rofi_default_args[@]}" "$ROFI_ARGS") || true
|
||||
| $ROFI_CMD "${rofi_default_args[@]}" $ROFI_ARGS) || true
|
||||
if [ -z "$line" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 jnphilipp <me@jnphilipp.org>
|
||||
# Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -21,7 +20,7 @@
|
||||
|
||||
# Opens all links to feeds defined in the head of a site
|
||||
#
|
||||
# Ideal for use with tabs_are_windows. Set a hotkey to launch this script, then:
|
||||
# Ideal for use with tabs-are-windows. Set a hotkey to launch this script, then:
|
||||
# :bind gF spawn --userscript openfeeds
|
||||
#
|
||||
# Use the hotkey to open the feeds in new tab/window, press 'gF' to open
|
||||
|
||||
@@ -9,7 +9,7 @@ directly ask me via IRC (nickname thorsten\`) in #qutebrowser on freenode.
|
||||
|
||||
$blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
|
||||
WARNING: the passwords are stored in qutebrowser's
|
||||
debug log reachable via the url qute://log
|
||||
debug log reachable via the url qute:log
|
||||
$blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
|
||||
|
||||
Usage: run as a userscript form qutebrowser, e.g.:
|
||||
@@ -64,7 +64,7 @@ die() {
|
||||
javascript_escape() {
|
||||
# print the first argument in an escaped way, such that it can safely
|
||||
# be used within javascripts double quotes
|
||||
sed "s,[\\\\'\"],\\\\&,g" <<< "$1"
|
||||
sed "s,[\\\'\"],\\\&,g" <<< "$1"
|
||||
}
|
||||
|
||||
# ======================================================= #
|
||||
@@ -178,7 +178,7 @@ choose_entry_menu() {
|
||||
if [ "$nr" -eq 1 ] && ! ((menu_if_one_entry)) ; then
|
||||
file="${files[0]}"
|
||||
else
|
||||
file=$( printf '%s\n' "${files[@]}" | "${MENU_COMMAND[@]}" )
|
||||
file=$( printf "%s\n" "${files[@]}" | "${MENU_COMMAND[@]}" )
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ choose_entry_rofi() {
|
||||
}
|
||||
|
||||
choose_entry_zenity() {
|
||||
MENU_COMMAND=( zenity --list --title "qutebrowser password fill"
|
||||
MENU_COMMAND=( zenity --list --title "Qutebrowser password fill"
|
||||
--text "Pick the password entry:"
|
||||
--column "Name" )
|
||||
choose_entry_menu || true
|
||||
@@ -199,7 +199,7 @@ choose_entry_zenity_radio() {
|
||||
zenity_helper() {
|
||||
awk '{ print $0 ; print $0 }' \
|
||||
| zenity --list --radiolist \
|
||||
--title "qutebrowser password fill" \
|
||||
--title "Qutebrowser password fill" \
|
||||
--text "Pick the password entry:" \
|
||||
--column " " --column "Name"
|
||||
}
|
||||
@@ -220,7 +220,7 @@ user_pattern='^(user|username|login): '
|
||||
GPG_OPTS=( "--quiet" "--yes" "--compress-algo=none" "--no-encrypt-to" )
|
||||
GPG="gpg"
|
||||
export GPG_TTY="${GPG_TTY:-$(tty 2>/dev/null)}"
|
||||
command -v gpg2 &>/dev/null && GPG="gpg2"
|
||||
which gpg2 &>/dev/null && GPG="gpg2"
|
||||
[[ -n $GPG_AGENT_INFO || $GPG == "gpg2" ]] && GPG_OPTS+=( "--batch" "--use-agent" )
|
||||
|
||||
pass_backend() {
|
||||
@@ -236,7 +236,7 @@ pass_backend() {
|
||||
if ((match_line)) ; then
|
||||
# add entries with matching URL-tag
|
||||
while read -r -d "" passfile ; do
|
||||
if $GPG "${GPG_OPTS[@]}" -d "$passfile" \
|
||||
if $GPG "${GPG_OPTS}" -d "$passfile" \
|
||||
| grep --max-count=1 -iE "${match_line_pattern}${url}" > /dev/null
|
||||
then
|
||||
passfile="${passfile#$PREFIX}"
|
||||
@@ -269,7 +269,7 @@ pass_backend() {
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done < <($GPG "${GPG_OPTS[@]}" -d "$path" )
|
||||
done < <($GPG "${GPG_OPTS}" -d "$path" )
|
||||
}
|
||||
}
|
||||
# =======================================================
|
||||
@@ -283,8 +283,8 @@ secret_backend() {
|
||||
query_entries() {
|
||||
local domain="$1"
|
||||
while read -r line ; do
|
||||
if [[ "$line" == "attribute.username = "* ]] ; then
|
||||
files+=("$domain ${line:21}")
|
||||
if [[ "$line" =~ "attribute.username = " ]] ; then
|
||||
files+=("$domain ${line#${BASH_REMATCH[0]}}")
|
||||
fi
|
||||
done < <( secret-tool search --unlock --all domain "$domain" 2>&1 )
|
||||
}
|
||||
@@ -303,7 +303,6 @@ pass_backend
|
||||
QUTE_CONFIG_DIR=${QUTE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/qutebrowser/}
|
||||
PWFILL_CONFIG=${PWFILL_CONFIG:-${QUTE_CONFIG_DIR}/password_fill_rc}
|
||||
if [ -f "$PWFILL_CONFIG" ] ; then
|
||||
# shellcheck source=/dev/null
|
||||
source "$PWFILL_CONFIG"
|
||||
fi
|
||||
init
|
||||
@@ -312,7 +311,7 @@ simplify_url "$QUTE_URL"
|
||||
query_entries "${simple_url}"
|
||||
no_entries_found
|
||||
# remove duplicates
|
||||
mapfile -t files < <(printf '%s\n' "${files[@]}" | sort | uniq )
|
||||
mapfile -t files < <(printf "%s\n" "${files[@]}" | sort | uniq )
|
||||
choose_entry
|
||||
if [ -z "$file" ] ; then
|
||||
# choose_entry didn't want any of these entries
|
||||
@@ -328,17 +327,6 @@ open_entry "$file"
|
||||
|
||||
js() {
|
||||
cat <<EOF
|
||||
function isVisible(elem) {
|
||||
var style = elem.ownerDocument.defaultView.getComputedStyle(elem, null);
|
||||
|
||||
if (style.getPropertyValue("visibility") !== "visible" ||
|
||||
style.getPropertyValue("display") === "none" ||
|
||||
style.getPropertyValue("opacity") === "0") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return elem.offsetWidth > 0 && elem.offsetHeight > 0;
|
||||
};
|
||||
function hasPasswordField(form) {
|
||||
var inputs = form.getElementsByTagName("input");
|
||||
for (var j = 0; j < inputs.length; j++) {
|
||||
@@ -353,15 +341,11 @@ cat <<EOF
|
||||
var inputs = form.getElementsByTagName("input");
|
||||
for (var j = 0; j < inputs.length; j++) {
|
||||
var input = inputs[j];
|
||||
if (isVisible(input) && (input.type == "text" || input.type == "email")) {
|
||||
input.focus();
|
||||
if (input.type == "text" || input.type == "email") {
|
||||
input.value = "$(javascript_escape "${username}")";
|
||||
input.blur();
|
||||
}
|
||||
if (input.type == "password") {
|
||||
input.focus();
|
||||
input.value = "$(javascript_escape "${password}")";
|
||||
input.blur();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2018 Jay Kamat <jaygkamat@gmail.com>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""This userscript allows for insertion of usernames and passwords from keepass
|
||||
databases using pykeepass. Since it is a userscript, it must be run from
|
||||
qutebrowser.
|
||||
|
||||
A sample invocation of this script is:
|
||||
|
||||
:spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx
|
||||
|
||||
And a sample binding
|
||||
|
||||
:bind --mode=insert <ctrl-i> spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx
|
||||
|
||||
-p or --path is a required argument.
|
||||
|
||||
--keyfile-path allows you to specify a keepass keyfile. If you only use a
|
||||
keyfile, also add --no-password as well. Specifying --no-password without
|
||||
--keyfile-path will lead to an error.
|
||||
|
||||
login information is inserted using :insert-text and :fake-key <Tab>, which
|
||||
means you must have a cursor in position before initiating this userscript. If
|
||||
you do not do this, you will get 'element not editable' errors.
|
||||
|
||||
If keepass takes a while to open the DB, you might want to consider reducing
|
||||
the number of transform rounds in your database settings.
|
||||
|
||||
Dependencies: pykeepass (in python3), PyQt5. Without pykeepass, you will get an
|
||||
exit code of 100.
|
||||
|
||||
********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!******************
|
||||
|
||||
WARNING: The login details are viewable as plaintext in qutebrowser's debug log
|
||||
(qute://log) and could be compromised if you decide to submit a crash report!
|
||||
|
||||
********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!******************
|
||||
|
||||
"""
|
||||
|
||||
# pylint: disable=bad-builtin
|
||||
|
||||
import argparse
|
||||
import enum
|
||||
import functools
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtWidgets import QApplication, QInputDialog, QLineEdit
|
||||
|
||||
try:
|
||||
import pykeepass
|
||||
except ImportError as e:
|
||||
print("pykeepass not found: {}".format(str(e)), file=sys.stderr)
|
||||
|
||||
# Since this is a common error, try to print it to the FIFO if we can.
|
||||
if 'QUTE_FIFO' in os.environ:
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
|
||||
fifo.write('message-error "pykeepass failed to be imported."\n')
|
||||
fifo.flush()
|
||||
sys.exit(100)
|
||||
|
||||
argument_parser = argparse.ArgumentParser(
|
||||
description="Fill passwords using keepass.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=__doc__)
|
||||
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
|
||||
argument_parser.add_argument('--path', '-p', required=True,
|
||||
help='Path to the keepass db.')
|
||||
argument_parser.add_argument('--keyfile-path', '-k', default=None,
|
||||
help='Path to a keepass keyfile')
|
||||
argument_parser.add_argument(
|
||||
'--no-password', action='store_true',
|
||||
help='Supply if no password is required to unlock this database. '
|
||||
'Only allowed with --keyfile-path')
|
||||
argument_parser.add_argument(
|
||||
'--dmenu-invocation', '-d', default='dmenu',
|
||||
help='Invocation used to execute a dmenu-provider')
|
||||
argument_parser.add_argument(
|
||||
'--dmenu-format', '-f', default='{title}: {username}',
|
||||
help='Format string for keys to display in dmenu.'
|
||||
' Must generate a unique string.')
|
||||
argument_parser.add_argument(
|
||||
'--no-insert-mode', '-n', dest='insert_mode', action='store_false',
|
||||
help="Don't automatically enter insert mode")
|
||||
argument_parser.add_argument(
|
||||
'--io-encoding', '-i', default='UTF-8',
|
||||
help='Encoding used to communicate with subprocesses')
|
||||
group = argument_parser.add_mutually_exclusive_group()
|
||||
group.add_argument('--username-fill-only', '-e',
|
||||
action='store_true', help='Only insert username')
|
||||
group.add_argument('--password-fill-only', '-w',
|
||||
action='store_true', help='Only insert password')
|
||||
|
||||
CMD_DELAY = 50
|
||||
|
||||
|
||||
class ExitCodes(enum.IntEnum):
|
||||
"""Stores various exit codes groups to use."""
|
||||
SUCCESS = 0
|
||||
FAILURE = 1
|
||||
# 1 is automatically used if Python throws an exception
|
||||
NO_CANDIDATES = 2
|
||||
USER_QUIT = 3
|
||||
DB_OPEN_FAIL = 4
|
||||
|
||||
INTERNAL_ERROR = 10
|
||||
|
||||
|
||||
def qute_command(command):
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
|
||||
fifo.write(command + '\n')
|
||||
fifo.flush()
|
||||
|
||||
|
||||
def stderr(to_print):
|
||||
"""Extra functionality to echo out errors to qb ui."""
|
||||
print(to_print, file=sys.stderr)
|
||||
qute_command('message-error "{}"'.format(to_print))
|
||||
|
||||
|
||||
def dmenu(items, invocation, encoding):
|
||||
"""Runs dmenu with given arguments."""
|
||||
command = shlex.split(invocation)
|
||||
process = subprocess.run(command, input='\n'.join(items).encode(encoding),
|
||||
stdout=subprocess.PIPE)
|
||||
return process.stdout.decode(encoding).strip()
|
||||
|
||||
|
||||
def get_password():
|
||||
"""Get a keepass db password from user."""
|
||||
_app = QApplication(sys.argv)
|
||||
text, ok = QInputDialog.getText(
|
||||
None, "KeePass DB Password",
|
||||
"Please enter your KeePass Master Password",
|
||||
QLineEdit.Password)
|
||||
if not ok:
|
||||
stderr('Password Prompt Rejected.')
|
||||
sys.exit(ExitCodes.USER_QUIT)
|
||||
return text
|
||||
|
||||
|
||||
def find_candidates(args, host):
|
||||
"""Finds candidates that match host"""
|
||||
file_path = os.path.expanduser(args.path)
|
||||
|
||||
# TODO find a way to keep the db open, so we don't open (and query
|
||||
# password) it every time
|
||||
|
||||
pw = None
|
||||
if not args.no_password:
|
||||
pw = get_password()
|
||||
|
||||
kf = args.keyfile_path
|
||||
if kf:
|
||||
kf = os.path.expanduser(kf)
|
||||
|
||||
try:
|
||||
kp = pykeepass.PyKeePass(file_path, password=pw, keyfile=kf)
|
||||
except Exception as e:
|
||||
stderr("There was an error opening the DB: {}".format(str(e)))
|
||||
|
||||
return kp.find_entries(url="{}{}{}".format(".*", host, ".*"), regex=True)
|
||||
|
||||
|
||||
def candidate_to_str(args, candidate):
|
||||
"""Turns candidate into a human readable string for dmenu"""
|
||||
return args.dmenu_format.format(title=candidate.title,
|
||||
url=candidate.url,
|
||||
username=candidate.username,
|
||||
path=candidate.path,
|
||||
uuid=candidate.uuid)
|
||||
|
||||
|
||||
def candidate_to_secret(candidate):
|
||||
"""Turns candidate into a generic (user, password) tuple"""
|
||||
return (candidate.username, candidate.password)
|
||||
|
||||
|
||||
def run(args):
|
||||
"""Runs qute-keepass"""
|
||||
if not args.url:
|
||||
argument_parser.print_help()
|
||||
return ExitCodes.FAILURE
|
||||
|
||||
url_host = QUrl(args.url).host()
|
||||
|
||||
if not url_host:
|
||||
stderr('{} was not parsed as a valid URL!'.format(args.url))
|
||||
return ExitCodes.INTERNAL_ERROR
|
||||
|
||||
# Find candidates matching the host of the given URL
|
||||
candidates = find_candidates(args, url_host)
|
||||
if not candidates:
|
||||
stderr('No candidates for URL {!r} found!'.format(args.url))
|
||||
return ExitCodes.NO_CANDIDATES
|
||||
|
||||
# Create a map so we can get turn the resulting string from dmenu back into
|
||||
# a candidate
|
||||
candidates_strs = list(map(functools.partial(candidate_to_str, args),
|
||||
candidates))
|
||||
candidates_map = dict(zip(candidates_strs, candidates))
|
||||
|
||||
if len(candidates) == 1:
|
||||
selection = candidates.pop()
|
||||
else:
|
||||
selection = dmenu(candidates_strs,
|
||||
args.dmenu_invocation,
|
||||
args.io_encoding)
|
||||
|
||||
if selection not in candidates_map:
|
||||
stderr("'{}' was not a valid entry!").format(selection)
|
||||
return ExitCodes.USER_QUIT
|
||||
|
||||
selection = candidates_map[selection]
|
||||
|
||||
username, password = candidate_to_secret(selection)
|
||||
|
||||
insert_mode = ';; enter-mode insert' if args.insert_mode else ''
|
||||
if args.username_fill_only:
|
||||
qute_command('insert-text {}{}'.format(username, insert_mode))
|
||||
elif args.password_fill_only:
|
||||
qute_command('insert-text {}{}'.format(password, insert_mode))
|
||||
else:
|
||||
# Enter username and password using insert-key and fake-key <Tab>
|
||||
# (which supports more passwords than fake-key only), then switch back
|
||||
# into insert-mode, so the form can be directly submitted by hitting
|
||||
# enter afterwards. It dosen't matter when we go into insert mode, but
|
||||
# the other commands need to be be executed sequentially, so we add
|
||||
# delays with later.
|
||||
qute_command('insert-text {} ;;'
|
||||
'later {} fake-key <Tab> ;;'
|
||||
'later {} insert-text {}{}'
|
||||
.format(username, CMD_DELAY,
|
||||
CMD_DELAY * 2, password, insert_mode))
|
||||
|
||||
return ExitCodes.SUCCESS
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
arguments = argument_parser.parse_args()
|
||||
sys.exit(run(arguments))
|
||||
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com>
|
||||
# Adapted for LastPass by Wayne Cheng (welps) <waynethecheng@gmail.com>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published bjy
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Insert login information using lastpass CLI and a dmenu-compatible application (e.g. dmenu, rofi -dmenu, ...).
|
||||
A short demonstration can be seen here: https://i.imgur.com/zA61NrF.gifv.
|
||||
"""
|
||||
|
||||
USAGE = """The domain of the site has to be in the name of the LastPass entry, for example: "github.com/cryzed" or
|
||||
"websites/github.com". The login information is inserted by emulating key events using qutebrowser's fake-key command in this manner:
|
||||
[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms.
|
||||
|
||||
You must log into LastPass CLI using `lpass login <email>` prior to use of this script. The LastPass CLI agent only holds your master password for an hour by default. If you wish to change this, please see `man lpass`.
|
||||
|
||||
To use in qutebrowser, run: `spawn --userscript qute-lastpass`
|
||||
"""
|
||||
|
||||
EPILOG = """Dependencies: tldextract (Python 3 module), LastPass CLI (1.3 or newer)
|
||||
|
||||
WARNING: The login details are viewable as plaintext in qutebrowser's debug log (qute://log) and might be shared if
|
||||
you decide to submit a crash report!"""
|
||||
|
||||
import argparse
|
||||
import enum
|
||||
import fnmatch
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import tldextract
|
||||
|
||||
argument_parser = argparse.ArgumentParser(
|
||||
description=__doc__, usage=USAGE, epilog=EPILOG)
|
||||
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
|
||||
argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu',
|
||||
help='Invocation used to execute a dmenu-provider')
|
||||
argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false',
|
||||
help="Don't automatically enter insert mode")
|
||||
argument_parser.add_argument('--io-encoding', '-i', default='UTF-8',
|
||||
help='Encoding used to communicate with subprocesses')
|
||||
argument_parser.add_argument('--merge-candidates', '-m', action='store_true',
|
||||
help='Merge pass candidates for fully-qualified and registered domain name')
|
||||
group = argument_parser.add_mutually_exclusive_group()
|
||||
group.add_argument('--username-only', '-e',
|
||||
action='store_true', help='Only insert username')
|
||||
group.add_argument('--password-only', '-w',
|
||||
action='store_true', help='Only insert password')
|
||||
|
||||
stderr = functools.partial(print, file=sys.stderr)
|
||||
|
||||
class ExitCodes(enum.IntEnum):
|
||||
SUCCESS = 0
|
||||
FAILURE = 1
|
||||
# 1 is automatically used if Python throws an exception
|
||||
NO_PASS_CANDIDATES = 2
|
||||
COULD_NOT_MATCH_USERNAME = 3
|
||||
COULD_NOT_MATCH_PASSWORD = 4
|
||||
|
||||
def qute_command(command):
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
|
||||
fifo.write(command + '\n')
|
||||
fifo.flush()
|
||||
|
||||
def pass_(domain, encoding):
|
||||
args = ['lpass', 'show', '-x', '-j', '-G', '.*{:s}.*'.format(domain)]
|
||||
process = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
err = process.stderr.decode(encoding).strip()
|
||||
if err:
|
||||
msg = "LastPass CLI returned for {:s} - {:s}".format(domain, err)
|
||||
stderr(msg)
|
||||
return '[]'
|
||||
|
||||
out = process.stdout.decode(encoding).strip()
|
||||
|
||||
return out
|
||||
|
||||
def dmenu(items, invocation, encoding):
|
||||
command = shlex.split(invocation)
|
||||
process = subprocess.run(command, input='\n'.join(
|
||||
items).encode(encoding), stdout=subprocess.PIPE)
|
||||
return process.stdout.decode(encoding).strip()
|
||||
|
||||
|
||||
def fake_key_raw(text):
|
||||
for character in text:
|
||||
# Escape all characters by default, space requires special handling
|
||||
sequence = '" "' if character == ' ' else '\{}'.format(character)
|
||||
qute_command('fake-key {}'.format(sequence))
|
||||
|
||||
|
||||
def main(arguments):
|
||||
if not arguments.url:
|
||||
argument_parser.print_help()
|
||||
return ExitCodes.FAILURE
|
||||
|
||||
extract_result = tldextract.extract(arguments.url)
|
||||
|
||||
# Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains),
|
||||
# the registered domain name and finally: the IPv4 address if that's what
|
||||
# the URL represents
|
||||
candidates = []
|
||||
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.subdomain + extract_result.domain, extract_result.domain, extract_result.ipv4]):
|
||||
target_candidates = json.loads(pass_(target, arguments.io_encoding))
|
||||
if not target_candidates:
|
||||
continue
|
||||
|
||||
candidates = candidates + target_candidates
|
||||
if not arguments.merge_candidates:
|
||||
break
|
||||
else:
|
||||
if not candidates:
|
||||
stderr('No pass candidates for URL {!r} found!'.format(
|
||||
arguments.url))
|
||||
return ExitCodes.NO_PASS_CANDIDATES
|
||||
|
||||
if len(candidates) == 1:
|
||||
selection = candidates.pop()
|
||||
else:
|
||||
choices = ["{:s} | {:s} | {:s} | {:s}".format(c["id"], c["name"], c["url"], c["username"]) for c in candidates]
|
||||
choice = dmenu(choices, arguments.dmenu_invocation, arguments.io_encoding)
|
||||
choiceId = choice.split("|")[0].strip()
|
||||
selection = next((c for (i, c) in enumerate(candidates) if c["id"] == choiceId), None)
|
||||
|
||||
# Nothing was selected, simply return
|
||||
if not selection:
|
||||
return ExitCodes.SUCCESS
|
||||
|
||||
username = selection["username"]
|
||||
password = selection["password"]
|
||||
|
||||
if arguments.username_only:
|
||||
fake_key_raw(username)
|
||||
elif arguments.password_only:
|
||||
fake_key_raw(password)
|
||||
else:
|
||||
# Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch
|
||||
# back into insert-mode, so the form can be directly submitted by
|
||||
# hitting enter afterwards
|
||||
fake_key_raw(username)
|
||||
qute_command('fake-key <Tab>')
|
||||
fake_key_raw(password)
|
||||
|
||||
if arguments.insert_mode:
|
||||
qute_command('enter-mode insert')
|
||||
|
||||
return ExitCodes.SUCCESS
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
arguments = argument_parser.parse_args()
|
||||
sys.exit(main(arguments))
|
||||
@@ -1,207 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Insert login information using pass and a dmenu-compatible application (e.g. dmenu, rofi -dmenu, ...). A short
|
||||
demonstration can be seen here: https://i.imgur.com/KN3XuZP.gif.
|
||||
"""
|
||||
|
||||
USAGE = """The domain of the site has to appear as a segment in the pass path, for example: "github.com/cryzed" or
|
||||
"websites/github.com". How the username and password are determined is freely configurable using the CLI arguments. The
|
||||
login information is inserted by emulating key events using qutebrowser's fake-key command in this manner:
|
||||
[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms.
|
||||
|
||||
Suggested bindings similar to Uzbl's `formfiller` script:
|
||||
|
||||
config.bind('<z><l>', 'spawn --userscript qute-pass')
|
||||
config.bind('<z><u><l>', 'spawn --userscript qute-pass --username-only')
|
||||
config.bind('<z><p><l>', 'spawn --userscript qute-pass --password-only')
|
||||
config.bind('<z><o><l>', 'spawn --userscript qute-pass --otp-only')
|
||||
"""
|
||||
|
||||
EPILOG = """Dependencies: tldextract (Python 3 module), pass, pass-otp (optional).
|
||||
For issues and feedback please use: https://github.com/cryzed/qutebrowser-userscripts.
|
||||
|
||||
WARNING: The login details are viewable as plaintext in qutebrowser's debug log (qute://log) and might be shared if
|
||||
you decide to submit a crash report!"""
|
||||
|
||||
import argparse
|
||||
import enum
|
||||
import fnmatch
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import tldextract
|
||||
|
||||
argument_parser = argparse.ArgumentParser(description=__doc__, usage=USAGE, epilog=EPILOG)
|
||||
argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
|
||||
argument_parser.add_argument('--password-store', '-p', default=os.path.expanduser('~/.password-store'),
|
||||
help='Path to your pass password-store')
|
||||
argument_parser.add_argument('--username-pattern', '-u', default=r'.*/(.+)',
|
||||
help='Regular expression that matches the username')
|
||||
argument_parser.add_argument('--username-target', '-U', choices=['path', 'secret'], default='path',
|
||||
help='The target for the username regular expression')
|
||||
argument_parser.add_argument('--password-pattern', '-P', default=r'(.*)',
|
||||
help='Regular expression that matches the password')
|
||||
argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu',
|
||||
help='Invocation used to execute a dmenu-provider')
|
||||
argument_parser.add_argument('--no-insert-mode', '-n', dest='insert_mode', action='store_false',
|
||||
help="Don't automatically enter insert mode")
|
||||
argument_parser.add_argument('--io-encoding', '-i', default='UTF-8',
|
||||
help='Encoding used to communicate with subprocesses')
|
||||
argument_parser.add_argument('--merge-candidates', '-m', action='store_true',
|
||||
help='Merge pass candidates for fully-qualified and registered domain name')
|
||||
group = argument_parser.add_mutually_exclusive_group()
|
||||
group.add_argument('--username-only', '-e', action='store_true', help='Only insert username')
|
||||
group.add_argument('--password-only', '-w', action='store_true', help='Only insert password')
|
||||
group.add_argument('--otp-only', '-o', action='store_true', help='Only insert OTP code')
|
||||
|
||||
stderr = functools.partial(print, file=sys.stderr)
|
||||
|
||||
|
||||
class ExitCodes(enum.IntEnum):
|
||||
SUCCESS = 0
|
||||
FAILURE = 1
|
||||
# 1 is automatically used if Python throws an exception
|
||||
NO_PASS_CANDIDATES = 2
|
||||
COULD_NOT_MATCH_USERNAME = 3
|
||||
COULD_NOT_MATCH_PASSWORD = 4
|
||||
|
||||
|
||||
def qute_command(command):
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
|
||||
fifo.write(command + '\n')
|
||||
fifo.flush()
|
||||
|
||||
|
||||
def find_pass_candidates(domain, password_store_path):
|
||||
candidates = []
|
||||
for path, directories, file_names in os.walk(password_store_path):
|
||||
if directories or domain not in path.split(os.path.sep):
|
||||
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)
|
||||
return candidates
|
||||
|
||||
|
||||
def _run_pass(command, encoding):
|
||||
process = subprocess.run(command, stdout=subprocess.PIPE)
|
||||
return process.stdout.decode(encoding).strip()
|
||||
|
||||
|
||||
def pass_(path, encoding):
|
||||
return _run_pass(['pass', path], encoding)
|
||||
|
||||
|
||||
def pass_otp(path, encoding):
|
||||
return _run_pass(['pass', 'otp', path], encoding)
|
||||
|
||||
|
||||
def dmenu(items, invocation, encoding):
|
||||
command = shlex.split(invocation)
|
||||
process = subprocess.run(command, input='\n'.join(items).encode(encoding), stdout=subprocess.PIPE)
|
||||
return process.stdout.decode(encoding).strip()
|
||||
|
||||
|
||||
def fake_key_raw(text):
|
||||
for character in text:
|
||||
# Escape all characters by default, space requires special handling
|
||||
sequence = '" "' if character == ' ' else '\{}'.format(character)
|
||||
qute_command('fake-key {}'.format(sequence))
|
||||
|
||||
|
||||
def main(arguments):
|
||||
if not arguments.url:
|
||||
argument_parser.print_help()
|
||||
return ExitCodes.FAILURE
|
||||
|
||||
extract_result = tldextract.extract(arguments.url)
|
||||
|
||||
# Expand potential ~ in paths, since this script won't be called from a shell that does it for us
|
||||
password_store_path = os.path.expanduser(arguments.password_store)
|
||||
|
||||
# Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains),
|
||||
# the registered domain name and finally: the IPv4 address if that's what the URL represents
|
||||
candidates = set()
|
||||
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.ipv4]):
|
||||
target_candidates = find_pass_candidates(target, password_store_path)
|
||||
if not target_candidates:
|
||||
continue
|
||||
|
||||
candidates.update(target_candidates)
|
||||
if not arguments.merge_candidates:
|
||||
break
|
||||
else:
|
||||
if not candidates:
|
||||
stderr('No pass candidates for URL {!r} found!'.format(arguments.url))
|
||||
return ExitCodes.NO_PASS_CANDIDATES
|
||||
|
||||
selection = candidates.pop() if len(candidates) == 1 else dmenu(sorted(candidates), arguments.dmenu_invocation,
|
||||
arguments.io_encoding)
|
||||
# Nothing was selected, simply return
|
||||
if not selection:
|
||||
return ExitCodes.SUCCESS
|
||||
|
||||
secret = pass_(selection, arguments.io_encoding)
|
||||
|
||||
# Match username
|
||||
target = selection if arguments.username_target == 'path' else secret
|
||||
match = re.match(arguments.username_pattern, target)
|
||||
if not match:
|
||||
stderr('Failed to match username pattern on {}!'.format(arguments.username_target))
|
||||
return ExitCodes.COULD_NOT_MATCH_USERNAME
|
||||
username = match.group(1)
|
||||
|
||||
# Match password
|
||||
match = re.match(arguments.password_pattern, secret)
|
||||
if not match:
|
||||
stderr('Failed to match password pattern on secret!')
|
||||
return ExitCodes.COULD_NOT_MATCH_PASSWORD
|
||||
password = match.group(1)
|
||||
|
||||
if arguments.username_only:
|
||||
fake_key_raw(username)
|
||||
elif arguments.password_only:
|
||||
fake_key_raw(password)
|
||||
elif arguments.otp_only:
|
||||
otp = pass_otp(selection, arguments.io_encoding)
|
||||
fake_key_raw(otp)
|
||||
else:
|
||||
# Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch
|
||||
# back into insert-mode, so the form can be directly submitted by hitting enter afterwards
|
||||
fake_key_raw(username)
|
||||
qute_command('fake-key <Tab>')
|
||||
fake_key_raw(password)
|
||||
|
||||
if arguments.insert_mode:
|
||||
qute_command('enter-mode insert')
|
||||
|
||||
return ExitCodes.SUCCESS
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
arguments = argument_parser.parse_args()
|
||||
sys.exit(main(arguments))
|
||||
24
qutebrowser/qt.py → misc/userscripts/qutebrowser_viewsource
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -17,12 +17,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Wrappers around Qt/PyQt code."""
|
||||
#
|
||||
# This script fetches the unprocessed HTML source for a page and opens it in vim.
|
||||
# :bind gf spawn --userscript qutebrowser_viewsource
|
||||
#
|
||||
# Caveat: Does not use authentication of any kind. Add it in if you want it to.
|
||||
#
|
||||
|
||||
# pylint: disable=unused-import
|
||||
# PyQt 5.11 comes with a bundled sip,
|
||||
# for older PyQt versions it's a separate module.
|
||||
try:
|
||||
from PyQt5 import sip
|
||||
except ImportError:
|
||||
import sip
|
||||
path=/tmp/qutebrowser_$(mktemp XXXXXXXX).html
|
||||
|
||||
curl "$QUTE_URL" > $path
|
||||
urxvt -e vim "$path"
|
||||
|
||||
rm "$path"
|
||||
@@ -35,12 +35,17 @@ get_selection() {
|
||||
|
||||
# Main
|
||||
# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/font
|
||||
[[ -s $confdir/dmenu/font ]] && read -r font < "$confdir"/dmenu/font
|
||||
if [[ -s $confdir/dmenu/font ]]; then
|
||||
read -r font < "$confdir"/dmenu/font
|
||||
fi
|
||||
|
||||
[[ $font ]] && opts+=(-fn "$font")
|
||||
if [[ $font ]]; then
|
||||
opts+=(-fn "$font")
|
||||
fi
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
[[ -s $optsfile ]] && source "$optsfile"
|
||||
if [[ -s $optsfile ]]; then
|
||||
source "$optsfile"
|
||||
fi
|
||||
|
||||
url=$(get_selection)
|
||||
url=${url/*http/http}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Executes python-readability on current page and opens the summary as new tab.
|
||||
#
|
||||
# Depends on the python-readability package, or its fork:
|
||||
#
|
||||
# - https://github.com/buriy/python-readability
|
||||
# - https://github.com/bookieio/breadability
|
||||
#
|
||||
# Usage:
|
||||
# :spawn --userscript readability
|
||||
#
|
||||
from __future__ import absolute_import
|
||||
import codecs, os
|
||||
|
||||
tmpfile = os.path.join(
|
||||
os.environ.get('QUTE_DATA_DIR',
|
||||
os.path.expanduser('~/.local/share/qutebrowser')),
|
||||
'userscripts/readability.html')
|
||||
|
||||
if not os.path.exists(os.path.dirname(tmpfile)):
|
||||
os.makedirs(os.path.dirname(tmpfile))
|
||||
|
||||
with codecs.open(os.environ['QUTE_HTML'], 'r', 'utf-8') as source:
|
||||
data = source.read()
|
||||
|
||||
try:
|
||||
from breadability.readable import Article as reader
|
||||
doc = reader(data)
|
||||
content = doc.readable
|
||||
except ImportError:
|
||||
from readability import Document
|
||||
doc = Document(data)
|
||||
content = doc.summary().replace('<html>', '<html><head><title>%s</title></head>' % doc.title())
|
||||
|
||||
with codecs.open(tmpfile, 'w', 'utf-8') as target:
|
||||
target.write('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />')
|
||||
target.write(content)
|
||||
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
|
||||
fifo.write('open -t %s' % tmpfile)
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Adds DuckDuckGo bang as searchengine.
|
||||
#
|
||||
# Usage:
|
||||
# :spawn --userscript ripbang [bang]...
|
||||
#
|
||||
# Example:
|
||||
# :spawn --userscript ripbang amazon maps
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import os, re, requests, sys
|
||||
|
||||
try:
|
||||
from urllib.parse import unquote
|
||||
except ImportError:
|
||||
from urllib import unquote
|
||||
|
||||
for argument in sys.argv[1:]:
|
||||
bang = '!' + argument
|
||||
r = requests.get('https://duckduckgo.com/',
|
||||
params={'q': bang + ' SEARCHTEXT'})
|
||||
|
||||
searchengine = unquote(re.search("url=[^']+", r.text).group(0))
|
||||
searchengine = searchengine.replace('url=', '')
|
||||
searchengine = searchengine.replace('/l/?kh=-1&uddg=', '')
|
||||
searchengine = searchengine.replace('SEARCHTEXT', '{}')
|
||||
|
||||
if os.getenv('QUTE_FIFO'):
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as fifo:
|
||||
fifo.write('set searchengines %s %s' % (bang, searchengine))
|
||||
else:
|
||||
print('%s %s' % (bang, searchengine))
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright 2016 Jan Verbeek (blyxxyz) <ring@openmailbox.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/>.
|
||||
|
||||
# This script keeps track of URLs in RSS feeds and opens new ones.
|
||||
# New feeds can be added with ':spawn -u /path/to/userscripts/rss add' or
|
||||
# ':spawn -u /path/to/userscripts/rss <url>'.
|
||||
# New items can be opened with ':spawn -u /path/to/userscripts/rss'.
|
||||
# The script doesn't really parse XML, and searches for things that look like
|
||||
# item links. It might open things that aren't real links, and it might miss
|
||||
# real links.
|
||||
|
||||
config_dir="$HOME/.qute-rss"
|
||||
|
||||
add_feed () {
|
||||
touch "feeds"
|
||||
if grep -Fq "$1" "feeds"; then
|
||||
notice "$1 is saved already."
|
||||
else
|
||||
printf '%s\n' "$1" >> "feeds"
|
||||
fi
|
||||
}
|
||||
|
||||
# Show an error message and exit
|
||||
fail () {
|
||||
echo "message-error '$*'" > "$QUTE_FIFO"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get a sorted list of item URLs from a RSS feed
|
||||
get_items () {
|
||||
$curl "$@" | grep "$text_only" -zo -e '<guid[^<>]*>[^<>]*</guid>' \
|
||||
-e '<link[^<>]*>[^<>]*</link>' \
|
||||
-e '<link[^<>]*href="[^"]*"' |
|
||||
grep "$text_only" -o 'http[^<>"]*' | sort | uniq
|
||||
}
|
||||
|
||||
# Show an info message
|
||||
notice () {
|
||||
echo "message-info '$*'" > "$QUTE_FIFO"
|
||||
}
|
||||
|
||||
# Update a database of a feed and open new URLs
|
||||
read_items () {
|
||||
cd read_urls || return 1
|
||||
feed_file="$(echo "$1" | tr -d /)"
|
||||
feed_temp_file="$(mktemp "$feed_file.tmp.XXXXXXXXXX")"
|
||||
feed_new_items="$(mktemp "$feed_file.new.XXXXXXXXXX")"
|
||||
get_items "$1" > "$feed_temp_file"
|
||||
if [ ! -s "$feed_temp_file" ]; then
|
||||
notice "No items found for $1."
|
||||
rm "$feed_temp_file" "$feed_new_items"
|
||||
elif [ ! -f "$feed_file" ]; then
|
||||
notice "$1 is a new feed. All items will be marked as read."
|
||||
mv "$feed_temp_file" "$feed_file"
|
||||
rm "$feed_new_items"
|
||||
else
|
||||
sort -o "$feed_file" "$feed_file"
|
||||
comm -2 -3 "$feed_temp_file" "$feed_file" | tee "$feed_new_items"
|
||||
cat "$feed_new_items" >> "$feed_file"
|
||||
sort -o "$feed_file" "$feed_file"
|
||||
rm "$feed_temp_file" "$feed_new_items"
|
||||
fi | while read -r item; do
|
||||
echo "open -t $item" > "$QUTE_FIFO"
|
||||
done
|
||||
}
|
||||
|
||||
if [ ! -d "$config_dir/read_urls" ]; then
|
||||
notice "Creating configuration directory."
|
||||
mkdir -p "$config_dir/read_urls"
|
||||
fi
|
||||
|
||||
cd "$config_dir" || exit 1
|
||||
|
||||
if [ $# != 0 ]; then
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "add" ]; then
|
||||
add_feed "$QUTE_URL"
|
||||
else
|
||||
add_feed "$arg"
|
||||
fi
|
||||
done
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -f "feeds" ]; then
|
||||
fail "Add feeds by running ':spawn -u rss add' or ':spawn -u rss <url>'."
|
||||
fi
|
||||
|
||||
if curl --version >&-; then
|
||||
curl="curl -sL"
|
||||
elif wget --version >&-; then
|
||||
curl="wget -qO -"
|
||||
else
|
||||
fail "Either curl or wget is needed to run this script."
|
||||
fi
|
||||
|
||||
# Detect GNU grep so we can force it to treat everything as text
|
||||
if < /dev/null grep --help 2>&1 | grep -q -- -a; then
|
||||
text_only="-a"
|
||||
fi
|
||||
|
||||
while read -r feed_url; do
|
||||
read_items "$feed_url" &
|
||||
done < "$config_dir/feeds"
|
||||
|
||||
wait
|
||||
@@ -25,10 +25,12 @@
|
||||
[[ $QUTE_MODE == 'hints' ]] && title=$QUTE_SELECTED_TEXT || title=$QUTE_TITLE
|
||||
|
||||
# try to add the task and grab the output
|
||||
if msg="$(task add "$title" "$*" 2>&1)"; then
|
||||
msg="$(task add $title $@ 2>&1)"
|
||||
|
||||
if [[ $? == 0 ]]; then
|
||||
# annotate the new task with the url, send the output back to the browser
|
||||
task +LATEST annotate "$QUTE_URL"
|
||||
echo "message-info '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO"
|
||||
echo "message-info '$msg'" >> $QUTE_FIFO
|
||||
else
|
||||
echo "message-error '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO"
|
||||
echo "message-error '$msg'" >> $QUTE_FIFO
|
||||
fi
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2018 jnphilipp <mail@jnphilipp.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/>.
|
||||
|
||||
# Change your tor identity.
|
||||
#
|
||||
# Set a hotkey to launch this script, then:
|
||||
# :bind ti spawn --userscript tor_identity PASSWORD
|
||||
#
|
||||
# Use the hotkey to change your tor identity, press 'ti' to change it.
|
||||
# https://stem.torproject.org/faq.html#how-do-i-request-a-new-identity-from-tor
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
from stem import Signal
|
||||
from stem.control import Controller
|
||||
except ImportError:
|
||||
if os.getenv('QUTE_FIFO'):
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as f:
|
||||
f.write('message-error "Failed to import stem."')
|
||||
else:
|
||||
print('Failed to import stem.')
|
||||
|
||||
|
||||
password = sys.argv[1]
|
||||
with Controller.from_port(port=9051) as controller:
|
||||
controller.authenticate(password)
|
||||
controller.signal(Signal.NEWNYM)
|
||||
if os.getenv('QUTE_FIFO'):
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as f:
|
||||
f.write('message-info "Tor identity changed."')
|
||||
else:
|
||||
print('Tor identity changed.')
|
||||
@@ -49,8 +49,8 @@ msg() {
|
||||
|
||||
MPV_COMMAND=${MPV_COMMAND:-mpv}
|
||||
# Warning: spaces in single flags are not supported
|
||||
MPV_FLAGS=${MPV_FLAGS:- --force-window --no-terminal --keep-open=yes --ytdl}
|
||||
IFS=" " read -r -a video_command <<< "$MPV_COMMAND $MPV_FLAGS"
|
||||
MPV_FLAGS=${MPV_FLAGS:- --force-window --no-terminal --keep-open=yes --ytdl }
|
||||
video_command=( "$MPV_COMMAND" $MPV_FLAGS )
|
||||
|
||||
js() {
|
||||
cat <<EOF
|
||||
@@ -140,4 +140,4 @@ printjs() {
|
||||
echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
|
||||
|
||||
msg info "Opening $QUTE_URL with mpv"
|
||||
"${video_command[@]}" "$@" "$QUTE_URL"
|
||||
"${video_command[@]}" "$QUTE_URL"
|
||||
|
||||
48
pytest.ini
@@ -1,14 +1,12 @@
|
||||
[pytest]
|
||||
log_level = NOTSET
|
||||
addopts = --strict -rfEw --faulthandler-timeout=90 --instafail --pythonwarnings error --benchmark-columns=Min,Max,Median
|
||||
testpaths = tests
|
||||
addopts = --strict -rfEw --faulthandler-timeout=70 --instafail
|
||||
markers =
|
||||
gui: Tests using the GUI (e.g. spawning widgets)
|
||||
posix: Tests which only can run on a POSIX OS.
|
||||
windows: Tests which only can run on Windows.
|
||||
linux: Tests which only can run on Linux.
|
||||
mac: Tests which only can run on macOS.
|
||||
not_mac: Tests which can not run on macOS.
|
||||
osx: Tests which only can run on OS X.
|
||||
not_osx: Tests which can not run on OS X.
|
||||
not_frozen: Tests which can't be run if sys.frozen is True.
|
||||
no_xvfb: Tests which can't be run with Xvfb.
|
||||
frozen: Tests which can only be run if sys.frozen is True.
|
||||
@@ -16,32 +14,15 @@ markers =
|
||||
end2end: End to end tests which run qutebrowser as subprocess
|
||||
xfail_norun: xfail the test with out running it
|
||||
ci: Tests which should only run on CI.
|
||||
no_ci: Tests which should not run on CI.
|
||||
qtwebengine_todo: Features still missing with QtWebEngine
|
||||
qtwebengine_skip: Tests not applicable with QtWebEngine
|
||||
qtwebkit_skip: Tests not applicable with QtWebKit
|
||||
qtwebengine_flaky: Tests which are flaky (and currently skipped) with QtWebEngine
|
||||
qtwebengine_mac_xfail: Tests which fail on macOS with QtWebEngine
|
||||
js_prompt: Tests needing to display a javascript prompt
|
||||
this: Used to mark tests during development
|
||||
no_invalid_lines: Don't fail on unparseable lines in end2end tests
|
||||
issue2478: Tests which are broken on Windows with QtWebEngine, https://github.com/qutebrowser/qutebrowser/issues/2478
|
||||
issue3572: Tests which are broken with QtWebEngine and Qt 5.10, https://github.com/qutebrowser/qutebrowser/issues/3572
|
||||
qtbug60673: Tests which are broken if the conversion from orange selection to real selection is flaky
|
||||
fake_os: Fake utils.is_* to a fake operating system
|
||||
unicode_locale: Tests which need an unicode locale to work
|
||||
qtwebkit6021_skip: Tests which would fail on WebKit version 602.1
|
||||
flaky_once: Try to rerun this test once if it fails
|
||||
qt_log_level_fail = WARNING
|
||||
qt_log_ignore =
|
||||
^SpellCheck: .*
|
||||
^SetProcessDpiAwareness failed: .*
|
||||
^QWindowsWindow::setGeometry(Dp)?: Unable to set geometry .*
|
||||
^QWindowsWindow::setGeometryDp: Unable to set geometry .*
|
||||
^QProcess: Destroyed while process .* is still running\.
|
||||
^"Method "GetAll" with signature "s" on interface "org\.freedesktop\.DBus\.Properties" doesn't exist
|
||||
^"Method \\"GetAll\\" with signature \\"s\\" on interface \\"org\.freedesktop\.DBus\.Properties\\" doesn't exist\\n"
|
||||
^propsReply "Method \\"GetAll\\" with signature \\"s\\" on interface \\"org\.freedesktop\.DBus\.Properties\\" doesn't exist\\n"
|
||||
^nmReply "Method \\"GetDevices\\" with signature \\"\\" on interface \\"org\.freedesktop\.NetworkManager\\" doesn't exist\\n"
|
||||
^"Object path cannot be empty"
|
||||
^virtual void QSslSocketBackendPrivate::transmit\(\) SSL write failed with error: -9805
|
||||
^virtual void QSslSocketBackendPrivate::transmit\(\) SSLRead failed with: -9805
|
||||
^Type conversion already registered from type .*
|
||||
@@ -49,22 +30,11 @@ qt_log_ignore =
|
||||
^QWaitCondition: Destroyed while threads are still waiting
|
||||
^QXcbXSettings::QXcbXSettings\(QXcbScreen\*\) Failed to get selection owner for XSETTINGS_S atom
|
||||
^QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to .*
|
||||
^QXcbClipboard: SelectionRequest too old
|
||||
^QGeoclueMaster error creating GeoclueMasterClient\.
|
||||
^Geoclue error: Process org\.freedesktop\.Geoclue\.Master exited with status 127
|
||||
^QObject::connect: Cannot connect \(null\)::stateChanged\(QNetworkSession::State\) to QNetworkReplyHttpImpl::_q_networkSessionStateChanged\(QNetworkSession::State\)
|
||||
^QXcbClipboard: Cannot transfer data, no data available
|
||||
^load glyph failed
|
||||
^Error when parsing the netrc file
|
||||
^Image of format '' blocked because it is not considered safe. If you are sure it is safe to do so, you can white-list the format by setting the environment variable QTWEBKIT_IMAGEFORMAT_WHITELIST=
|
||||
^QPainter::end: Painter ended with \d+ saved states
|
||||
^QSslSocket: cannot resolve .*
|
||||
^QSslSocket: cannot call unresolved function .*
|
||||
^Incompatible version of OpenSSL
|
||||
^QQuickWidget::invalidateRenderControl could not make context current
|
||||
^libpng warning: iCCP: known incorrect sRGB profile
|
||||
^inotify_add_watch\(".*"\) failed: "No space left on device"
|
||||
^QSettings::value: Empty key passed
|
||||
^Icon theme ".*" not found
|
||||
^Error receiving trust for a CA certificate
|
||||
qt_wait_signal_raising = true
|
||||
xfail_strict = true
|
||||
filterwarnings =
|
||||
# This happens in many qutebrowser dependencies...
|
||||
ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working:DeprecationWarning
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
[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
|
||||
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;
|
||||
@@ -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-2015 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-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -17,17 +17,19 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""A keyboard-driven, vim-like browser based on PyQt5."""
|
||||
# pylint: disable=line-too-long
|
||||
|
||||
"""A keyboard-driven, vim-like browser based on PyQt5 and QtWebKit."""
|
||||
|
||||
import os.path
|
||||
|
||||
__author__ = "Florian Bruhin"
|
||||
__copyright__ = "Copyright 2014-2018 Florian Bruhin (The Compiler)"
|
||||
__copyright__ = "Copyright 2014-2016 Florian Bruhin (The Compiler)"
|
||||
__license__ = "GPL"
|
||||
__maintainer__ = __author__
|
||||
__email__ = "mail@qutebrowser.org"
|
||||
__version_info__ = (1, 4, 0)
|
||||
__version_info__ = (0, 8, 1)
|
||||
__version__ = '.'.join(str(e) for e in __version_info__)
|
||||
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."
|
||||
__description__ = "A keyboard-driven, vim-like browser based on PyQt5 and QtWebKit."
|
||||
|
||||
basedir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
@@ -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-2016 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-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -17,29 +17,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Initialization of qutebrowser and application-wide things.
|
||||
|
||||
The run() function will get called once early initialization (in
|
||||
qutebrowser.py/earlyinit.py) is done. See the qutebrowser.py docstring for
|
||||
details about early initialization.
|
||||
|
||||
As we need to access the config before the QApplication is created, we
|
||||
initialize everything the config needs before the QApplication is created, and
|
||||
then leave it in a partially initialized state (no saving, no config errors
|
||||
shown yet).
|
||||
|
||||
We then set up the QApplication object and initialize a few more low-level
|
||||
things.
|
||||
|
||||
After that, init() and _init_modules() take over and initialize the rest.
|
||||
|
||||
After all initialization is done, the qt_mainloop() function is called, which
|
||||
blocks and spins the Qt mainloop.
|
||||
"""
|
||||
"""Initialization of qutebrowser and application-wide things."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import configparser
|
||||
import functools
|
||||
import json
|
||||
import shutil
|
||||
@@ -49,9 +32,10 @@ import datetime
|
||||
import tokenize
|
||||
|
||||
from PyQt5.QtWidgets import QApplication, QWidget
|
||||
from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon, QWindow
|
||||
from PyQt5.QtWebKit import QWebSettings
|
||||
from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon, QCursor, QWindow
|
||||
from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QTimer, QUrl,
|
||||
QObject, QEvent, pyqtSignal, Qt)
|
||||
QObject, Qt, QEvent, pyqtSignal)
|
||||
try:
|
||||
import hunter
|
||||
except ImportError:
|
||||
@@ -59,27 +43,19 @@ except ImportError:
|
||||
|
||||
import qutebrowser
|
||||
import qutebrowser.resources
|
||||
from qutebrowser.completion import completiondelegate
|
||||
from qutebrowser.completion.models import miscmodels
|
||||
from qutebrowser.completion.models import instances as completionmodels
|
||||
from qutebrowser.commands import cmdutils, runners, cmdexc
|
||||
from qutebrowser.config import config, websettings, configfiles, configinit
|
||||
from qutebrowser.browser import (urlmarks, adblock, 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.keyinput import macros
|
||||
from qutebrowser.mainwindow import mainwindow, prompt
|
||||
from qutebrowser.misc import (readline, ipc, savemanager, sessions,
|
||||
crashsignal, earlyinit, sql, cmdhistory,
|
||||
backendproblem)
|
||||
from qutebrowser.utils import (log, version, message, utils, urlutils, objreg,
|
||||
usertypes, standarddir, error)
|
||||
# pylint: disable=unused-import
|
||||
# We import those to run the cmdutils.register decorators.
|
||||
from qutebrowser.mainwindow.statusbar import command
|
||||
from qutebrowser.misc import utilcmds
|
||||
# pylint: enable=unused-import
|
||||
from qutebrowser.config import style, config, websettings, configexc
|
||||
from qutebrowser.browser import urlmarks, adblock
|
||||
from qutebrowser.browser.webkit import cookies, cache, history, downloads
|
||||
from qutebrowser.browser.webkit.network import (qutescheme, proxy,
|
||||
networkmanager)
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
from qutebrowser.misc import readline, ipc, savemanager, sessions, crashsignal
|
||||
from qutebrowser.misc import utilcmds # pylint: disable=unused-import
|
||||
from qutebrowser.utils import (log, version, message, utils, qtutils, urlutils,
|
||||
objreg, usertypes, standarddir, error, debug)
|
||||
# We import utilcmds to run the cmdutils.register decorators.
|
||||
|
||||
|
||||
qApp = None
|
||||
@@ -87,19 +63,16 @@ qApp = None
|
||||
|
||||
def run(args):
|
||||
"""Initialize everything and run the application."""
|
||||
if args.version:
|
||||
print(version.version())
|
||||
sys.exit(usertypes.Exit.ok)
|
||||
|
||||
if args.temp_basedir:
|
||||
args.basedir = tempfile.mkdtemp(prefix='qutebrowser-basedir-')
|
||||
|
||||
quitter = Quitter(args)
|
||||
objreg.register('quitter', quitter)
|
||||
|
||||
log.init.debug("Initializing directories...")
|
||||
standarddir.init(args)
|
||||
utils.preload_resources()
|
||||
|
||||
log.init.debug("Initializing config...")
|
||||
configinit.early_init(args)
|
||||
|
||||
global qApp
|
||||
qApp = Application(args)
|
||||
qApp.setOrganizationName("qutebrowser")
|
||||
@@ -107,10 +80,6 @@ def run(args):
|
||||
qApp.setApplicationVersion(qutebrowser.__version__)
|
||||
qApp.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)
|
||||
crash_handler.activate()
|
||||
@@ -159,30 +128,33 @@ def init(args, crash_handler):
|
||||
log.init.debug("Starting init...")
|
||||
qApp.setQuitOnLastWindowClosed(False)
|
||||
_init_icon()
|
||||
utils.actute_warning()
|
||||
|
||||
try:
|
||||
_init_modules(args, crash_handler)
|
||||
except (OSError, UnicodeDecodeError, browsertab.WebTabError) as e:
|
||||
except (OSError, UnicodeDecodeError) as e:
|
||||
error.handle_fatal_exc(e, args, "Error while initializing!",
|
||||
pre_text="Error while initializing")
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
|
||||
QTimer.singleShot(0, functools.partial(_process_args, args))
|
||||
QTimer.singleShot(10, functools.partial(_init_late_modules, args))
|
||||
|
||||
log.init.debug("Initializing eventfilter...")
|
||||
event_filter = EventFilter(qApp)
|
||||
qApp.installEventFilter(event_filter)
|
||||
objreg.register('event-filter', event_filter)
|
||||
|
||||
log.init.debug("Connecting signals...")
|
||||
config_obj = objreg.get('config')
|
||||
config_obj.style_changed.connect(style.get_stylesheet.cache_clear)
|
||||
qApp.focusChanged.connect(on_focus_changed)
|
||||
|
||||
_process_args(args)
|
||||
qApp.focusChanged.connect(message.on_focus_changed)
|
||||
|
||||
QDesktopServices.setUrlHandler('http', open_desktopservices_url)
|
||||
QDesktopServices.setUrlHandler('https', open_desktopservices_url)
|
||||
QDesktopServices.setUrlHandler('qute', open_desktopservices_url)
|
||||
|
||||
objreg.get('web-history').import_txt()
|
||||
|
||||
log.init.debug("Init done!")
|
||||
crash_handler.raise_crashdlg()
|
||||
|
||||
@@ -194,35 +166,37 @@ def _init_icon():
|
||||
for size in [16, 24, 32, 48, 64, 96, 128, 256, 512]:
|
||||
filename = ':/icons/qutebrowser-{}x{}.png'.format(size, size)
|
||||
pixmap = QPixmap(filename)
|
||||
if pixmap.isNull():
|
||||
log.init.warning("Failed to load {}".format(filename))
|
||||
else:
|
||||
fallback_icon.addPixmap(pixmap)
|
||||
qtutils.ensure_not_null(pixmap)
|
||||
fallback_icon.addPixmap(pixmap)
|
||||
qtutils.ensure_not_null(fallback_icon)
|
||||
icon = QIcon.fromTheme('qutebrowser', fallback_icon)
|
||||
if icon.isNull():
|
||||
log.init.warning("Failed to load icon")
|
||||
else:
|
||||
qApp.setWindowIcon(icon)
|
||||
qtutils.ensure_not_null(icon)
|
||||
qApp.setWindowIcon(icon)
|
||||
|
||||
|
||||
def _process_args(args):
|
||||
"""Open startpage etc. and process commandline args."""
|
||||
config_obj = objreg.get('config')
|
||||
for sect, opt, val in args.temp_settings:
|
||||
try:
|
||||
config_obj.set('temp', sect, opt, val)
|
||||
except (configexc.Error, configparser.Error) as e:
|
||||
message.error('current', "set: {} - {}".format(
|
||||
e.__class__.__name__, e))
|
||||
|
||||
if not args.override_restore:
|
||||
_load_session(args.session)
|
||||
session_manager = objreg.get('session-manager')
|
||||
if not session_manager.did_load:
|
||||
log.init.debug("Initializing main window...")
|
||||
window = mainwindow.MainWindow(private=None)
|
||||
window = mainwindow.MainWindow()
|
||||
if not args.nowindow:
|
||||
window.show()
|
||||
qApp.setActiveWindow(window)
|
||||
|
||||
process_pos_args(args.command)
|
||||
_open_startpage()
|
||||
_open_special_pages(args)
|
||||
|
||||
delta = datetime.datetime.now() - earlyinit.START_TIME
|
||||
log.init.debug("Init finished after {}s".format(delta.total_seconds()))
|
||||
_open_quickstart(args)
|
||||
|
||||
|
||||
def _load_session(name):
|
||||
@@ -231,25 +205,24 @@ def _load_session(name):
|
||||
Args:
|
||||
name: The name of the session to load, or None to read state file.
|
||||
"""
|
||||
session_manager = objreg.get('session-manager')
|
||||
if name is None and session_manager.exists('_autosave'):
|
||||
name = '_autosave'
|
||||
elif name is None:
|
||||
state_config = objreg.get('state-config')
|
||||
if name is None:
|
||||
try:
|
||||
name = configfiles.state['general']['session']
|
||||
name = state_config['general']['session']
|
||||
except KeyError:
|
||||
# No session given as argument and none in the session file ->
|
||||
# start without loading a session
|
||||
return
|
||||
|
||||
session_manager = objreg.get('session-manager')
|
||||
try:
|
||||
session_manager.load(name)
|
||||
except sessions.SessionNotFoundError:
|
||||
message.error("Session {} not found!".format(name))
|
||||
message.error('current', "Session {} not found!".format(name))
|
||||
except sessions.SessionError as e:
|
||||
message.error("Failed to load session {}: {}".format(name, e))
|
||||
message.error('current', "Failed to load session {}: {}".format(
|
||||
name, e))
|
||||
try:
|
||||
del configfiles.state['general']['session']
|
||||
del state_config['general']['session']
|
||||
except KeyError:
|
||||
pass
|
||||
# If this was a _restart session, delete it.
|
||||
@@ -281,7 +254,7 @@ def process_pos_args(args, via_ipc=False, cwd=None, target_arg=None):
|
||||
win_id = mainwindow.get_window(via_ipc, force_tab=True)
|
||||
log.init.debug("Startup cmd {!r}".format(cmd))
|
||||
commandrunner = runners.CommandRunner(win_id)
|
||||
commandrunner.run_safely(cmd[1:])
|
||||
commandrunner.run_safely_init(cmd[1:])
|
||||
elif not cmd:
|
||||
log.init.debug("Empty argument")
|
||||
win_id = mainwindow.get_window(via_ipc, force_window=True)
|
||||
@@ -289,39 +262,22 @@ def process_pos_args(args, via_ipc=False, cwd=None, target_arg=None):
|
||||
if via_ipc and target_arg and target_arg != 'auto':
|
||||
open_target = target_arg
|
||||
else:
|
||||
open_target = None
|
||||
open_target = config.get('general', 'new-instance-open-target')
|
||||
win_id = mainwindow.get_window(via_ipc, force_target=open_target)
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=win_id)
|
||||
log.init.debug("Startup URL {}".format(cmd))
|
||||
if not cwd: # could also be an empty string due to the PyQt signal
|
||||
cwd = None
|
||||
try:
|
||||
url = urlutils.fuzzy_url(cmd, cwd, relative=True)
|
||||
except urlutils.InvalidUrlError as e:
|
||||
message.error("Error in startup argument '{}': {}".format(
|
||||
cmd, e))
|
||||
message.error('current', "Error in startup argument '{}': "
|
||||
"{}".format(cmd, e))
|
||||
else:
|
||||
win_id = open_url(url, target=open_target, via_ipc=via_ipc)
|
||||
|
||||
|
||||
def open_url(url, target=None, no_raise=False, via_ipc=True):
|
||||
"""Open an URL in new window/tab.
|
||||
|
||||
Args:
|
||||
url: An URL to open.
|
||||
target: same as new_instance_open_target (used as a default).
|
||||
no_raise: suppress target window raising.
|
||||
via_ipc: Whether the arguments were transmitted over IPC.
|
||||
|
||||
Return:
|
||||
ID of a window that was used to open URL
|
||||
"""
|
||||
target = target or config.val.new_instance_open_target
|
||||
background = target in {'tab-bg', 'tab-bg-silent'}
|
||||
win_id = mainwindow.get_window(via_ipc, force_target=target,
|
||||
no_raise=no_raise)
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=win_id)
|
||||
log.init.debug("About to open URL: {}".format(url.toDisplayString()))
|
||||
tabbed_browser.tabopen(url, background=background, related=False)
|
||||
return win_id
|
||||
background = open_target in ['tab-bg', 'tab-bg-silent']
|
||||
tabbed_browser.tabopen(url, background=background,
|
||||
explicit=True)
|
||||
|
||||
|
||||
def _open_startpage(win_id=None):
|
||||
@@ -337,70 +293,65 @@ def _open_startpage(win_id=None):
|
||||
window_ids = [win_id]
|
||||
else:
|
||||
window_ids = objreg.window_registry
|
||||
for cur_win_id in list(window_ids): # Copying as the dict could change
|
||||
for cur_win_id in window_ids:
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=cur_win_id)
|
||||
if tabbed_browser.widget.count() == 0:
|
||||
log.init.debug("Opening start pages")
|
||||
for url in config.val.url.start_pages:
|
||||
tabbed_browser.tabopen(url)
|
||||
if tabbed_browser.count() == 0:
|
||||
log.init.debug("Opening startpage")
|
||||
for urlstr in config.get('general', 'startpage'):
|
||||
try:
|
||||
url = urlutils.fuzzy_url(urlstr, do_search=False)
|
||||
except urlutils.InvalidUrlError as e:
|
||||
message.error('current', "Error when opening startpage: "
|
||||
"{}".format(e))
|
||||
tabbed_browser.tabopen(QUrl('about:blank'))
|
||||
else:
|
||||
tabbed_browser.tabopen(url)
|
||||
|
||||
|
||||
def _open_special_pages(args):
|
||||
"""Open special notification pages which are only shown once.
|
||||
|
||||
Currently this is:
|
||||
- Quickstart page if it's the first start.
|
||||
- Legacy QtWebKit warning if needed.
|
||||
def _open_quickstart(args):
|
||||
"""Open quickstart if it's the first start.
|
||||
|
||||
Args:
|
||||
args: The argparse namespace.
|
||||
"""
|
||||
if args.basedir is not None:
|
||||
# With --basedir given, don't open anything.
|
||||
if args.datadir is not None or args.basedir is not None:
|
||||
# With --datadir or --basedir given, don't open quickstart.
|
||||
return
|
||||
|
||||
general_sect = configfiles.state['general']
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window='last-focused')
|
||||
|
||||
# Quickstart page
|
||||
|
||||
quickstart_done = general_sect.get('quickstart-done') == '1'
|
||||
|
||||
state_config = objreg.get('state-config')
|
||||
try:
|
||||
quickstart_done = state_config['general']['quickstart-done'] == '1'
|
||||
except KeyError:
|
||||
quickstart_done = False
|
||||
if not quickstart_done:
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window='last-focused')
|
||||
tabbed_browser.tabopen(
|
||||
QUrl('https://www.qutebrowser.org/quickstart.html'))
|
||||
general_sect['quickstart-done'] = '1'
|
||||
QUrl('http://www.qutebrowser.org/quickstart.html'))
|
||||
state_config['general']['quickstart-done'] = '1'
|
||||
|
||||
# Setting migration page
|
||||
|
||||
needs_migration = os.path.exists(
|
||||
os.path.join(standarddir.config(), 'qutebrowser.conf'))
|
||||
migration_shown = general_sect.get('config-migration-shown') == '1'
|
||||
|
||||
if needs_migration and not migration_shown:
|
||||
tabbed_browser.tabopen(QUrl('qute://help/configuring.html'),
|
||||
background=False)
|
||||
general_sect['config-migration-shown'] = '1'
|
||||
def _save_version():
|
||||
"""Save the current version to the state config."""
|
||||
state_config = objreg.get('state-config')
|
||||
state_config['general']['version'] = qutebrowser.__version__
|
||||
|
||||
|
||||
def on_focus_changed(_old, new):
|
||||
"""Register currently focused main window in the object registry."""
|
||||
if new is None:
|
||||
return
|
||||
|
||||
if not isinstance(new, QWidget):
|
||||
log.misc.debug("on_focus_changed called with non-QWidget {!r}".format(
|
||||
new))
|
||||
return
|
||||
|
||||
window = new.window()
|
||||
if isinstance(window, mainwindow.MainWindow):
|
||||
objreg.register('last-focused-main-window', window, update=True)
|
||||
# A focused window must also be visible, and in this case we should
|
||||
# consider it as the most recently looked-at window
|
||||
objreg.register('last-visible-main-window', window, update=True)
|
||||
if new is None or not isinstance(new, mainwindow.MainWindow):
|
||||
try:
|
||||
objreg.delete('last-focused-main-window')
|
||||
except KeyError:
|
||||
pass
|
||||
qApp.restoreOverrideCursor()
|
||||
else:
|
||||
objreg.register('last-focused-main-window', new.window(), update=True)
|
||||
_maybe_hide_mouse_cursor()
|
||||
|
||||
|
||||
def open_desktopservices_url(url):
|
||||
@@ -411,6 +362,17 @@ def open_desktopservices_url(url):
|
||||
tabbed_browser.tabopen(url)
|
||||
|
||||
|
||||
@config.change_filter('ui', 'hide-mouse-cursor', function=True)
|
||||
def _maybe_hide_mouse_cursor():
|
||||
"""Hide the mouse cursor if it isn't yet and it's configured."""
|
||||
if config.get('ui', 'hide-mouse-cursor'):
|
||||
if qApp.overrideCursor() is not None:
|
||||
return
|
||||
qApp.setOverrideCursor(QCursor(Qt.BlankCursor))
|
||||
else:
|
||||
qApp.restoreOverrideCursor()
|
||||
|
||||
|
||||
def _init_modules(args, crash_handler):
|
||||
"""Initialize all 'modules' which need to be initialized.
|
||||
|
||||
@@ -418,91 +380,81 @@ def _init_modules(args, crash_handler):
|
||||
args: The argparse namespace.
|
||||
crash_handler: The CrashHandler instance.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
log.init.debug("Initializing save manager...")
|
||||
save_manager = savemanager.SaveManager(qApp)
|
||||
objreg.register('save-manager', save_manager)
|
||||
configinit.late_init(save_manager)
|
||||
|
||||
log.init.debug("Checking backend requirements...")
|
||||
backendproblem.init()
|
||||
|
||||
log.init.debug("Initializing prompts...")
|
||||
prompt.init()
|
||||
|
||||
save_manager.add_saveable('version', _save_version)
|
||||
log.init.debug("Initializing network...")
|
||||
networkmanager.init()
|
||||
|
||||
log.init.debug("Initializing proxy...")
|
||||
proxy.init()
|
||||
|
||||
log.init.debug("Initializing readline-bridge...")
|
||||
readline_bridge = readline.ReadlineBridge()
|
||||
objreg.register('readline-bridge', readline_bridge)
|
||||
|
||||
try:
|
||||
log.init.debug("Initializing sql...")
|
||||
sql.init(os.path.join(standarddir.data(), 'history.sqlite'))
|
||||
|
||||
log.init.debug("Initializing web history...")
|
||||
history.init(qApp)
|
||||
except sql.SqlError as e:
|
||||
if e.environmental:
|
||||
error.handle_fatal_exc(e, args, 'Error initializing SQL',
|
||||
pre_text='Error initializing SQL')
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
else:
|
||||
raise
|
||||
|
||||
log.init.debug("Initializing completion...")
|
||||
completiondelegate.init()
|
||||
|
||||
log.init.debug("Initializing command history...")
|
||||
cmdhistory.init()
|
||||
|
||||
log.init.debug("Initializing directories...")
|
||||
standarddir.init(args)
|
||||
log.init.debug("Initializing config...")
|
||||
config.init(qApp)
|
||||
save_manager.init_autosave()
|
||||
log.init.debug("Initializing web history...")
|
||||
history.init(qApp)
|
||||
log.init.debug("Initializing crashlog...")
|
||||
if not args.no_err_windows:
|
||||
crash_handler.handle_segfault()
|
||||
|
||||
log.init.debug("Initializing sessions...")
|
||||
sessions.init(qApp)
|
||||
|
||||
log.init.debug("Initializing js-bridge...")
|
||||
js_bridge = qutescheme.JSBridge(qApp)
|
||||
objreg.register('js-bridge', js_bridge)
|
||||
log.init.debug("Initializing websettings...")
|
||||
websettings.init(args)
|
||||
|
||||
websettings.init()
|
||||
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)
|
||||
objreg.register('quickmark-manager', quickmark_manager)
|
||||
|
||||
log.init.debug("Initializing bookmarks...")
|
||||
bookmark_manager = urlmarks.BookmarkManager(qApp)
|
||||
objreg.register('bookmark-manager', bookmark_manager)
|
||||
|
||||
log.init.debug("Initializing proxy...")
|
||||
proxy.init()
|
||||
log.init.debug("Initializing cookies...")
|
||||
cookie_jar = cookies.CookieJar(qApp)
|
||||
ram_cookie_jar = cookies.RAMCookieJar(qApp)
|
||||
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)
|
||||
objreg.register('cache', diskcache)
|
||||
|
||||
log.init.debug("Initializing downloads...")
|
||||
download_manager = qtnetworkdownloads.DownloadManager(parent=qApp)
|
||||
objreg.register('qtnetwork-download-manager', download_manager)
|
||||
|
||||
log.init.debug("Initializing Greasemonkey...")
|
||||
greasemonkey.init()
|
||||
|
||||
log.init.debug("Initializing completions...")
|
||||
completionmodels.init()
|
||||
log.init.debug("Misc initialization...")
|
||||
macros.init()
|
||||
# Init backend-specific stuff
|
||||
browsertab.init()
|
||||
if config.get('ui', 'hide-wayland-decoration'):
|
||||
os.environ['QT_WAYLAND_DISABLE_WINDOWDECORATION'] = '1'
|
||||
else:
|
||||
os.environ.pop('QT_WAYLAND_DISABLE_WINDOWDECORATION', None)
|
||||
_maybe_hide_mouse_cursor()
|
||||
objreg.get('config').changed.connect(_maybe_hide_mouse_cursor)
|
||||
temp_downloads = downloads.TempDownloadManager(qApp)
|
||||
objreg.register('temporary-downloads', temp_downloads)
|
||||
|
||||
|
||||
def _init_late_modules(args):
|
||||
"""Initialize modules which can be inited after the window is shown."""
|
||||
log.init.debug("Reading web history...")
|
||||
reader = objreg.get('web-history').async_read()
|
||||
with debug.log_time(log.init, 'Reading history'):
|
||||
while True:
|
||||
QApplication.processEvents()
|
||||
try:
|
||||
next(reader)
|
||||
except StopIteration:
|
||||
break
|
||||
except (OSError, UnicodeDecodeError) as e:
|
||||
error.handle_fatal_exc(e, args, "Error while initializing!",
|
||||
pre_text="Error while initializing")
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
|
||||
|
||||
class Quitter:
|
||||
@@ -547,13 +499,12 @@ class Quitter:
|
||||
with tokenize.open(os.path.join(dirpath, fn)) as f:
|
||||
compile(f.read(), fn, 'exec')
|
||||
|
||||
def _get_restart_args(self, pages=(), session=None, override_args=None):
|
||||
def _get_restart_args(self, pages=(), session=None):
|
||||
"""Get the current working directory and args to relaunch qutebrowser.
|
||||
|
||||
Args:
|
||||
pages: The pages to re-open.
|
||||
session: The session to load, or None.
|
||||
override_args: Argument overrides as a dict.
|
||||
|
||||
Return:
|
||||
An (args, cwd) tuple.
|
||||
@@ -569,12 +520,12 @@ class Quitter:
|
||||
cwd = os.path.abspath(os.path.dirname(sys.executable))
|
||||
else:
|
||||
args = [sys.executable, '-m', 'qutebrowser']
|
||||
cwd = os.path.join(
|
||||
os.path.abspath(os.path.dirname(qutebrowser.__file__)), '..')
|
||||
cwd = os.path.join(os.path.abspath(os.path.dirname(
|
||||
qutebrowser.__file__)), '..')
|
||||
if not os.path.isdir(cwd):
|
||||
# Probably running from a python egg. Let's fallback to
|
||||
# cwd=None and see if that works out.
|
||||
# See https://github.com/qutebrowser/qutebrowser/issues/323
|
||||
# See https://github.com/The-Compiler/qutebrowser/issues/323
|
||||
cwd = None
|
||||
|
||||
# Add all open pages so they get reopened.
|
||||
@@ -600,12 +551,8 @@ class Quitter:
|
||||
argdict['session'] = session
|
||||
argdict['override_restore'] = False
|
||||
# Ensure :restart works with --temp-basedir
|
||||
if self._args.temp_basedir:
|
||||
argdict['temp_basedir'] = False
|
||||
argdict['temp_basedir_restarted'] = True
|
||||
|
||||
if override_args is not None:
|
||||
argdict.update(override_args)
|
||||
argdict['temp_basedir'] = False
|
||||
argdict['temp_basedir_restarted'] = True
|
||||
|
||||
# Dump the data
|
||||
data = json.dumps(argdict)
|
||||
@@ -631,7 +578,7 @@ class Quitter:
|
||||
if ok:
|
||||
self.shutdown(restart=True)
|
||||
|
||||
def restart(self, pages=(), session=None, override_args=None):
|
||||
def restart(self, pages=(), session=None):
|
||||
"""Inner logic to restart qutebrowser.
|
||||
|
||||
The "better" way to restart is to pass a session (_restart usually) as
|
||||
@@ -644,7 +591,6 @@ class Quitter:
|
||||
Args:
|
||||
pages: A list of URLs to open.
|
||||
session: The session to load, or None.
|
||||
override_args: Argument overrides as a dict.
|
||||
|
||||
Return:
|
||||
True if the restart succeeded, False otherwise.
|
||||
@@ -654,19 +600,13 @@ class Quitter:
|
||||
log.destroy.debug("sys.path: {}".format(sys.path))
|
||||
log.destroy.debug("sys.argv: {}".format(sys.argv))
|
||||
log.destroy.debug("frozen: {}".format(hasattr(sys, 'frozen')))
|
||||
|
||||
# Save the session if one is given.
|
||||
if session is not None:
|
||||
session_manager = objreg.get('session-manager')
|
||||
session_manager.save(session, with_private=True)
|
||||
|
||||
# Make sure we're not accepting a connection from the new process
|
||||
# before we fully exited.
|
||||
ipc.server.shutdown()
|
||||
|
||||
session_manager.save(session)
|
||||
# Open a new process and immediately shutdown the existing one
|
||||
try:
|
||||
args, cwd = self._get_restart_args(pages, session, override_args)
|
||||
args, cwd = self._get_restart_args(pages, session)
|
||||
if cwd is None:
|
||||
subprocess.Popen(args)
|
||||
else:
|
||||
@@ -677,24 +617,8 @@ class Quitter:
|
||||
else:
|
||||
return True
|
||||
|
||||
@cmdutils.register(instance='quitter', name='quit')
|
||||
@cmdutils.argument('session', completion=miscmodels.session)
|
||||
def quit(self, save=False, session=None):
|
||||
"""Quit qutebrowser.
|
||||
Args:
|
||||
save: When given, save the open windows even if auto_save.session
|
||||
is turned off.
|
||||
session: The name of the session to save.
|
||||
"""
|
||||
if session is not None and not save:
|
||||
raise cmdexc.CommandError("Session name given without --save!")
|
||||
if save:
|
||||
if session is None:
|
||||
session = sessions.default
|
||||
self.shutdown(session=session)
|
||||
else:
|
||||
self.shutdown()
|
||||
|
||||
@cmdutils.register(instance='quitter', name=['quit', 'q'],
|
||||
ignore_args=True)
|
||||
def shutdown(self, status=0, session=None, last_window=False,
|
||||
restart=False):
|
||||
"""Quit qutebrowser.
|
||||
@@ -711,16 +635,22 @@ class Quitter:
|
||||
self._shutting_down = True
|
||||
log.destroy.debug("Shutting down with status {}, session {}...".format(
|
||||
status, session))
|
||||
session_manager = objreg.get('session-manager', None)
|
||||
if session_manager is not None:
|
||||
if session is not None:
|
||||
session_manager.save(session, last_window=last_window,
|
||||
load_next_time=True)
|
||||
elif config.val.auto_save.session:
|
||||
session_manager.save(sessions.default, last_window=last_window,
|
||||
load_next_time=True)
|
||||
|
||||
if prompt.prompt_queue.shutdown():
|
||||
session_manager = objreg.get('session-manager')
|
||||
if session is not None:
|
||||
session_manager.save(session, last_window=last_window,
|
||||
load_next_time=True)
|
||||
elif config.get('general', 'save-session'):
|
||||
session_manager.save(sessions.default, last_window=last_window,
|
||||
load_next_time=True)
|
||||
|
||||
deferrer = False
|
||||
for win_id in objreg.window_registry:
|
||||
prompter = objreg.get('prompter', None, scope='window',
|
||||
window=win_id)
|
||||
if prompter is not None and prompter.shutdown():
|
||||
deferrer = True
|
||||
if deferrer:
|
||||
# If shutdown was called while we were asking a question, we're in
|
||||
# a still sub-eventloop (which gets quit now) and not in the main
|
||||
# one.
|
||||
@@ -735,7 +665,7 @@ class Quitter:
|
||||
# event loop, so we can shut down immediately.
|
||||
self._shutdown(status, restart=restart)
|
||||
|
||||
def _shutdown(self, status, restart): # noqa
|
||||
def _shutdown(self, status, restart):
|
||||
"""Second stage of shutdown."""
|
||||
log.destroy.debug("Stage 2 of shutting down...")
|
||||
if qApp is None:
|
||||
@@ -744,16 +674,14 @@ class Quitter:
|
||||
# Remove eventfilter
|
||||
try:
|
||||
log.destroy.debug("Removing eventfilter...")
|
||||
event_filter = objreg.get('event-filter', None)
|
||||
if event_filter is not None:
|
||||
qApp.removeEventFilter(event_filter)
|
||||
qApp.removeEventFilter(objreg.get('event-filter'))
|
||||
except AttributeError:
|
||||
pass
|
||||
# Close all windows
|
||||
QApplication.closeAllWindows()
|
||||
# Shut down IPC
|
||||
try:
|
||||
ipc.server.shutdown()
|
||||
objreg.get('ipc-server').shutdown()
|
||||
except KeyError:
|
||||
pass
|
||||
# Save everything
|
||||
@@ -771,9 +699,9 @@ class Quitter:
|
||||
e, self._args, "Error while saving!",
|
||||
pre_text="Error while saving {}".format(key))
|
||||
# Disable storage so removing tempdir will work
|
||||
websettings.shutdown()
|
||||
# Disable application proxy factory to fix segfaults with Qt 5.10.1
|
||||
proxy.shutdown()
|
||||
QWebSettings.setIconDatabasePath('')
|
||||
QWebSettings.setOfflineWebApplicationCachePath('')
|
||||
QWebSettings.globalSettings().setLocalStoragePath('')
|
||||
# Re-enable faulthandler to stdout, then remove crash log
|
||||
log.destroy.debug("Deactivating crash log...")
|
||||
objreg.get('crash-handler').destroy_crashlogfile()
|
||||
@@ -783,20 +711,27 @@ class Quitter:
|
||||
atexit.register(shutil.rmtree, self._args.basedir,
|
||||
ignore_errors=True)
|
||||
# Delete temp download dir
|
||||
downloads.temp_download_manager.cleanup()
|
||||
objreg.get('temporary-downloads').cleanup()
|
||||
# If we don't kill our custom handler here we might get segfaults
|
||||
log.destroy.debug("Deactivating message handler...")
|
||||
qInstallMessageHandler(None)
|
||||
# Now we can hopefully quit without segfaults
|
||||
log.destroy.debug("Deferring QApplication::exit...")
|
||||
objreg.get('signal-handler').deactivate()
|
||||
session_manager = objreg.get('session-manager', None)
|
||||
if session_manager is not None:
|
||||
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))
|
||||
|
||||
@cmdutils.register(instance='quitter', name='wq')
|
||||
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
|
||||
def save_and_quit(self, name=sessions.default):
|
||||
"""Save open pages and quit.
|
||||
|
||||
Args:
|
||||
name: The name of the session.
|
||||
"""
|
||||
self.shutdown(session=name)
|
||||
|
||||
|
||||
class Application(QApplication):
|
||||
|
||||
@@ -804,7 +739,6 @@ class Application(QApplication):
|
||||
|
||||
Attributes:
|
||||
_args: ArgumentParser instance.
|
||||
_last_focus_object: The last focused object's repr.
|
||||
"""
|
||||
|
||||
new_window = pyqtSignal(mainwindow.MainWindow)
|
||||
@@ -815,9 +749,7 @@ class Application(QApplication):
|
||||
Args:
|
||||
Argument namespace from argparse.
|
||||
"""
|
||||
self._last_focus_object = None
|
||||
|
||||
qt_args = configinit.qt_args(args)
|
||||
qt_args = qtutils.get_args(args)
|
||||
log.init.debug("Qt arguments: {}, based on {}".format(qt_args, args))
|
||||
super().__init__(qt_args)
|
||||
|
||||
@@ -829,28 +761,11 @@ class Application(QApplication):
|
||||
|
||||
self.launch_time = datetime.datetime.now()
|
||||
self.focusObjectChanged.connect(self.on_focus_object_changed)
|
||||
self.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def on_focus_object_changed(self, obj):
|
||||
"""Log when the focus object changed."""
|
||||
output = repr(obj)
|
||||
if self._last_focus_object != output:
|
||||
log.misc.debug("Focus object changed: {}".format(output))
|
||||
self._last_focus_object = output
|
||||
|
||||
def event(self, e):
|
||||
"""Handle macOS FileOpen events."""
|
||||
if e.type() == QEvent.FileOpen:
|
||||
url = e.url()
|
||||
if url.isValid():
|
||||
open_url(url, no_raise=True)
|
||||
else:
|
||||
message.error("Invalid URL: {}".format(url.errorString()))
|
||||
else:
|
||||
return super().event(e)
|
||||
|
||||
return True
|
||||
log.misc.debug("Focus object changed: {!r}".format(obj))
|
||||
|
||||
def __repr__(self):
|
||||
return utils.get_repr(self)
|
||||
@@ -858,7 +773,7 @@ class Application(QApplication):
|
||||
def exit(self, status):
|
||||
"""Extend QApplication::exit to log the event."""
|
||||
log.destroy.debug("Now calling QApplication::exit.")
|
||||
if 'debug-exit' in self._args.debug_flags:
|
||||
if self._args.debug_exit:
|
||||
if hunter is None:
|
||||
print("Not logging late shutdown because hunter could not be "
|
||||
"imported!", file=sys.stderr)
|
||||
@@ -882,9 +797,12 @@ class EventFilter(QObject):
|
||||
super().__init__(parent)
|
||||
self._activated = True
|
||||
self._handlers = {
|
||||
QEvent.MouseButtonDblClick: self._handle_mouse_event,
|
||||
QEvent.MouseButtonPress: self._handle_mouse_event,
|
||||
QEvent.MouseButtonRelease: self._handle_mouse_event,
|
||||
QEvent.MouseMove: self._handle_mouse_event,
|
||||
QEvent.KeyPress: self._handle_key_event,
|
||||
QEvent.KeyRelease: self._handle_key_event,
|
||||
QEvent.ShortcutOverride: self._handle_key_event,
|
||||
}
|
||||
|
||||
def _handle_key_event(self, event):
|
||||
@@ -902,11 +820,24 @@ class EventFilter(QObject):
|
||||
return False
|
||||
try:
|
||||
man = objreg.get('mode-manager', scope='window', window='current')
|
||||
return man.handle_event(event)
|
||||
return man.eventFilter(event)
|
||||
except objreg.RegistryUnavailableError:
|
||||
# No window available yet, or not a MainWindow
|
||||
return False
|
||||
|
||||
def _handle_mouse_event(self, _event):
|
||||
"""Handle a mouse event.
|
||||
|
||||
Args:
|
||||
_event: The QEvent which is about to be delivered.
|
||||
|
||||
Return:
|
||||
True if the event should be filtered, False if it's passed through.
|
||||
"""
|
||||
# Mouse cursor shown (overrideCursor None) -> don't filter event
|
||||
# Mouse cursor hidden (overrideCursor not None) -> filter event
|
||||
return qApp.overrideCursor() is not None
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
"""Handle an event.
|
||||
|
||||
|
||||
@@ -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 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-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -26,10 +26,9 @@ import posixpath
|
||||
import zipfile
|
||||
import fnmatch
|
||||
|
||||
from qutebrowser.browser import downloads
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import objreg, standarddir, log, message
|
||||
from qutebrowser.commands import cmdutils
|
||||
from qutebrowser.utils import objreg, standarddir, log, message, usertypes
|
||||
from qutebrowser.commands import cmdutils, cmdexc
|
||||
|
||||
|
||||
def guess_zip_filename(zf):
|
||||
@@ -58,7 +57,7 @@ def get_fileobj(byte_io):
|
||||
byte_io = zf.open(filename, mode='r')
|
||||
else:
|
||||
byte_io.seek(0) # rewind what zipfile.is_zipfile did
|
||||
return byte_io
|
||||
return io.TextIOWrapper(byte_io, encoding='utf-8')
|
||||
|
||||
|
||||
def is_whitelisted_host(host):
|
||||
@@ -67,7 +66,11 @@ def is_whitelisted_host(host):
|
||||
Args:
|
||||
host: The host of the request as string.
|
||||
"""
|
||||
for pattern in config.val.content.host_blocking.whitelist:
|
||||
whitelist = config.get('content', 'host-blocking-whitelist')
|
||||
if whitelist is None:
|
||||
return False
|
||||
|
||||
for pattern in whitelist:
|
||||
if fnmatch.fnmatch(host, pattern.lower()):
|
||||
return True
|
||||
return False
|
||||
@@ -94,8 +97,14 @@ class HostBlocker:
|
||||
_done_count: How many files have been read successfully.
|
||||
_local_hosts_file: The path to the blocked-hosts file.
|
||||
_config_hosts_file: The path to a blocked-hosts in ~/.config
|
||||
|
||||
Class attributes:
|
||||
WHITELISTED: Hosts which never should be blocked.
|
||||
"""
|
||||
|
||||
WHITELISTED = ('localhost', 'localhost.localdomain', 'broadcasthost',
|
||||
'local')
|
||||
|
||||
def __init__(self):
|
||||
self._blocked_hosts = set()
|
||||
self._config_blocked_hosts = set()
|
||||
@@ -103,17 +112,23 @@ class HostBlocker:
|
||||
self._done_count = 0
|
||||
|
||||
data_dir = standarddir.data()
|
||||
self._local_hosts_file = os.path.join(data_dir, 'blocked-hosts')
|
||||
self._update_files()
|
||||
if data_dir is None:
|
||||
self._local_hosts_file = None
|
||||
else:
|
||||
self._local_hosts_file = os.path.join(data_dir, 'blocked-hosts')
|
||||
self.on_config_changed()
|
||||
|
||||
config_dir = standarddir.config()
|
||||
self._config_hosts_file = os.path.join(config_dir, 'blocked-hosts')
|
||||
if config_dir is None:
|
||||
self._config_hosts_file = None
|
||||
else:
|
||||
self._config_hosts_file = os.path.join(config_dir, 'blocked-hosts')
|
||||
|
||||
config.instance.changed.connect(self._update_files)
|
||||
objreg.get('config').changed.connect(self.on_config_changed)
|
||||
|
||||
def is_blocked(self, url):
|
||||
"""Check if the given URL (as QUrl) is blocked."""
|
||||
if not config.val.content.host_blocking.enabled:
|
||||
if not config.get('content', 'host-blocking-enabled'):
|
||||
return False
|
||||
host = url.host()
|
||||
return ((host in self._blocked_hosts or
|
||||
@@ -130,14 +145,14 @@ class HostBlocker:
|
||||
Return:
|
||||
True if a read was attempted, False otherwise
|
||||
"""
|
||||
if not os.path.exists(filename):
|
||||
if filename is None or not os.path.exists(filename):
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
target.add(line.strip())
|
||||
except (OSError, UnicodeDecodeError):
|
||||
except OSError:
|
||||
log.misc.exception("Failed to read host blocklist!")
|
||||
|
||||
return True
|
||||
@@ -146,6 +161,9 @@ class HostBlocker:
|
||||
"""Read hosts from the existing blocked-hosts file."""
|
||||
self._blocked_hosts = set()
|
||||
|
||||
if self._local_hosts_file is None:
|
||||
return
|
||||
|
||||
self._read_hosts_file(self._config_hosts_file,
|
||||
self._config_blocked_hosts)
|
||||
|
||||
@@ -154,31 +172,37 @@ class HostBlocker:
|
||||
|
||||
if not found:
|
||||
args = objreg.get('args')
|
||||
if (config.val.content.host_blocking.lists and
|
||||
args.basedir is None and
|
||||
config.val.content.host_blocking.enabled):
|
||||
message.info("Run :adblock-update to get adblock lists.")
|
||||
if (config.get('content', 'host-block-lists') is not None and
|
||||
args.basedir is None):
|
||||
message.info('current',
|
||||
"Run :adblock-update to get adblock lists.")
|
||||
|
||||
@cmdutils.register(instance='host-blocker')
|
||||
def adblock_update(self):
|
||||
@cmdutils.argument('win_id', win_id=True)
|
||||
def adblock_update(self, win_id):
|
||||
"""Update the adblock block lists.
|
||||
|
||||
This updates `~/.local/share/qutebrowser/blocked-hosts` with downloaded
|
||||
host lists and re-reads `~/.config/qutebrowser/blocked-hosts`.
|
||||
This updates ~/.local/share/qutebrowser/blocked-hosts with downloaded
|
||||
host lists and re-reads ~/.config/qutebrowser/blocked-hosts.
|
||||
"""
|
||||
self._read_hosts_file(self._config_hosts_file,
|
||||
self._config_blocked_hosts)
|
||||
if self._local_hosts_file is None:
|
||||
raise cmdexc.CommandError("No data storage is configured!")
|
||||
self._blocked_hosts = set()
|
||||
self._done_count = 0
|
||||
download_manager = objreg.get('qtnetwork-download-manager')
|
||||
for url in config.val.content.host_blocking.lists:
|
||||
urls = config.get('content', 'host-block-lists')
|
||||
download_manager = objreg.get('download-manager', scope='window',
|
||||
window='last-focused')
|
||||
if urls is None:
|
||||
return
|
||||
for url in urls:
|
||||
if url.scheme() == 'file':
|
||||
filename = url.toLocalFile()
|
||||
try:
|
||||
fileobj = open(filename, 'rb')
|
||||
fileobj = open(url.path(), 'rb')
|
||||
except OSError as e:
|
||||
message.error("adblock: Error while reading {}: {}".format(
|
||||
filename, e.strerror))
|
||||
message.error(win_id, "adblock: Error while reading {}: "
|
||||
"{}".format(url.path(), e.strerror))
|
||||
continue
|
||||
download = FakeDownload(fileobj)
|
||||
self._in_progress.append(download)
|
||||
@@ -186,59 +210,13 @@ class HostBlocker:
|
||||
else:
|
||||
fobj = io.BytesIO()
|
||||
fobj.name = 'adblock: ' + url.host()
|
||||
target = downloads.FileObjDownloadTarget(fobj)
|
||||
target = usertypes.FileObjDownloadTarget(fobj)
|
||||
download = download_manager.get(url, target=target,
|
||||
auto_remove=True)
|
||||
self._in_progress.append(download)
|
||||
download.finished.connect(
|
||||
functools.partial(self.on_download_finished, download))
|
||||
|
||||
def _parse_line(self, line):
|
||||
"""Parse a line from a host file.
|
||||
|
||||
Args:
|
||||
line: The bytes object to parse.
|
||||
|
||||
Returns:
|
||||
True if parsing succeeded, False otherwise.
|
||||
"""
|
||||
if line.startswith(b'#'):
|
||||
# Ignoring comments early so we don't have to care about
|
||||
# encoding errors in them.
|
||||
return True
|
||||
|
||||
try:
|
||||
line = line.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
log.misc.error("Failed to decode: {!r}".format(line))
|
||||
return False
|
||||
|
||||
# Remove comments
|
||||
try:
|
||||
hash_idx = line.index('#')
|
||||
line = line[:hash_idx]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
line = line.strip()
|
||||
# Skip empty lines
|
||||
if not line:
|
||||
return True
|
||||
|
||||
parts = line.split()
|
||||
if len(parts) == 1:
|
||||
# "one host per line" format
|
||||
hosts = [parts[0]]
|
||||
else:
|
||||
# /etc/hosts format
|
||||
hosts = parts[1:]
|
||||
|
||||
for host in hosts:
|
||||
if '.' in host and not host.endswith('.localdomain'):
|
||||
self._blocked_hosts.add(host)
|
||||
|
||||
return True
|
||||
|
||||
def _merge_file(self, byte_io):
|
||||
"""Read and merge host files.
|
||||
|
||||
@@ -252,21 +230,38 @@ class HostBlocker:
|
||||
line_count = 0
|
||||
try:
|
||||
f = get_fileobj(byte_io)
|
||||
except (OSError, zipfile.BadZipFile, zipfile.LargeZipFile,
|
||||
LookupError) as e:
|
||||
message.error("adblock: Error while reading {}: {} - {}".format(
|
||||
byte_io.name, e.__class__.__name__, e))
|
||||
except (OSError, UnicodeDecodeError, zipfile.BadZipFile,
|
||||
zipfile.LargeZipFile) as e:
|
||||
message.error('current', "adblock: Error while reading {}: {} - "
|
||||
"{}".format(byte_io.name, e.__class__.__name__, e))
|
||||
return
|
||||
|
||||
for line in f:
|
||||
line_count += 1
|
||||
ok = self._parse_line(line)
|
||||
if not ok:
|
||||
# Remove comments
|
||||
try:
|
||||
hash_idx = line.index('#')
|
||||
line = line[:hash_idx]
|
||||
except ValueError:
|
||||
pass
|
||||
line = line.strip()
|
||||
# Skip empty lines
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) == 1:
|
||||
# "one host per line" format
|
||||
host = parts[0]
|
||||
elif len(parts) == 2:
|
||||
# /etc/hosts format
|
||||
host = parts[1]
|
||||
else:
|
||||
error_count += 1
|
||||
|
||||
continue
|
||||
if host not in self.WHITELISTED:
|
||||
self._blocked_hosts.add(host)
|
||||
log.misc.debug("{}: read {} lines".format(byte_io.name, line_count))
|
||||
if error_count > 0:
|
||||
message.error("adblock: {} read errors for {}".format(
|
||||
message.error('current', "adblock: {} read errors for {}".format(
|
||||
error_count, byte_io.name))
|
||||
|
||||
def on_lists_downloaded(self):
|
||||
@@ -274,13 +269,14 @@ class HostBlocker:
|
||||
with open(self._local_hosts_file, 'w', encoding='utf-8') as f:
|
||||
for host in sorted(self._blocked_hosts):
|
||||
f.write(host + '\n')
|
||||
message.info("adblock: Read {} hosts from {} sources.".format(
|
||||
len(self._blocked_hosts), self._done_count))
|
||||
message.info('current', "adblock: Read {} hosts from {} sources."
|
||||
.format(len(self._blocked_hosts), self._done_count))
|
||||
|
||||
@config.change_filter('content.host_blocking.lists')
|
||||
def _update_files(self):
|
||||
@config.change_filter('content', 'host-block-lists')
|
||||
def on_config_changed(self):
|
||||
"""Update files when the config changed."""
|
||||
if not config.val.content.host_blocking.lists:
|
||||
urls = config.get('content', 'host-block-lists')
|
||||
if urls is None and self._local_hosts_file is not None:
|
||||
try:
|
||||
os.remove(self._local_hosts_file)
|
||||
except FileNotFoundError:
|
||||
|
||||
@@ -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 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -19,56 +19,37 @@
|
||||
|
||||
"""Base class for a wrapper over QWebView/QWebEngineView."""
|
||||
|
||||
import enum
|
||||
import itertools
|
||||
|
||||
import attr
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QPoint
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QWidget, QApplication
|
||||
|
||||
import pygments
|
||||
import pygments.lexers
|
||||
import pygments.formatters
|
||||
from PyQt5.QtWidgets import QWidget, QLayout
|
||||
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
|
||||
urlutils, message)
|
||||
from qutebrowser.misc import miscwidgets, objects
|
||||
from qutebrowser.browser import mouse, hints
|
||||
from qutebrowser.qt import sip
|
||||
from qutebrowser.utils import utils, objreg, usertypes, message, log, qtutils
|
||||
|
||||
|
||||
tab_id_gen = itertools.count(0)
|
||||
|
||||
|
||||
def create(win_id, private, parent=None):
|
||||
def create(win_id, parent=None):
|
||||
"""Get a QtWebKit/QtWebEngine tab object.
|
||||
|
||||
Args:
|
||||
win_id: The window ID where the tab will be shown.
|
||||
private: Whether the tab is a private/off the record tab.
|
||||
parent: The Qt parent to set.
|
||||
"""
|
||||
# Importing modules here so we don't depend on QtWebEngine without the
|
||||
# argument and to avoid circular imports.
|
||||
mode_manager = modeman.instance(win_id)
|
||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||
if objreg.get('args').backend == 'webengine':
|
||||
from qutebrowser.browser.webengine import webenginetab
|
||||
tab_class = webenginetab.WebEngineTab
|
||||
else:
|
||||
from qutebrowser.browser.webkit import webkittab
|
||||
tab_class = webkittab.WebKitTab
|
||||
return tab_class(win_id=win_id, mode_manager=mode_manager, private=private,
|
||||
parent=parent)
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize backend-specific modules."""
|
||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||
from qutebrowser.browser.webengine import webenginetab
|
||||
webenginetab.init()
|
||||
return tab_class(win_id=win_id, mode_manager=mode_manager, parent=parent)
|
||||
|
||||
|
||||
class WebTabError(Exception):
|
||||
@@ -76,21 +57,35 @@ class WebTabError(Exception):
|
||||
"""Base class for various errors."""
|
||||
|
||||
|
||||
class UnsupportedOperationError(WebTabError):
|
||||
class WrapperLayout(QLayout):
|
||||
|
||||
"""Raised when an operation is not supported with the given backend."""
|
||||
"""A Qt layout which simply wraps a single widget.
|
||||
|
||||
This is used so the widget is hidden behind a AbstractTab API and can't
|
||||
easily be accidentally accessed.
|
||||
"""
|
||||
|
||||
def __init__(self, widget, parent=None):
|
||||
super().__init__(parent)
|
||||
self._widget = widget
|
||||
|
||||
def addItem(self, _widget):
|
||||
raise AssertionError("Should never be called!")
|
||||
|
||||
def sizeHint(self):
|
||||
return self._widget.sizeHint()
|
||||
|
||||
def itemAt(self, _index): # pragma: no cover
|
||||
# For some reason this sometimes gets called by Qt.
|
||||
return None
|
||||
|
||||
def takeAt(self, _index):
|
||||
raise AssertionError("Should never be called!")
|
||||
|
||||
def setGeometry(self, rect):
|
||||
self._widget.setGeometry(rect)
|
||||
|
||||
|
||||
TerminationStatus = enum.Enum('TerminationStatus', [
|
||||
'normal',
|
||||
'abnormal', # non-zero exit status
|
||||
'crashed', # e.g. segfault
|
||||
'killed',
|
||||
'unknown',
|
||||
])
|
||||
|
||||
|
||||
@attr.s
|
||||
class TabData:
|
||||
|
||||
"""A simple namespace with a fixed set of attributes.
|
||||
@@ -100,87 +95,14 @@ class TabData:
|
||||
load.
|
||||
inspector: The QWebInspector used for this webview.
|
||||
viewing_source: Set if we're currently showing a source view.
|
||||
Only used when sources are shown via pygments.
|
||||
open_target: Where to open the next link.
|
||||
Only used for QtWebKit.
|
||||
override_target: Override for open_target for fake clicks (like hints).
|
||||
Only used for QtWebKit.
|
||||
pinned: Flag to pin the tab.
|
||||
fullscreen: Whether the tab has a video shown fullscreen currently.
|
||||
netrc_used: Whether netrc authentication was performed.
|
||||
input_mode: current input mode for the tab.
|
||||
"""
|
||||
|
||||
keep_icon = attr.ib(False)
|
||||
viewing_source = attr.ib(False)
|
||||
inspector = attr.ib(None)
|
||||
open_target = attr.ib(usertypes.ClickTarget.normal)
|
||||
override_target = attr.ib(None)
|
||||
pinned = attr.ib(False)
|
||||
fullscreen = attr.ib(False)
|
||||
netrc_used = attr.ib(False)
|
||||
input_mode = attr.ib(usertypes.KeyMode.normal)
|
||||
__slots__ = ['keep_icon', 'viewing_source', 'inspector']
|
||||
|
||||
def should_show_icon(self):
|
||||
return (config.val.tabs.favicons.show == 'always' or
|
||||
config.val.tabs.favicons.show == 'pinned' and self.pinned)
|
||||
|
||||
|
||||
class AbstractAction:
|
||||
|
||||
"""Attribute of AbstractTab for Qt WebActions.
|
||||
|
||||
Class attributes (overridden by subclasses):
|
||||
action_class: The class actions are defined on (QWeb{Engine,}Page)
|
||||
action_base: The type of the actions (QWeb{Engine,}Page.WebAction)
|
||||
"""
|
||||
|
||||
action_class = None
|
||||
action_base = None
|
||||
|
||||
def __init__(self, tab):
|
||||
self._widget = None
|
||||
self._tab = tab
|
||||
|
||||
def exit_fullscreen(self):
|
||||
"""Exit the fullscreen mode."""
|
||||
raise NotImplementedError
|
||||
|
||||
def save_page(self):
|
||||
"""Save the current page."""
|
||||
raise NotImplementedError
|
||||
|
||||
def run_string(self, name):
|
||||
"""Run a webaction based on its name."""
|
||||
member = getattr(self.action_class, name, None)
|
||||
if not isinstance(member, self.action_base):
|
||||
raise WebTabError("{} is not a valid web action!".format(name))
|
||||
self._widget.triggerPageAction(member)
|
||||
|
||||
def show_source(self,
|
||||
pygments=False): # pylint: disable=redefined-outer-name
|
||||
"""Show the source of the current page in a new tab."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _show_source_pygments(self):
|
||||
|
||||
def show_source_cb(source):
|
||||
"""Show source as soon as it's ready."""
|
||||
# WORKAROUND for https://github.com/PyCQA/pylint/issues/491
|
||||
# pylint: disable=no-member
|
||||
lexer = pygments.lexers.HtmlLexer()
|
||||
formatter = pygments.formatters.HtmlFormatter(
|
||||
full=True, linenos='table')
|
||||
# pylint: enable=no-member
|
||||
highlighted = pygments.highlight(source, lexer, formatter)
|
||||
|
||||
tb = objreg.get('tabbed-browser', scope='window',
|
||||
window=self._tab.win_id)
|
||||
new_tab = tb.tabopen(background=False, related=True)
|
||||
new_tab.set_html(highlighted, self._tab.url())
|
||||
new_tab.data.viewing_source = True
|
||||
|
||||
self._tab.dump_async(show_source_cb)
|
||||
def __init__(self):
|
||||
self.keep_icon = False
|
||||
self.viewing_source = False
|
||||
self.inspector = None
|
||||
|
||||
|
||||
class AbstractPrinting:
|
||||
@@ -196,20 +118,10 @@ class AbstractPrinting:
|
||||
def check_printer_support(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def check_preview_support(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_pdf(self, filename):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_printer(self, printer, callback=None):
|
||||
"""Print the tab.
|
||||
|
||||
Args:
|
||||
printer: The QPrinter to print to.
|
||||
callback: Called with a boolean
|
||||
(True if printing succeeded, False otherwise)
|
||||
"""
|
||||
def to_printer(self, printer):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -219,8 +131,6 @@ class AbstractSearch(QObject):
|
||||
|
||||
Attributes:
|
||||
text: The last thing this view was searched for.
|
||||
search_displayed: Whether we're currently displaying search results in
|
||||
this view.
|
||||
_flags: The flags of the last search (needs to be set by subclasses).
|
||||
_widget: The underlying WebView widget.
|
||||
"""
|
||||
@@ -229,30 +139,14 @@ class AbstractSearch(QObject):
|
||||
super().__init__(parent)
|
||||
self._widget = None
|
||||
self.text = None
|
||||
self.search_displayed = False
|
||||
|
||||
def _is_case_sensitive(self, ignore_case):
|
||||
"""Check if case-sensitivity should be used.
|
||||
|
||||
This assumes self.text is already set properly.
|
||||
|
||||
Arguments:
|
||||
ignore_case: The ignore_case value from the config.
|
||||
"""
|
||||
mapping = {
|
||||
'smart': not self.text.islower(),
|
||||
'never': True,
|
||||
'always': False,
|
||||
}
|
||||
return mapping[ignore_case]
|
||||
|
||||
def search(self, text, *, ignore_case='never', reverse=False,
|
||||
def search(self, text, *, ignore_case=False, reverse=False,
|
||||
result_cb=None):
|
||||
"""Find the given text on the page.
|
||||
|
||||
Args:
|
||||
text: The text to search for.
|
||||
ignore_case: Search case-insensitively. ('always'/'never/'smart')
|
||||
ignore_case: Search case-insensitively. (True/False/'smart')
|
||||
reverse: Reverse search direction.
|
||||
result_cb: Called with a bool indicating whether a match was found.
|
||||
"""
|
||||
@@ -288,36 +182,36 @@ class AbstractZoom(QObject):
|
||||
_default_zoom_changed: Whether the zoom was changed from the default.
|
||||
"""
|
||||
|
||||
def __init__(self, tab, parent=None):
|
||||
def __init__(self, win_id, parent=None):
|
||||
super().__init__(parent)
|
||||
self._tab = tab
|
||||
self._widget = None
|
||||
self._win_id = win_id
|
||||
self._default_zoom_changed = False
|
||||
self._init_neighborlist()
|
||||
config.instance.changed.connect(self._on_config_changed)
|
||||
self._zoom_factor = float(config.val.zoom.default) / 100
|
||||
objreg.get('config').changed.connect(self._on_config_changed)
|
||||
|
||||
# # FIXME:qtwebengine is this needed?
|
||||
# # For some reason, this signal doesn't get disconnected automatically
|
||||
# # when the WebView is destroyed on older PyQt versions.
|
||||
# # See https://github.com/qutebrowser/qutebrowser/issues/390
|
||||
# # See https://github.com/The-Compiler/qutebrowser/issues/390
|
||||
# self.destroyed.connect(functools.partial(
|
||||
# cfg.changed.disconnect, self.init_neighborlist))
|
||||
|
||||
@pyqtSlot(str)
|
||||
def _on_config_changed(self, option):
|
||||
if option in ['zoom.levels', 'zoom.default']:
|
||||
@pyqtSlot(str, str)
|
||||
def _on_config_changed(self, section, option):
|
||||
if section == 'ui' and option in ['zoom-levels', 'default-zoom']:
|
||||
if not self._default_zoom_changed:
|
||||
factor = float(config.val.zoom.default) / 100
|
||||
self.set_factor(factor)
|
||||
factor = float(config.get('ui', 'default-zoom')) / 100
|
||||
self._set_factor_internal(factor)
|
||||
self._default_zoom_changed = False
|
||||
self._init_neighborlist()
|
||||
|
||||
def _init_neighborlist(self):
|
||||
"""Initialize self._neighborlist."""
|
||||
levels = config.val.zoom.levels
|
||||
levels = config.get('ui', 'zoom-levels')
|
||||
self._neighborlist = usertypes.NeighborList(
|
||||
levels, mode=usertypes.NeighborList.Modes.edge)
|
||||
self._neighborlist.fuzzyval = config.val.zoom.default
|
||||
self._neighborlist.fuzzyval = config.get('ui', 'default-zoom')
|
||||
|
||||
def offset(self, offset):
|
||||
"""Increase/Decrease the zoom level by the given offset.
|
||||
@@ -332,9 +226,6 @@ class AbstractZoom(QObject):
|
||||
self.set_factor(float(level) / 100, fuzzyval=False)
|
||||
return level
|
||||
|
||||
def _set_factor_internal(self, factor):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_factor(self, factor, *, fuzzyval=True):
|
||||
"""Zoom to a given zoom factor.
|
||||
|
||||
@@ -346,37 +237,38 @@ class AbstractZoom(QObject):
|
||||
self._neighborlist.fuzzyval = int(factor * 100)
|
||||
if factor < 0:
|
||||
raise ValueError("Can't zoom to factor {}!".format(factor))
|
||||
|
||||
default_zoom_factor = float(config.val.zoom.default) / 100
|
||||
self._default_zoom_changed = (factor != default_zoom_factor)
|
||||
|
||||
self._zoom_factor = factor
|
||||
self._default_zoom_changed = True
|
||||
self._set_factor_internal(factor)
|
||||
|
||||
def factor(self):
|
||||
return self._zoom_factor
|
||||
raise NotImplementedError
|
||||
|
||||
def set_default(self):
|
||||
self._set_factor_internal(float(config.val.zoom.default) / 100)
|
||||
default_zoom = config.get('ui', 'default-zoom')
|
||||
self._set_factor_internal(float(default_zoom) / 100)
|
||||
|
||||
def set_current(self):
|
||||
self._set_factor_internal(self._zoom_factor)
|
||||
@pyqtSlot(QPoint)
|
||||
def _on_mouse_wheel_zoom(self, delta):
|
||||
"""Handle zooming via mousewheel requested by the web view."""
|
||||
divider = config.get('input', 'mouse-zoom-divider')
|
||||
factor = self.factor() + delta.y() / divider
|
||||
if factor < 0:
|
||||
return
|
||||
perc = int(100 * factor)
|
||||
message.info(self._win_id, "Zoom level: {}%".format(perc))
|
||||
self._neighborlist.fuzzyval = perc
|
||||
self._set_factor_internal(factor)
|
||||
self._default_zoom_changed = True
|
||||
|
||||
|
||||
class AbstractCaret(QObject):
|
||||
|
||||
"""Attribute of AbstractTab for caret browsing.
|
||||
"""Attribute of AbstractTab for caret browsing."""
|
||||
|
||||
Signals:
|
||||
selection_toggled: Emitted when the selection was toggled.
|
||||
arg: Whether the selection is now active.
|
||||
"""
|
||||
|
||||
selection_toggled = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, tab, mode_manager, parent=None):
|
||||
def __init__(self, win_id, tab, mode_manager, parent=None):
|
||||
super().__init__(parent)
|
||||
self._tab = tab
|
||||
self._win_id = win_id
|
||||
self._widget = None
|
||||
self.selection_enabled = False
|
||||
mode_manager.entered.connect(self._on_mode_entered)
|
||||
@@ -385,7 +277,7 @@ class AbstractCaret(QObject):
|
||||
def _on_mode_entered(self, mode):
|
||||
raise NotImplementedError
|
||||
|
||||
def _on_mode_left(self, mode):
|
||||
def _on_mode_left(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_next_line(self, count=1):
|
||||
@@ -439,15 +331,11 @@ class AbstractCaret(QObject):
|
||||
def drop_selection(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def selection(self, callback):
|
||||
def has_selection(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _follow_enter(self, tab):
|
||||
"""Follow a link by faking an enter press."""
|
||||
if tab:
|
||||
self._tab.key_press(Qt.Key_Enter, modifier=Qt.ControlModifier)
|
||||
else:
|
||||
self._tab.key_press(Qt.Key_Enter)
|
||||
def selection(self, html=False):
|
||||
raise NotImplementedError
|
||||
|
||||
def follow_selected(self, *, tab=False):
|
||||
raise NotImplementedError
|
||||
@@ -463,12 +351,6 @@ class AbstractScroller(QObject):
|
||||
super().__init__(parent)
|
||||
self._tab = tab
|
||||
self._widget = None
|
||||
self.perc_changed.connect(self._log_scroll_pos_change)
|
||||
|
||||
@pyqtSlot()
|
||||
def _log_scroll_pos_change(self):
|
||||
log.webview.vdebug("Scroll position changed to {}".format(
|
||||
self.pos_px()))
|
||||
|
||||
def _init_widget(self, widget):
|
||||
self._widget = widget
|
||||
@@ -485,9 +367,6 @@ class AbstractScroller(QObject):
|
||||
def to_point(self, point):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_anchor(self, name):
|
||||
raise NotImplementedError
|
||||
|
||||
def delta(self, x=0, y=0):
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -542,23 +421,11 @@ class AbstractHistory:
|
||||
def current_idx(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def back(self, count=1):
|
||||
"""Go back in the tab's history."""
|
||||
idx = self.current_idx() - count
|
||||
if idx >= 0:
|
||||
self._go_to_item(self._item_at(idx))
|
||||
else:
|
||||
self._go_to_item(self._item_at(0))
|
||||
raise WebTabError("At beginning of history.")
|
||||
def back(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def forward(self, count=1):
|
||||
"""Go forward in the tab's history."""
|
||||
idx = self.current_idx() + count
|
||||
if idx < len(self):
|
||||
self._go_to_item(self._item_at(idx))
|
||||
else:
|
||||
self._go_to_item(self._item_at(len(self) - 1))
|
||||
raise WebTabError("At end of history.")
|
||||
def forward(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def can_go_back(self):
|
||||
raise NotImplementedError
|
||||
@@ -566,12 +433,6 @@ class AbstractHistory:
|
||||
def can_go_forward(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _item_at(self, i):
|
||||
raise NotImplementedError
|
||||
|
||||
def _go_to_item(self, item):
|
||||
raise NotImplementedError
|
||||
|
||||
def serialize(self):
|
||||
"""Serialize into an opaque format understood by self.deserialize."""
|
||||
raise NotImplementedError
|
||||
@@ -585,82 +446,6 @@ class AbstractHistory:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractElements:
|
||||
|
||||
"""Finding and handling of elements on the page."""
|
||||
|
||||
def __init__(self, tab):
|
||||
self._widget = None
|
||||
self._tab = tab
|
||||
|
||||
def find_css(self, selector, callback, *, only_visible=False):
|
||||
"""Find all HTML elements matching a given selector async.
|
||||
|
||||
Args:
|
||||
callback: The callback to be called when the search finished.
|
||||
selector: The CSS selector to search for.
|
||||
only_visible: Only show elements which are visible on screen.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def find_id(self, elem_id, callback):
|
||||
"""Find the HTML element with the given ID async.
|
||||
|
||||
Args:
|
||||
callback: The callback to be called when the search finished.
|
||||
elem_id: The ID to search for.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def find_focused(self, callback):
|
||||
"""Find the focused element on the page async.
|
||||
|
||||
Args:
|
||||
callback: The callback to be called when the search finished.
|
||||
Called with a WebEngineElement or None.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def find_at_pos(self, pos, callback):
|
||||
"""Find the element at the given position async.
|
||||
|
||||
This is also called "hit test" elsewhere.
|
||||
|
||||
Args:
|
||||
pos: The QPoint to get the element for.
|
||||
callback: The callback to be called when the search finished.
|
||||
Called with a WebEngineElement or None.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractAudio(QObject):
|
||||
|
||||
"""Handling of audio/muting for this tab."""
|
||||
|
||||
muted_changed = pyqtSignal(bool)
|
||||
recently_audible_changed = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._widget = None
|
||||
|
||||
def set_muted(self, muted: bool):
|
||||
"""Set this tab as muted or not."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_muted(self):
|
||||
"""Whether this tab is muted."""
|
||||
raise NotImplementedError
|
||||
|
||||
def toggle_muted(self):
|
||||
self.set_muted(not self.is_muted())
|
||||
|
||||
def is_recently_audible(self):
|
||||
"""Whether this tab has had audio playing recently."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractTab(QWidget):
|
||||
|
||||
"""A wrapper over the given widget to hide its API and expose another one.
|
||||
@@ -670,7 +455,6 @@ class AbstractTab(QWidget):
|
||||
Attributes:
|
||||
history: The AbstractHistory for the current tab.
|
||||
registry: The ObjectRegistry associated with this tab.
|
||||
private: Whether private browsing is turned on for this tab.
|
||||
|
||||
_load_status: loading status of this page
|
||||
Accessible via load_status() method.
|
||||
@@ -685,14 +469,6 @@ class AbstractTab(QWidget):
|
||||
new_tab_requested: Emitted when a new tab should be opened with the
|
||||
given URL.
|
||||
load_status_changed: The loading status changed
|
||||
fullscreen_requested: Fullscreen display was requested by the page.
|
||||
arg: True if fullscreen should be turned on,
|
||||
False if it should be turned off.
|
||||
renderer_process_terminated: Emitted when the underlying renderer
|
||||
process terminated.
|
||||
arg 0: A TerminationStatus member.
|
||||
arg 1: The exit code.
|
||||
predicted_navigation: Emitted before we tell Qt to open a URL.
|
||||
"""
|
||||
|
||||
window_close_requested = pyqtSignal()
|
||||
@@ -706,14 +482,8 @@ class AbstractTab(QWidget):
|
||||
new_tab_requested = pyqtSignal(QUrl)
|
||||
url_changed = pyqtSignal(QUrl)
|
||||
shutting_down = pyqtSignal()
|
||||
contents_size_changed = pyqtSignal(QSizeF)
|
||||
add_history_item = pyqtSignal(QUrl, QUrl, str) # url, requested url, title
|
||||
fullscreen_requested = pyqtSignal(bool)
|
||||
renderer_process_terminated = pyqtSignal(TerminationStatus, int)
|
||||
predicted_navigation = pyqtSignal(QUrl)
|
||||
|
||||
def __init__(self, *, win_id, mode_manager, private, parent=None):
|
||||
self.private = private
|
||||
def __init__(self, win_id, parent=None):
|
||||
self.win_id = win_id
|
||||
self.tab_id = next(tab_id_gen)
|
||||
super().__init__(parent)
|
||||
@@ -724,45 +494,34 @@ class AbstractTab(QWidget):
|
||||
tab_registry[self.tab_id] = self
|
||||
objreg.register('tab', self, registry=self.registry)
|
||||
|
||||
# self.history = AbstractHistory(self)
|
||||
# self.scroller = AbstractScroller(self, parent=self)
|
||||
# self.caret = AbstractCaret(win_id=win_id, tab=self, mode_manager=...,
|
||||
# parent=self)
|
||||
# self.zoom = AbstractZoom(win_id=win_id)
|
||||
# self.search = AbstractSearch(parent=self)
|
||||
# self.printing = AbstractPrinting()
|
||||
self.data = TabData()
|
||||
self._layout = miscwidgets.WrapperLayout(self)
|
||||
self._layout = None
|
||||
self._widget = None
|
||||
self._progress = 0
|
||||
self._has_ssl_errors = False
|
||||
self._mode_manager = mode_manager
|
||||
self._load_status = usertypes.LoadStatus.none
|
||||
self._mouse_event_filter = mouse.MouseEventFilter(
|
||||
self, parent=self)
|
||||
self.backend = None
|
||||
|
||||
# FIXME:qtwebengine Should this be public api via self.hints?
|
||||
# Also, should we get it out of objreg?
|
||||
hintmanager = hints.HintManager(win_id, self.tab_id, parent=self)
|
||||
objreg.register('hintmanager', hintmanager, scope='tab',
|
||||
window=self.win_id, tab=self.tab_id)
|
||||
|
||||
self.predicted_navigation.connect(self._on_predicted_navigation)
|
||||
|
||||
def _set_widget(self, widget):
|
||||
# pylint: disable=protected-access
|
||||
self._layout = WrapperLayout(widget, self)
|
||||
self._widget = widget
|
||||
self._layout.wrap(self, widget)
|
||||
self.history._history = widget.history()
|
||||
self.scroller._init_widget(widget)
|
||||
self.caret._widget = widget
|
||||
self.zoom._widget = widget
|
||||
self.search._widget = widget
|
||||
self.printing._widget = widget
|
||||
self.action._widget = widget
|
||||
self.elements._widget = widget
|
||||
self.audio._widget = widget
|
||||
self.settings._settings = widget.settings()
|
||||
|
||||
self._install_event_filter()
|
||||
self.zoom.set_default()
|
||||
|
||||
def _install_event_filter(self):
|
||||
raise NotImplementedError
|
||||
widget.mouse_wheel_zoom.connect(self.zoom._on_mouse_wheel_zoom)
|
||||
widget.setParent(self)
|
||||
self.setFocusProxy(widget)
|
||||
|
||||
def _set_load_status(self, val):
|
||||
"""Setter for load_status."""
|
||||
@@ -772,39 +531,6 @@ class AbstractTab(QWidget):
|
||||
self._load_status = val
|
||||
self.load_status_changed.emit(val.name)
|
||||
|
||||
def event_target(self):
|
||||
"""Return the widget events should be sent to."""
|
||||
raise NotImplementedError
|
||||
|
||||
def send_event(self, evt):
|
||||
"""Send the given event to the underlying widget.
|
||||
|
||||
The event will be sent via QApplication.postEvent.
|
||||
Note that a posted event may not be re-used in any way!
|
||||
"""
|
||||
# This only gives us some mild protection against re-using events, but
|
||||
# it's certainly better than a segfault.
|
||||
if getattr(evt, 'posted', False):
|
||||
raise utils.Unreachable("Can't re-use an event which was already "
|
||||
"posted!")
|
||||
|
||||
recipient = self.event_target()
|
||||
if recipient is None:
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/3888
|
||||
log.webview.warning("Unable to find event target!")
|
||||
return
|
||||
|
||||
evt.posted = True
|
||||
QApplication.postEvent(recipient, evt)
|
||||
|
||||
@pyqtSlot(QUrl)
|
||||
def _on_predicted_navigation(self, url):
|
||||
"""Adjust the title if we are going to visit an URL soon."""
|
||||
qtutils.ensure_valid(url)
|
||||
url_string = url.toDisplayString()
|
||||
log.webview.debug("Predicted navigation: {}".format(url_string))
|
||||
self.title_changed.emit(url_string)
|
||||
|
||||
@pyqtSlot(QUrl)
|
||||
def _on_url_changed(self, url):
|
||||
"""Update title when URL has changed and no title is available."""
|
||||
@@ -820,80 +546,32 @@ class AbstractTab(QWidget):
|
||||
self._set_load_status(usertypes.LoadStatus.loading)
|
||||
self.load_started.emit()
|
||||
|
||||
@pyqtSlot(usertypes.NavigationRequest)
|
||||
def _on_navigation_request(self, navigation):
|
||||
"""Handle common acceptNavigationRequest code."""
|
||||
url = utils.elide(navigation.url.toDisplayString(), 100)
|
||||
log.webview.debug("navigation request: url {}, type {}, is_main_frame "
|
||||
"{}".format(url,
|
||||
navigation.navigation_type,
|
||||
navigation.is_main_frame))
|
||||
|
||||
if (navigation.navigation_type == navigation.Type.link_clicked and
|
||||
not navigation.url.isValid()):
|
||||
msg = urlutils.get_errstring(navigation.url,
|
||||
"Invalid link clicked")
|
||||
message.error(msg)
|
||||
self.data.open_target = usertypes.ClickTarget.normal
|
||||
navigation.accepted = False
|
||||
|
||||
def handle_auto_insert_mode(self, ok):
|
||||
"""Handle `input.insert_mode.auto_load` after loading finished."""
|
||||
if not config.val.input.insert_mode.auto_load or not ok:
|
||||
return
|
||||
|
||||
cur_mode = self._mode_manager.mode
|
||||
if cur_mode == usertypes.KeyMode.insert:
|
||||
return
|
||||
|
||||
def _auto_insert_mode_cb(elem):
|
||||
"""Called from JS after finding the focused element."""
|
||||
if elem is None:
|
||||
log.webview.debug("No focused element!")
|
||||
return
|
||||
if elem.is_editable():
|
||||
modeman.enter(self.win_id, usertypes.KeyMode.insert,
|
||||
'load finished', only_if_normal=True)
|
||||
|
||||
self.elements.find_focused(_auto_insert_mode_cb)
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def _on_load_finished(self, ok):
|
||||
if sip.isdeleted(self._widget):
|
||||
# https://github.com/qutebrowser/qutebrowser/issues/3498
|
||||
return
|
||||
|
||||
sess_manager = objreg.get('session-manager')
|
||||
sess_manager.save_autosave()
|
||||
|
||||
if ok and not self._has_ssl_errors:
|
||||
if self.url().scheme() == 'https':
|
||||
self._set_load_status(usertypes.LoadStatus.success_https)
|
||||
else:
|
||||
self._set_load_status(usertypes.LoadStatus.success)
|
||||
|
||||
elif ok:
|
||||
self._set_load_status(usertypes.LoadStatus.warn)
|
||||
else:
|
||||
self._set_load_status(usertypes.LoadStatus.error)
|
||||
|
||||
self.load_finished.emit(ok)
|
||||
|
||||
if not self.title():
|
||||
self.title_changed.emit(self.url().toDisplayString())
|
||||
|
||||
self.zoom.set_current()
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_history_trigger(self):
|
||||
"""Emit add_history_item when triggered by backend-specific signal."""
|
||||
raise NotImplementedError
|
||||
|
||||
@pyqtSlot(int)
|
||||
def _on_load_progress(self, perc):
|
||||
self._progress = perc
|
||||
self.load_progress.emit(perc)
|
||||
|
||||
def url(self, requested=False):
|
||||
@pyqtSlot()
|
||||
def _on_ssl_errors(self):
|
||||
self._has_ssl_errors = True
|
||||
|
||||
def url(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def progress(self):
|
||||
@@ -902,12 +580,11 @@ class AbstractTab(QWidget):
|
||||
def load_status(self):
|
||||
return self._load_status
|
||||
|
||||
def _openurl_prepare(self, url, *, predict=True):
|
||||
def _openurl_prepare(self, url):
|
||||
qtutils.ensure_valid(url)
|
||||
if predict:
|
||||
self.predicted_navigation.emit(url)
|
||||
self.title_changed.emit(url.toDisplayString())
|
||||
|
||||
def openurl(self, url, *, predict=True):
|
||||
def openurl(self, url):
|
||||
raise NotImplementedError
|
||||
|
||||
def reload(self, *, force=False):
|
||||
@@ -919,29 +596,27 @@ class AbstractTab(QWidget):
|
||||
def clear_ssl_errors(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def key_press(self, key, modifier=Qt.NoModifier):
|
||||
"""Send a fake key event to this tab."""
|
||||
raise NotImplementedError
|
||||
|
||||
def dump_async(self, callback, *, plain=False):
|
||||
"""Dump the current page's html asynchronously.
|
||||
"""Dump the current page to a file ascync.
|
||||
|
||||
The given callback will be called with the result when dumping is
|
||||
complete.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def run_js_async(self, code, callback=None, *, world=None):
|
||||
def run_js_async(self, code, callback=None):
|
||||
"""Run javascript async.
|
||||
|
||||
The given callback will be called with the result when running JS is
|
||||
complete.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
Args:
|
||||
code: The javascript code to run.
|
||||
callback: The callback to call with the result, or None.
|
||||
world: A world ID (int or usertypes.JsWorld member) to run the JS
|
||||
in the main world or in another isolated world.
|
||||
def run_js_blocking(self, code):
|
||||
"""Run javascript and block.
|
||||
|
||||
This returns the result to the caller. Its use should be avoided when
|
||||
possible as it runs a local event loop for QtWebEngine.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -954,32 +629,13 @@ class AbstractTab(QWidget):
|
||||
def icon(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_html(self, html, base_url=QUrl()):
|
||||
raise NotImplementedError
|
||||
|
||||
def networkaccessmanager(self):
|
||||
"""Get the QNetworkAccessManager for this tab.
|
||||
|
||||
This is only implemented for QtWebKit.
|
||||
For QtWebEngine, always returns None.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def user_agent(self):
|
||||
"""Get the user agent for this tab.
|
||||
|
||||
This is only implemented for QtWebKit.
|
||||
For QtWebEngine, always returns None.
|
||||
"""
|
||||
def set_html(self, html, base_url):
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
url = utils.elide(self.url().toDisplayString(QUrl.EncodeUnicode),
|
||||
100)
|
||||
except (AttributeError, RuntimeError) as exc:
|
||||
url = '<{}>'.format(exc.__class__.__name__)
|
||||
except AttributeError:
|
||||
url = '<AttributeError>'
|
||||
return utils.get_repr(self, tab_id=self.tab_id, url=url)
|
||||
|
||||
def is_deleted(self):
|
||||
return sip.isdeleted(self._widget)
|
||||
|
||||