summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/changelog.asciidoc11
-rw-r--r--doc/help/settings.asciidoc9
-rw-r--r--doc/install.asciidoc6
-rw-r--r--doc/userscripts.asciidoc3
-rw-r--r--misc/requirements/requirements-check-manifest.txt2
-rw-r--r--misc/requirements/requirements-dev.txt8
-rw-r--r--misc/requirements/requirements-mypy.txt8
-rw-r--r--misc/requirements/requirements-mypy.txt-raw3
-rw-r--r--misc/requirements/requirements-pylint.txt2
-rw-r--r--misc/requirements/requirements-tests.txt11
-rw-r--r--misc/userscripts/README.md3
-rw-r--r--qutebrowser/browser/greasemonkey.py17
-rw-r--r--qutebrowser/browser/hints.py6
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py7
-rw-r--r--qutebrowser/browser/webkit/http.py2
-rw-r--r--qutebrowser/config/configdata.yml5
-rw-r--r--qutebrowser/mainwindow/prompt.py2
-rwxr-xr-xscripts/dev/build_release.py2
-rw-r--r--scripts/dev/recompile_requirements.py3
-rw-r--r--scripts/link_pyqt.py4
-rw-r--r--tests/end2end/features/test_history_bdd.py1
-rw-r--r--tests/unit/browser/webengine/test_webenginetab.py39
-rw-r--r--tests/unit/javascript/test_greasemonkey.py14
23 files changed, 142 insertions, 26 deletions
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc
index 46c6c283a..3387198e9 100644
--- a/doc/changelog.asciidoc
+++ b/doc/changelog.asciidoc
@@ -24,6 +24,8 @@ Added
- New `content.prefers_reduced_motion` setting to request websites to reduce
non-essential motion/animations.
+- New `colors.prompts.selected.fg` setting to customize the text color for
+ selected items in filename prompts.
Changed
~~~~~~~
@@ -31,6 +33,15 @@ Changed
- The `fonts.web.*` settings now support URL patterns.
- The `:greasemonkey-reload` command now shows a list of loaded scripts and has
a new `--quiet` switch to suppress that message.
+- When launching a userscript via hints, a new `QUTE_CURRENT_URL` environment
+ variable now points to the current page (rather than the URL of the selected
+ element, where `QUTE_URL` points to).
+
+Fixed
+~~~~~
+
+- Crash when two Greasemonkey scripts have the same name (usually happening
+ because the same file is in both the data and the config directory).
[[v2.2.3]]
v2.2.3 (2021-06-01)
diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc
index 90197fddb..c5206e52e 100644
--- a/doc/help/settings.asciidoc
+++ b/doc/help/settings.asciidoc
@@ -67,6 +67,7 @@
|<<colors.prompts.border,colors.prompts.border>>|Border used around UI elements in prompts.
|<<colors.prompts.fg,colors.prompts.fg>>|Foreground color for prompts.
|<<colors.prompts.selected.bg,colors.prompts.selected.bg>>|Background color for the selected item in filename prompts.
+|<<colors.prompts.selected.fg,colors.prompts.selected.fg>>|Foreground color for the selected item in filename prompts.
|<<colors.statusbar.caret.bg,colors.statusbar.caret.bg>>|Background color of the statusbar in caret mode.
|<<colors.statusbar.caret.fg,colors.statusbar.caret.fg>>|Foreground color of the statusbar in caret mode.
|<<colors.statusbar.caret.selection.bg,colors.statusbar.caret.selection.bg>>|Background color of the statusbar in caret mode with a selection.
@@ -1233,6 +1234,14 @@ Type: <<types,QssColor>>
Default: +pass:[grey]+
+[[colors.prompts.selected.fg]]
+=== colors.prompts.selected.fg
+Foreground color for the selected item in filename prompts.
+
+Type: <<types,QssColor>>
+
+Default: +pass:[white]+
+
[[colors.statusbar.caret.bg]]
=== colors.statusbar.caret.bg
Background color of the statusbar in caret mode.
diff --git a/doc/install.asciidoc b/doc/install.asciidoc
index 580a511c5..83c332b4d 100644
--- a/doc/install.asciidoc
+++ b/doc/install.asciidoc
@@ -342,11 +342,11 @@ The binary release ships with a QtWebEngine built without proprietary codec
support. To get support for e.g. h264/mp4 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:
+This binary is also available through the https://brew.sh/[Homebrew] package
+manager as a https://github.com/Homebrew/homebrew-cask[cask]:
----
-$ brew install qutebrowser --cask
+$ brew install qutebrowser
----
Nightly builds
diff --git a/doc/userscripts.asciidoc b/doc/userscripts.asciidoc
index b2af783fb..5ff358ee0 100644
--- a/doc/userscripts.asciidoc
+++ b/doc/userscripts.asciidoc
@@ -52,7 +52,7 @@ The following environment variables will be set when a userscript is launched:
In `command` mode:
-- `QUTE_URL`: The current URL.
+- `QUTE_URL`: The current page URL.
- `QUTE_TITLE`: The title of the current page.
- `QUTE_SELECTED_TEXT`: The text currently selected on the page.
- `QUTE_COUNT`: The `count` from the spawn command running the userscript.
@@ -60,6 +60,7 @@ In `command` mode:
In `hints` mode:
- `QUTE_URL`: The URL selected via hints.
+- `QUTE_CURRENT_URL`: The current page URL.
- `QUTE_SELECTED_TEXT`: The plain text of the element selected via hints.
- `QUTE_SELECTED_HTML`: The HTML of the element selected via hints.
diff --git a/misc/requirements/requirements-check-manifest.txt b/misc/requirements/requirements-check-manifest.txt
index 6d96812e5..18bec7464 100644
--- a/misc/requirements/requirements-check-manifest.txt
+++ b/misc/requirements/requirements-check-manifest.txt
@@ -1,6 +1,6 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-build==0.4.0
+build==0.5.0
check-manifest==0.46
packaging==20.9
pep517==0.10.0
diff --git a/misc/requirements/requirements-dev.txt b/misc/requirements/requirements-dev.txt
index 1ecb63561..eec2e2a95 100644
--- a/misc/requirements/requirements-dev.txt
+++ b/misc/requirements/requirements-dev.txt
@@ -7,18 +7,18 @@ chardet==4.0.0
cryptography==3.4.7
Deprecated==1.2.12
github3.py==2.0.0
-hunter==3.3.3
+hunter==3.3.5
idna==2.10
-jwcrypto==0.9
+jwcrypto==0.9.1
manhole==1.8.0
packaging==20.9
pycparser==2.20
Pympler==0.9
pyparsing==2.4.7
-PyQt-builder==1.10.0
+PyQt-builder==1.10.1
python-dateutil==2.8.1
requests==2.25.1
-sip==6.1.0
+sip==6.1.1
six==1.16.0
toml==0.10.2
uritemplate==3.0.1
diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt
index 7dfb82e0f..8fdd6692d 100644
--- a/misc/requirements/requirements-mypy.txt
+++ b/misc/requirements/requirements-mypy.txt
@@ -1,7 +1,7 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
chardet==4.0.0
-diff-cover==5.1.2
+diff-cover==5.2.0
importlib-metadata==4.5.0
importlib-resources==5.1.4
inflect==5.3.0
@@ -9,11 +9,13 @@ Jinja2==3.0.1
jinja2-pluralize==0.3.0
lxml==4.6.3
MarkupSafe==2.0.1
-mypy==0.812
+mypy==0.902
mypy-extensions==0.4.3
pluggy==0.13.1
Pygments==2.9.0
PyQt5-stubs==5.15.2.0
-typed-ast==1.4.3
+toml==0.10.2
+types-dataclasses==0.1.5
+types-PyYAML==5.4.3
typing-extensions==3.10.0.0
zipp==3.4.1
diff --git a/misc/requirements/requirements-mypy.txt-raw b/misc/requirements/requirements-mypy.txt-raw
index e93eaae0b..4baeec11f 100644
--- a/misc/requirements/requirements-mypy.txt-raw
+++ b/misc/requirements/requirements-mypy.txt-raw
@@ -1,7 +1,10 @@
mypy
lxml # For HTML reports
diff-cover
+
PyQt5-stubs
+types-dataclasses
+types-PyYAML
# So stubs are available even on newer Python versions
importlib_resources
diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt
index 5d6ef8f30..eaaeaa5ef 100644
--- a/misc/requirements/requirements-pylint.txt
+++ b/misc/requirements/requirements-pylint.txt
@@ -10,7 +10,7 @@ future==0.18.2
github3.py==2.0.0
idna==2.10
isort==4.3.21
-jwcrypto==0.9
+jwcrypto==0.9.1
lazy-object-proxy==1.4.3
mccabe==0.6.1
pefile==2021.5.24
diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt
index 15c2503c7..e8d28d6f9 100644
--- a/misc/requirements/requirements-tests.txt
+++ b/misc/requirements/requirements-tests.txt
@@ -1,6 +1,5 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
-apipkg==1.5
attrs==21.2.0
beautifulsoup4==4.9.3
certifi==2021.5.30
@@ -9,12 +8,12 @@ cheroot==8.5.2
click==8.0.1
coverage==5.5
EasyProcess==0.3
-execnet==1.8.1
+execnet==1.9.0
filelock==3.0.12
Flask==2.0.1
glob2==0.7
-hunter==3.3.3
-hypothesis==6.13.14
+hunter==3.3.5
+hypothesis==6.14.0
icdiff==1.9.1
idna==2.10
iniconfig==1.1.1
@@ -42,10 +41,10 @@ pytest-forked==1.3.0
pytest-icdiff==0.5
pytest-instafail==0.4.2
pytest-mock==3.6.1
-pytest-qt==4.0.0
+pytest-qt==4.0.2
pytest-repeat==0.9.1
pytest-rerunfailures==10.0
-pytest-xdist==2.2.1
+pytest-xdist==2.3.0
pytest-xvfb==2.0.0
PyVirtualDisplay==2.2
requests==2.25.1
diff --git a/misc/userscripts/README.md b/misc/userscripts/README.md
index 7dbb0c840..843cf22d0 100644
--- a/misc/userscripts/README.md
+++ b/misc/userscripts/README.md
@@ -78,6 +78,9 @@ The following userscripts can be found on their own repositories.
Integration with 1password on macOS.
- [localhost](https://github.com/SidharthArya/.qutebrowser/blob/master/userscripts/localhost):
Quickly navigate to localhost:port. For reference: [A quicker way to reach localhost with qutebrowser](https://sidhartharya.me/a-quicker-way-to-reach-localhost-with-qutebrowser/)
+- [untrack-url](https://github.com/qutebrowser/qutebrowser/discussions/6555),
+ convert various URLs (YouTube/Reddit/Twitter/Instagram/Google Maps) to other
+ services (Invidious, Teddit, Nitter, Bibliogram, OpenStreetMap).
[Zotero]: https://www.zotero.org/
[Pocket]: https://getpocket.com/
diff --git a/qutebrowser/browser/greasemonkey.py b/qutebrowser/browser/greasemonkey.py
index 03db3be0c..d0245937f 100644
--- a/qutebrowser/browser/greasemonkey.py
+++ b/qutebrowser/browser/greasemonkey.py
@@ -67,6 +67,7 @@ class GreasemonkeyScript:
self.runs_on_sub_frames = True
self.jsworld = "main"
self.name = ''
+ self.dedup_suffix = 1
for name, value in properties:
if name == 'name':
@@ -104,6 +105,20 @@ class GreasemonkeyScript:
def __str__(self):
return self.name
+ def full_name(self) -> str:
+ """Get the full name of this script.
+
+ This includes a GM- prefix, its namespace (if any) and deduplication
+ counter suffix, if set.
+ """
+ parts = ['GM-']
+ if self.namespace is not None:
+ parts += [self.namespace, '/']
+ parts.append(self.name)
+ if self.dedup_suffix > 1:
+ parts.append(f"-{self.dedup_suffix}")
+ return ''.join(parts)
+
@classmethod
def parse(cls, source, filename=None):
"""GreasemonkeyScript factory.
@@ -346,7 +361,7 @@ class GreasemonkeyManager(QObject):
self._in_progress_dls.remove(download)
if not self._add_script_with_requires(script):
log.greasemonkey.debug(
- "Finished download {download.basename} for script {script} "
+ f"Finished download {download.basename} for script {script} "
"but some requirements are still pending")
def _add_script_with_requires(self, script, quiet=False):
diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py
index 7cdd0fd84..e127cd10a 100644
--- a/qutebrowser/browser/hints.py
+++ b/qutebrowser/browser/hints.py
@@ -325,14 +325,18 @@ class HintActions:
cmd = context.args[0]
args = context.args[1:]
+ flags = QUrl.FullyEncoded
+
env = {
'QUTE_MODE': 'hints',
'QUTE_SELECTED_TEXT': str(elem),
'QUTE_SELECTED_HTML': elem.outer_xml(),
+ 'QUTE_CURRENT_URL':
+ context.baseurl.toString(flags), # type: ignore[arg-type]
}
+
url = elem.resolve_url(context.baseurl)
if url is not None:
- flags = QUrl.FullyEncoded
env['QUTE_URL'] = url.toString(flags) # type: ignore[arg-type]
try:
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index f9b636bde..b2ca42cbe 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -1112,7 +1112,12 @@ class _WebEngineScripts(QObject):
page_scripts = self._widget.page().scripts()
self._remove_all_greasemonkey_scripts()
+ seen_names = set()
for script in scripts:
+ while script.full_name() in seen_names:
+ script.dedup_suffix += 1
+ seen_names.add(script.full_name())
+
new_script = QWebEngineScript()
try:
@@ -1144,7 +1149,7 @@ class _WebEngineScripts(QObject):
new_script.setInjectionPoint(QWebEngineScript.DocumentReady)
new_script.setSourceCode(script.code())
- new_script.setName(f"GM-{script.name}")
+ new_script.setName(script.full_name())
new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
if script.needs_document_end_workaround():
diff --git a/qutebrowser/browser/webkit/http.py b/qutebrowser/browser/webkit/http.py
index 35cecd89a..eacb95679 100644
--- a/qutebrowser/browser/webkit/http.py
+++ b/qutebrowser/browser/webkit/http.py
@@ -64,7 +64,7 @@ class ContentDisposition:
# "duplicate ignored"), because even if we did ignore that one, it still wouldn't
# work properly...
_IGNORED_DEFECT = DefectWrapper(
- email.errors.InvalidHeaderDefect, # type: ignore[attr-defined]
+ email.errors.InvalidHeaderDefect,
'duplicate parameter name; duplicate ignored'
)
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index d12741ec4..297668a9f 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -2762,6 +2762,11 @@ colors.prompts.bg:
type: QssColor
desc: Background color for prompts.
+colors.prompts.selected.fg:
+ default: white
+ type: QssColor
+ desc: Foreground color for the selected item in filename prompts.
+
colors.prompts.selected.bg:
default: grey
type: QssColor
diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py
index 485f713d0..c3550d92f 100644
--- a/qutebrowser/mainwindow/prompt.py
+++ b/qutebrowser/mainwindow/prompt.py
@@ -268,6 +268,7 @@ class PromptContainer(QWidget):
}
QTreeView {
+ selection-color: {{ conf.colors.prompts.selected.fg }};
selection-background-color: {{ conf.colors.prompts.selected.bg }};
border: {{ conf.colors.prompts.border }};
}
@@ -278,6 +279,7 @@ class PromptContainer(QWidget):
QTreeView::item:selected, QTreeView::item:selected:hover,
QTreeView::branch:selected {
+ color: {{ conf.colors.prompts.selected.fg }};
background-color: {{ conf.colors.prompts.selected.bg }};
}
"""
diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py
index 2ca97de11..a1c6646eb 100755
--- a/scripts/dev/build_release.py
+++ b/scripts/dev/build_release.py
@@ -285,6 +285,8 @@ INFO_PLIST_UPDATES = {
'A website in qutebrowser wants to use your location information.',
'NSMicrophoneUsageDescription':
'A website in qutebrowser wants to use your microphone.',
+ 'NSBluetoothAlwaysUsageDescription':
+ 'A website in qutebrowser wants to access Bluetooth.',
}
diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py
index e8a188f6c..158741e5c 100644
--- a/scripts/dev/recompile_requirements.py
+++ b/scripts/dev/recompile_requirements.py
@@ -50,7 +50,6 @@ CHANGELOG_URLS = {
'EasyProcess': 'https://github.com/ponty/EasyProcess/commits/master',
'PyVirtualDisplay': 'https://github.com/ponty/PyVirtualDisplay/commits/master',
'execnet': 'https://execnet.readthedocs.io/en/latest/changelog.html',
- 'apipkg': 'https://github.com/pytest-dev/apipkg/blob/master/CHANGELOG',
'pytest-rerunfailures': 'https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst',
'pytest-repeat': 'https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst',
'requests': 'https://github.com/psf/requests/blob/master/HISTORY.md',
@@ -66,6 +65,8 @@ CHANGELOG_URLS = {
'glob2': 'https://github.com/miracle2k/python-glob2/blob/master/CHANGES',
'hypothesis': 'https://hypothesis.readthedocs.io/en/latest/changes.html',
'mypy': 'https://mypy-lang.blogspot.com/',
+ 'types-PyYAML': 'https://github.com/python/typeshed/commits/master/stubs/PyYAML',
+ 'types-dataclasses': 'https://github.com/python/typeshed/commits/master/stubs/dataclasses',
'pytest': 'https://docs.pytest.org/en/latest/changelog.html',
'iniconfig': 'https://github.com/RonnyPfannschmidt/iniconfig/blob/master/CHANGELOG',
'tox': 'https://tox.readthedocs.io/en/latest/changelog.html',
diff --git a/scripts/link_pyqt.py b/scripts/link_pyqt.py
index 0341de096..158cc145d 100644
--- a/scripts/link_pyqt.py
+++ b/scripts/link_pyqt.py
@@ -189,8 +189,8 @@ def get_venv_lib_path(path):
subdir = 'Scripts' if os.name == 'nt' else 'bin'
executable = os.path.join(path, subdir, 'python')
return run_py(executable,
- 'from distutils.sysconfig import get_python_lib',
- 'print(get_python_lib())')
+ 'from sysconfig import get_path',
+ 'print(get_path("platlib"))')
def get_tox_syspython(tox_path):
diff --git a/tests/end2end/features/test_history_bdd.py b/tests/end2end/features/test_history_bdd.py
index 3a44c2c11..29958c3e6 100644
--- a/tests/end2end/features/test_history_bdd.py
+++ b/tests/end2end/features/test_history_bdd.py
@@ -50,6 +50,7 @@ def check_query(quteproc, name, value):
@bdd.then(bdd.parsers.parse("the history should contain:\n{expected}"))
def check_history(quteproc, server, tmpdir, expected):
+ quteproc.wait_for(message='INSERT INTO History *', category='sql')
path = tmpdir / 'history'
quteproc.send_cmd(':debug-dump-history "{}"'.format(path))
quteproc.wait_for(category='message', loglevel=logging.INFO,
diff --git a/tests/unit/browser/webengine/test_webenginetab.py b/tests/unit/browser/webengine/test_webenginetab.py
index 274e216ba..3d8eec663 100644
--- a/tests/unit/browser/webengine/test_webenginetab.py
+++ b/tests/unit/browser/webengine/test_webenginetab.py
@@ -164,6 +164,45 @@ class TestWebengineScripts:
assert scripts_helper.get_script().injectionPoint() == expected
+ @pytest.mark.parametrize('header1, header2, expected_names', [
+ (
+ ["// @namespace ns1", "// @name same"],
+ ["// @namespace ns2", "// @name same"],
+ ['GM-ns1/same', 'GM-ns2/same'],
+ ),
+ (
+ ["// @name same"],
+ ["// @name same"],
+ ['GM-same', 'GM-same-2'],
+ ),
+ (
+ ["// @name same"],
+ ["// @name sam"],
+ ['GM-same', 'GM-sam'],
+ ),
+ ])
+ def test_greasemonkey_duplicate_name(self, scripts_helper,
+ header1, header2, expected_names):
+ template = """
+ // ==UserScript==
+ {header}
+ // ==/UserScript==
+ """
+ template = textwrap.dedent(template.lstrip('\n'))
+
+ source1 = template.format(header="\n".join(header1))
+ script1 = greasemonkey.GreasemonkeyScript.parse(source1)
+ source2 = template.format(header="\n".join(header2))
+ script2 = greasemonkey.GreasemonkeyScript.parse(source2)
+ scripts_helper.inject([script1, script2])
+
+ names = [script.name() for script in scripts_helper.get_scripts()]
+ assert names == expected_names
+
+ source3 = textwrap.dedent(template.lstrip('\n')).format(header="// @name other")
+ script3 = greasemonkey.GreasemonkeyScript.parse(source3)
+ scripts_helper.inject([script3])
+
def test_notification_permission_workaround():
"""Make sure the value for QWebEnginePage::Notifications is correct."""
diff --git a/tests/unit/javascript/test_greasemonkey.py b/tests/unit/javascript/test_greasemonkey.py
index 2bfb9ca83..17847816d 100644
--- a/tests/unit/javascript/test_greasemonkey.py
+++ b/tests/unit/javascript/test_greasemonkey.py
@@ -131,6 +131,20 @@ def test_no_name_with_fallback():
assert script.name == r"C:\COM1"
+@pytest.mark.parametrize('properties, inc_counter, expected', [
+ ([("name", "gorilla")], False, "GM-gorilla"),
+ ([("namespace", "apes"), ("name", "gorilla")], False, "GM-apes/gorilla"),
+
+ ([("name", "gorilla")], True, "GM-gorilla-2"),
+ ([("namespace", "apes"), ("name", "gorilla")], True, "GM-apes/gorilla-2"),
+])
+def test_full_name(properties, inc_counter, expected):
+ script = greasemonkey.GreasemonkeyScript(properties, code="")
+ if inc_counter:
+ script.dedup_suffix += 1
+ assert script.full_name() == expected
+
+
def test_bad_scheme(caplog):
"""qute:// isn't in the list of allowed schemes."""
_save_script("var nothing = true;\n", 'nothing.user.js')