diff options
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') |