From f2447217385dcb526aa9b7faf109d330117f096f Mon Sep 17 00:00:00 2001 From: qutebrowser bot Date: Mon, 8 Mar 2021 04:40:33 +0000 Subject: Update dependencies --- misc/requirements/requirements-flake8.txt | 2 +- misc/requirements/requirements-mypy.txt | 12 ++++++------ misc/requirements/requirements-pyinstaller.txt | 2 +- misc/requirements/requirements-pyroma.txt | 4 ++-- misc/requirements/requirements-sphinx.txt | 4 ++-- misc/requirements/requirements-tests.txt | 6 +++--- misc/requirements/requirements-tox.txt | 4 ++-- requirements.txt | 8 ++++---- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 493fa3cac..c83b57860 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -2,7 +2,7 @@ attrs==20.3.0 flake8==3.8.4 -flake8-bugbear==20.11.1 +flake8-bugbear==21.3.1 flake8-builtins==1.5.3 flake8-comprehensions==3.3.1 flake8-copyright==0.2.2 diff --git a/misc/requirements/requirements-mypy.txt b/misc/requirements/requirements-mypy.txt index 070339ed6..dfa80656b 100644 --- a/misc/requirements/requirements-mypy.txt +++ b/misc/requirements/requirements-mypy.txt @@ -1,10 +1,10 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py chardet==4.0.0 -diff-cover==4.2.1 -importlib-metadata==3.7.0 -importlib-resources==5.1.1 -inflect==5.2.0 +diff-cover==4.2.3 +importlib-metadata==3.7.2 +importlib-resources==5.1.2 +inflect==3.0.2 Jinja2==2.11.3 jinja2-pluralize==0.3.0 lxml==4.6.2 @@ -12,8 +12,8 @@ MarkupSafe==1.1.1 mypy==0.812 mypy-extensions==0.4.3 pluggy==0.13.1 -Pygments==2.8.0 +Pygments==2.8.1 PyQt5-stubs==5.15.2.0 typed-ast==1.4.2 typing-extensions==3.7.4.3 -zipp==3.4.0 +zipp==3.4.1 diff --git a/misc/requirements/requirements-pyinstaller.txt b/misc/requirements/requirements-pyinstaller.txt index 05a59200f..5b7c0137a 100644 --- a/misc/requirements/requirements-pyinstaller.txt +++ b/misc/requirements/requirements-pyinstaller.txt @@ -2,4 +2,4 @@ altgraph==0.17 pyinstaller==4.2 -pyinstaller-hooks-contrib==2020.11 +pyinstaller-hooks-contrib==2021.1 diff --git a/misc/requirements/requirements-pyroma.txt b/misc/requirements/requirements-pyroma.txt index 22a195e66..b64b99e24 100644 --- a/misc/requirements/requirements-pyroma.txt +++ b/misc/requirements/requirements-pyroma.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py docutils==0.16 -Pygments==2.5.2 -pyroma==2.6.1 +Pygments==2.8.1 +pyroma==3.1 diff --git a/misc/requirements/requirements-sphinx.txt b/misc/requirements/requirements-sphinx.txt index 495b8dcf5..352be342a 100644 --- a/misc/requirements/requirements-sphinx.txt +++ b/misc/requirements/requirements-sphinx.txt @@ -10,12 +10,12 @@ imagesize==1.2.0 Jinja2==2.11.3 MarkupSafe==1.1.1 packaging==20.9 -Pygments==2.8.0 +Pygments==2.8.1 pyparsing==2.4.7 pytz==2021.1 requests==2.25.1 snowballstemmer==2.1.0 -Sphinx==3.5.1 +Sphinx==3.5.2 sphinxcontrib-applehelp==1.0.2 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==1.0.3 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index bf214be0d..2bfaf91e0 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -15,7 +15,7 @@ filelock==3.0.12 Flask==1.1.2 glob2==0.7 hunter==3.3.1 -hypothesis==6.3.4 +hypothesis==6.6.0 icdiff==1.9.1 idna==2.10 iniconfig==1.1.1 @@ -33,7 +33,7 @@ pluggy==0.13.1 pprintpp==0.4.0 py==1.10.0 py-cpuinfo==7.0.0 -Pygments==2.8.0 +Pygments==2.8.1 pyparsing==2.4.7 pytest==6.2.2 pytest-bdd==4.0.2 @@ -48,7 +48,7 @@ pytest-repeat==0.9.1 pytest-rerunfailures==9.1.1 pytest-xdist==2.2.1 pytest-xvfb==2.0.0 -PyVirtualDisplay==2.0 +PyVirtualDisplay==2.1 requests==2.25.1 requests-file==1.5.1 six==1.15.0 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index 1e6382e1e..d44522118 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -8,9 +8,9 @@ pip==21.0.1 pluggy==0.13.1 py==1.10.0 pyparsing==2.4.7 -setuptools==54.0.0 +setuptools==54.1.1 six==1.15.0 toml==0.10.2 -tox==3.22.0 +tox==3.23.0 virtualenv==20.4.2 wheel==0.36.2 diff --git a/requirements.txt b/requirements.txt index c6eb86d6f..5572e206c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,11 +3,11 @@ adblock==0.4.2 ; python_version!="3.10" colorama==0.4.4 dataclasses==0.6 ; python_version<"3.7" -importlib-metadata==3.7.0 ; python_version<"3.8" -importlib-resources==5.1.1 ; python_version<"3.9" +importlib-metadata==3.7.2 ; python_version<"3.8" +importlib-resources==5.1.2 ; python_version<"3.9" Jinja2==2.11.3 MarkupSafe==1.1.1 -Pygments==2.8.0 +Pygments==2.8.1 PyYAML==5.4.1 typing-extensions==3.7.4.3 -zipp==3.4.0 +zipp==3.4.1 -- cgit v1.2.3-54-g00ecf From 45387d18a514103c686be559087420199cb49995 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 11:26:39 +0100 Subject: Fix some tests with non-english locales See #6234 --- tests/end2end/features/misc.feature | 2 +- tests/end2end/features/qutescheme.feature | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 351135fab..5d81a890a 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -140,7 +140,7 @@ Feature: Various utility commands. Scenario: :jseval --file using a file that doesn't exist as js-code When I run :jseval --file /nonexistentfile - Then the error "[Errno 2] No such file or directory: '/nonexistentfile'" should be shown + Then the error "[Errno 2] *: '/nonexistentfile'" should be shown And "No output or error" should not be logged # :debug-webaction diff --git a/tests/end2end/features/qutescheme.feature b/tests/end2end/features/qutescheme.feature index 286f8f80a..1424bbf09 100644 --- a/tests/end2end/features/qutescheme.feature +++ b/tests/end2end/features/qutescheme.feature @@ -215,7 +215,7 @@ Feature: Special qute:// pages Scenario: Running :pyeval --file using a non existing file When I run :debug-pyeval --file nonexistentfile - Then the error "[Errno 2] No such file or directory: 'nonexistentfile'" should be shown + Then the error "[Errno 2] *: 'nonexistentfile'" should be shown Scenario: Running :pyeval with --quiet When I run :debug-pyeval --quiet 1+1 -- cgit v1.2.3-54-g00ecf From 76b03db20eac1d9b52156191656d4c2adb290911 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 14:04:56 +0100 Subject: Revert "ci: Add workaround for Archlinux/Docker issue" This reverts commit 478e4de7bd1f26bebdcdc166d5369b2b5142c3e2. Fixed according to https://github.com/actions/virtual-environments/issues/2658 --- scripts/dev/ci/docker/Dockerfile.j2 | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scripts/dev/ci/docker/Dockerfile.j2 b/scripts/dev/ci/docker/Dockerfile.j2 index 03e5684ad..d3fc82793 100644 --- a/scripts/dev/ci/docker/Dockerfile.j2 +++ b/scripts/dev/ci/docker/Dockerfile.j2 @@ -1,12 +1,5 @@ FROM archlinux:latest -# WORKAROUND for glibc 2.33 and old Docker -# See https://github.com/actions/virtual-environments/issues/2658 -# Thanks to https://github.com/lxqt/lxqt-panel/pull/1562 -RUN patched_glibc=glibc-linux4-2.33-4-x86_64.pkg.tar.zst && \ - curl -LO "https://repo.archlinuxcn.org/x86_64/$patched_glibc" && \ - bsdtar -C / -xvf "$patched_glibc" - {% if unstable %} RUN sed -i '/^# after the header/a[kde-unstable]\nInclude = /etc/pacman.d/mirrorlist\n\n[testing]\nInclude = /etc/pacman.d/mirrorlist' /etc/pacman.conf {% endif %} -- cgit v1.2.3-54-g00ecf From 55fdae8eed0f93152b5676cca77b6243fd95d9b0 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Mon, 8 Mar 2021 17:40:57 +0100 Subject: userscripts/readability: add unique body class Until per-domains stylesheets are implemented, there is no way to apply style to the readability page only. With this patch, you can just use the global setting `content.user_stylesheets` by writing a more specific CSS selector. For example: body.qute-readability { font-family: Libertinus; font-size: 1.2em; text-align: justify; } will change the font and text alignment of the readability page, without altering the style of other websites. --- misc/userscripts/readability | 3 +++ misc/userscripts/readability-js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/misc/userscripts/readability b/misc/userscripts/readability index f9cbbf829..a6a6f2d52 100755 --- a/misc/userscripts/readability +++ b/misc/userscripts/readability @@ -57,6 +57,9 @@ with codecs.open(os.environ['QUTE_HTML'], 'r', 'utf-8') as source: title = doc.title() content = doc.summary().replace('', HEADER % title) + # add a class to make styling the page easier + content = content.replace('', '') + with codecs.open(tmpfile, 'w', 'utf-8') as target: target.write(content.lstrip()) diff --git a/misc/userscripts/readability-js b/misc/userscripts/readability-js index 2f24e065d..532df51c6 100755 --- a/misc/userscripts/readability-js +++ b/misc/userscripts/readability-js @@ -131,6 +131,9 @@ getDOM(target, domOpts).then(dom => { let article = reader.parse(); let content = util.format(HEADER, article.title) + article.content; + // add a class to make styling the page easier + content = content.replace('', '') + fs.writeFile(tmpFile, content, (err) => { if (err) { qute.messageError([`"${err}"`]) -- cgit v1.2.3-54-g00ecf From 163517800365ebc48c4947b3ad4ddd538ca8f31f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 18:06:20 +0100 Subject: Update changelog --- doc/changelog.asciidoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 3840f369d..c78fe556a 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -60,6 +60,9 @@ Changed - The `fileselect.*.command` settings now support file selectors writing the selected paths to stdout, which is used if no `{}` placeholder is contained in the configured command. +- The `readability` and `readability-js` userscripts now add a + `qute-readability` CSS class to the page, so that it can be styled easily via + a user stylesheet. Fixed ~~~~~ -- cgit v1.2.3-54-g00ecf From 0fb5352cdf0f220d2cce34f84bf96cfbbf541a30 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Mar 2021 20:51:32 +0100 Subject: Simplify _inject_greasemonkey_scripts The arguments aren't needed anymore since 05111e84236621d3923ed4efd36a7c6578407c20 and we can use f-strings nowadays. --- qutebrowser/browser/webengine/webenginetab.py | 47 ++++++++------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 4092fbe40..439d99570 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -1080,18 +1080,11 @@ class _WebEngineScripts(QObject): removed = page_scripts.remove(script) assert removed, script.name() - def _inject_greasemonkey_scripts(self, scripts=None, injection_point=None, - remove_first=True): + def _inject_greasemonkey_scripts(self, scripts): """Register user JavaScript files with the current tab. Args: - scripts: A list of GreasemonkeyScripts, or None to add all - known by the Greasemonkey subsystem. - injection_point: The QWebEngineScript::InjectionPoint stage - to inject the script into, None to use - auto-detection. - remove_first: Whether to remove all previously injected - scripts before adding these ones. + scripts: A list of GreasemonkeyScripts. """ if sip.isdeleted(self._widget): return @@ -1102,11 +1095,7 @@ class _WebEngineScripts(QObject): # While, taking care not to remove any other scripts that might # have been added elsewhere, like the one for stylesheets. page_scripts = self._widget.page().scripts() - if remove_first: - self._remove_all_greasemonkey_scripts() - - if not scripts: - return + self._remove_all_greasemonkey_scripts() for script in scripts: new_script = QWebEngineScript() @@ -1114,37 +1103,29 @@ class _WebEngineScripts(QObject): world = int(script.jsworld) if not 0 <= world <= qtutils.MAX_WORLD_ID: log.greasemonkey.error( - "script {} has invalid value for '@qute-js-world'" - ": {}, should be between 0 and {}" - .format( - script.name, - script.jsworld, - qtutils.MAX_WORLD_ID)) + f"script {script.name} has invalid value for '@qute-js-world'" + f": {script.jsworld}, should be between 0 and " + f"{qtutils.MAX_WORLD_ID}") continue except ValueError: try: - world = _JS_WORLD_MAP[usertypes.JsWorld[ - script.jsworld.lower()]] + world = _JS_WORLD_MAP[usertypes.JsWorld[script.jsworld.lower()]] except KeyError: log.greasemonkey.error( - "script {} has invalid value for '@qute-js-world'" - ": {}".format(script.name, script.jsworld)) + f"script {script.name} has invalid value for '@qute-js-world'" + f": {script.jsworld}") continue new_script.setWorldId(world) new_script.setSourceCode(script.code()) - new_script.setName("GM-{}".format(script.name)) + new_script.setName(f"GM-{script.name}") new_script.setRunsOnSubFrames(script.runs_on_sub_frames) - # Override the @run-at value parsed by QWebEngineScript if desired. - if injection_point: - new_script.setInjectionPoint(injection_point) - elif script.needs_document_end_workaround(): - log.greasemonkey.debug("Forcing @run-at document-end for {}" - .format(script.name)) + if script.needs_document_end_workaround(): + log.greasemonkey.debug( + f"Forcing @run-at document-end for {script.name}") new_script.setInjectionPoint(QWebEngineScript.DocumentReady) - log.greasemonkey.debug('adding script: {}' - .format(new_script.name())) + log.greasemonkey.debug(f'adding script: {new_script.name()}') page_scripts.insert(new_script) def _inject_site_specific_quirks(self): -- cgit v1.2.3-54-g00ecf From 321a350b8fd9206a8677902b30327790698e151e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 9 Mar 2021 17:28:43 +0100 Subject: Add renderer process status to all error messages Might help debug issues such as #6235 --- qutebrowser/mainwindow/tabbedbrowser.py | 14 +++++--------- tests/end2end/features/misc.feature | 10 +++++----- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 52241d777..8d2801d31 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -929,16 +929,12 @@ class TabbedBrowser(QWidget): return messages = { - browsertab.TerminationStatus.abnormal: - "Renderer process exited with status {}".format(code), - browsertab.TerminationStatus.crashed: - "Renderer process crashed", - browsertab.TerminationStatus.killed: - "Renderer process was killed", - browsertab.TerminationStatus.unknown: - "Renderer process did not start", + browsertab.TerminationStatus.abnormal: "Renderer process exited", + browsertab.TerminationStatus.crashed: "Renderer process crashed", + browsertab.TerminationStatus.killed: "Renderer process was killed", + browsertab.TerminationStatus.unknown: "Renderer process did not start", } - msg = messages[status] + msg = messages[status] + f" (status {code})" def show_error_page(html): tab.set_html(html) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 5d81a890a..e6a02e038 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -528,13 +528,13 @@ Feature: Various utility commands. @qtwebkit_skip @no_invalid_lines @posix Scenario: Renderer crash When I run :open -t chrome://crash - Then "Renderer process crashed" should be logged + Then "Renderer process crashed (status *)" should be logged And "* 'Error loading chrome://crash/'" should be logged @qtwebkit_skip @no_invalid_lines @flaky Scenario: Renderer kill When I run :open -t chrome://kill - Then "Renderer process was killed" should be logged + Then "Renderer process was killed (status *)" should be logged And "* 'Error loading chrome://kill/'" should be logged # https://github.com/qutebrowser/qutebrowser/issues/2290 @@ -544,7 +544,7 @@ Feature: Various utility commands. And I open data/numbers/1.txt And I open data/numbers/2.txt in a new tab And I run :open chrome://kill - And I wait for "Renderer process was killed" in the log + And I wait for "Renderer process was killed (status *)" in the log And I open data/numbers/3.txt Then no crash should happen @@ -554,11 +554,11 @@ Feature: Various utility commands. When I open data/crashers/webrtc.html in a new tab And I run :reload And I wait until data/crashers/webrtc.html is loaded - Then "Renderer process crashed" should not be logged + Then "Renderer process crashed (status *)" should not be logged Scenario: InstalledApps crash When I open data/crashers/installedapp.html in a new tab - Then "Renderer process was killed" should not be logged + Then "Renderer process was killed (status *)" should not be logged ## Other -- cgit v1.2.3-54-g00ecf From c4144a6e4b524631cad3113617ab64a909fd0503 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 9 Mar 2021 17:38:48 +0100 Subject: Add log-sensitive-keys debug flag --- doc/changelog.asciidoc | 3 +++ qutebrowser/keyinput/modeman.py | 11 ++++++----- qutebrowser/qutebrowser.py | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index c78fe556a..93a85866b 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -60,6 +60,9 @@ Changed - The `fileselect.*.command` settings now support file selectors writing the selected paths to stdout, which is used if no `{}` placeholder is contained in the configured command. +- The `--debug-flag` argument now understands a new `log-sensitive-keys` value + which logs all keypresses (including those in insert/passthrough/prompt/... + mode) for debugging. - The `readability` and `readability-js` userscripts now add a `qute-readability` CSS class to the page, so that it can be styled easily via a user stylesheet. diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 08c5a151b..c00120596 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -86,9 +86,10 @@ def init(win_id: int, parent: QObject) -> 'ModeManager': hintmanager = hints.HintManager(win_id, parent=parent) objreg.register('hintmanager', hintmanager, scope='window', window=win_id, command_only=True) - modeman.hintmanager = hintmanager + log_sensitive_keys = 'log-sensitive-keys' in objects.debug_flags + keyparsers: ParserDictType = { usertypes.KeyMode.normal: modeparsers.NormalKeyParser( @@ -110,7 +111,7 @@ def init(win_id: int, parent: QObject) -> 'ModeManager': commandrunner=commandrunner, parent=modeman, passthrough=True, - do_log=False, + do_log=log_sensitive_keys, supports_count=False), usertypes.KeyMode.passthrough: @@ -120,7 +121,7 @@ def init(win_id: int, parent: QObject) -> 'ModeManager': commandrunner=commandrunner, parent=modeman, passthrough=True, - do_log=False, + do_log=log_sensitive_keys, supports_count=False), usertypes.KeyMode.command: @@ -130,7 +131,7 @@ def init(win_id: int, parent: QObject) -> 'ModeManager': commandrunner=commandrunner, parent=modeman, passthrough=True, - do_log=False, + do_log=log_sensitive_keys, supports_count=False), usertypes.KeyMode.prompt: @@ -140,7 +141,7 @@ def init(win_id: int, parent: QObject) -> 'ModeManager': commandrunner=commandrunner, parent=modeman, passthrough=True, - do_log=False, + do_log=log_sensitive_keys, supports_count=False), usertypes.KeyMode.yesno: diff --git a/qutebrowser/qutebrowser.py b/qutebrowser/qutebrowser.py index 64c175293..9e1fb91cd 100644 --- a/qutebrowser/qutebrowser.py +++ b/qutebrowser/qutebrowser.py @@ -173,6 +173,7 @@ def debug_flag_error(flag): log-requests: Log all network requests. log-cookies: Log cookies in cookie filter. log-scroll-pos: Log all scrolling changes. + log-sensitive-keys: Log keypresses in passthrough modes. stack: Enable Chromium stack logging. chromium: Enable Chromium logging. wait-renderer-process: Wait for debugger in renderer process. @@ -181,7 +182,7 @@ def debug_flag_error(flag): """ valid_flags = ['debug-exit', 'pdb-postmortem', 'no-sql-history', 'no-scroll-filtering', 'log-requests', 'log-cookies', - 'log-scroll-pos', 'stack', 'chromium', + 'log-scroll-pos', 'log-sensitive-keys', 'stack', 'chromium', 'wait-renderer-process', 'avoid-chromium-init', 'werror'] if flag in valid_flags: -- cgit v1.2.3-54-g00ecf From 7eae8f069406bb72fa4a663c22a4c04245d750f2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 9 Mar 2021 18:10:51 +0100 Subject: Fix @run-at default for GreaseMonkey --- doc/changelog.asciidoc | 4 +++ qutebrowser/browser/webengine/webenginetab.py | 12 ++++++++ tests/unit/browser/webengine/test_webenginetab.py | 34 +++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 93a85866b..d57698df7 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -93,6 +93,10 @@ Fixed properly. - The "try again" button on error pages now works correctly with JavaScript disabled. +- If a GreaseMonkey script doesn't have a "@run-at" comment, qutebrowser + accidentally treated that as "@run-at document-idle". However, other + GreaseMonkey implementations default to "@run-at document-end" instead, which + is what qutebrowser now does, too. [[v2.0.2]] v2.0.2 (2021-02-04) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 439d99570..450d68751 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -1099,6 +1099,7 @@ class _WebEngineScripts(QObject): for script in scripts: new_script = QWebEngineScript() + try: world = int(script.jsworld) if not 0 <= world <= qtutils.MAX_WORLD_ID: @@ -1116,6 +1117,17 @@ class _WebEngineScripts(QObject): f": {script.jsworld}") continue new_script.setWorldId(world) + + # Corresponds to "@run-at document-end" which is the default according to + # https://wiki.greasespot.net/Metadata_Block#.40run-at - however, + # QtWebEngine uses QWebEngineScript.Deferred (@run-at document-idle) as + # default. + # + # NOTE that this needs to be done before setSourceCode, so that + # QtWebEngine's parsing of GreaseMonkey tags will override it if there is a + # @run-at comment. + new_script.setInjectionPoint(QWebEngineScript.DocumentReady) + new_script.setSourceCode(script.code()) new_script.setName(f"GM-{script.name}") new_script.setRunsOnSubFrames(script.runs_on_sub_frames) diff --git a/tests/unit/browser/webengine/test_webenginetab.py b/tests/unit/browser/webengine/test_webenginetab.py index 7827c379b..2c792f80e 100644 --- a/tests/unit/browser/webengine/test_webenginetab.py +++ b/tests/unit/browser/webengine/test_webenginetab.py @@ -20,6 +20,7 @@ """Test webenginetab.""" import logging +import textwrap import pytest QtWebEngineWidgets = pytest.importorskip("PyQt5.QtWebEngineWidgets") @@ -116,6 +117,39 @@ class TestWebengineScripts: script = collection.toList()[-1] assert script.injectionPoint() == QWebEngineScript.DocumentReady + @pytest.mark.parametrize('run_at, expected', [ + # UserScript::DocumentElementCreation + ('document-start', QWebEngineScript.DocumentCreation), + # UserScript::DocumentLoadFinished + ('document-end', QWebEngineScript.DocumentReady), + # UserScript::AfterLoad + ('document-idle', QWebEngineScript.Deferred), + # default according to https://wiki.greasespot.net/Metadata_Block#.40run-at + (None, QWebEngineScript.DocumentReady), + ]) + def test_run_at_values(self, webengine_scripts, run_at, expected): + if run_at is None: + script = """ + // ==UserScript== + // @name qutebrowser test userscript + // ==/UserScript== + """ + else: + script = f""" + // ==UserScript== + // @name qutebrowser test userscript + // @run-at {run_at} + // ==/UserScript== + """ + + script = textwrap.dedent(script.lstrip('\n')) + scripts = [greasemonkey.GreasemonkeyScript.parse(script)] + webengine_scripts._inject_greasemonkey_scripts(scripts) + + collection = webengine_scripts._widget.page().scripts() + script = collection.toList()[-1] + assert script.injectionPoint() == expected + def test_notification_permission_workaround(): """Make sure the value for QWebEnginePage::Notifications is correct.""" -- cgit v1.2.3-54-g00ecf From fb4a56318c8159551d54f7098324ea908275854f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 9 Mar 2021 18:23:10 +0100 Subject: Simplify TestWebengineScripts --- tests/unit/browser/webengine/test_webenginetab.py | 70 ++++++++++++++--------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/tests/unit/browser/webengine/test_webenginetab.py b/tests/unit/browser/webengine/test_webenginetab.py index 2c792f80e..156f7d26f 100644 --- a/tests/unit/browser/webengine/test_webenginetab.py +++ b/tests/unit/browser/webengine/test_webenginetab.py @@ -36,15 +36,38 @@ webenginetab = pytest.importorskip( pytestmark = pytest.mark.usefixtures('greasemonkey_manager') +class ScriptsHelper: + + """Helper to get the processed (usually Greasemonkey) scripts.""" + + def __init__(self, tab): + self._tab = tab + + def get_scripts(self, prefix='GM-'): + return [ + s for s in self._tab._widget.page().scripts().toList() + if s.name().startswith(prefix) + ] + + def get_script(self): + scripts = self.get_scripts() + assert len(scripts) == 1 + return scripts[0] + + def inject(self, scripts): + self._tab._scripts._inject_greasemonkey_scripts(scripts) + return self.get_scripts() + + class TestWebengineScripts: """Test the _WebEngineScripts utility class.""" @pytest.fixture - def webengine_scripts(self, webengine_tab): - return webengine_tab._scripts + def scripts_helper(self, webengine_tab): + return ScriptsHelper(webengine_tab) - def test_greasemonkey_undefined_world(self, webengine_scripts, caplog): + def test_greasemonkey_undefined_world(self, scripts_helper, caplog): """Make sure scripts with non-existent worlds are rejected.""" scripts = [ greasemonkey.GreasemonkeyScript( @@ -52,18 +75,16 @@ class TestWebengineScripts: ] with caplog.at_level(logging.ERROR, 'greasemonkey'): - webengine_scripts._inject_greasemonkey_scripts(scripts) + injected = scripts_helper.inject(scripts) assert len(caplog.records) == 1 msg = caplog.messages[0] assert "has invalid value for '@qute-js-world': Mars" in msg - collection = webengine_scripts._widget.page().scripts().toList() - assert not any(script.name().startswith('GM-') - for script in collection) + + assert not injected @pytest.mark.parametrize("worldid", [-1, 257]) - def test_greasemonkey_out_of_range_world(self, worldid, webengine_scripts, - caplog): + def test_greasemonkey_out_of_range_world(self, worldid, scripts_helper, caplog): """Make sure scripts with out-of-range worlds are rejected.""" scripts = [ greasemonkey.GreasemonkeyScript( @@ -71,19 +92,18 @@ class TestWebengineScripts: ] with caplog.at_level(logging.ERROR, 'greasemonkey'): - webengine_scripts._inject_greasemonkey_scripts(scripts) + injected = scripts_helper.inject(scripts) assert len(caplog.records) == 1 msg = caplog.messages[0] assert "has invalid value for '@qute-js-world': " in msg assert "should be between 0 and" in msg - collection = webengine_scripts._widget.page().scripts().toList() - assert not any(script.name().startswith('GM-') - for script in collection) + + assert not injected @pytest.mark.parametrize("worldid", [0, 10]) def test_greasemonkey_good_worlds_are_passed(self, worldid, - webengine_scripts, caplog): + scripts_helper, caplog): """Make sure scripts with valid worlds have it set.""" scripts = [ greasemonkey.GreasemonkeyScript( @@ -92,13 +112,11 @@ class TestWebengineScripts: ] with caplog.at_level(logging.ERROR, 'greasemonkey'): - webengine_scripts._inject_greasemonkey_scripts(scripts) + scripts_helper.inject(scripts) - collection = webengine_scripts._widget.page().scripts() - assert collection.toList()[-1].worldId() == worldid + assert scripts_helper.get_script().worldId() == worldid - def test_greasemonkey_document_end_workaround(self, monkeypatch, - webengine_scripts): + def test_greasemonkey_document_end_workaround(self, monkeypatch, scripts_helper): """Make sure document-end is forced when needed.""" monkeypatch.setattr(greasemonkey.objects, 'backend', usertypes.Backend.QtWebEngine) @@ -110,11 +128,9 @@ class TestWebengineScripts: ('run-at', 'document-start'), ], None) ] + scripts_helper.inject(scripts) - webengine_scripts._inject_greasemonkey_scripts(scripts) - - collection = webengine_scripts._widget.page().scripts() - script = collection.toList()[-1] + script = scripts_helper.get_script() assert script.injectionPoint() == QWebEngineScript.DocumentReady @pytest.mark.parametrize('run_at, expected', [ @@ -127,7 +143,7 @@ class TestWebengineScripts: # default according to https://wiki.greasespot.net/Metadata_Block#.40run-at (None, QWebEngineScript.DocumentReady), ]) - def test_run_at_values(self, webengine_scripts, run_at, expected): + def test_greasemonkey_run_at_values(self, scripts_helper, run_at, expected): if run_at is None: script = """ // ==UserScript== @@ -144,11 +160,9 @@ class TestWebengineScripts: script = textwrap.dedent(script.lstrip('\n')) scripts = [greasemonkey.GreasemonkeyScript.parse(script)] - webengine_scripts._inject_greasemonkey_scripts(scripts) + scripts_helper.inject(scripts) - collection = webengine_scripts._widget.page().scripts() - script = collection.toList()[-1] - assert script.injectionPoint() == expected + assert scripts_helper.get_script().injectionPoint() == expected def test_notification_permission_workaround(): -- cgit v1.2.3-54-g00ecf From 0a38fff4c675b384289728a4d45f9c1fe1f9d4fc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 9 Mar 2021 18:42:19 +0100 Subject: Simplify test_greasemonkey via js_tester fixture --- pytest.ini | 1 - tests/conftest.py | 6 - tests/unit/javascript/conftest.py | 7 +- tests/unit/javascript/test_greasemonkey.py | 189 ++++++++++++----------------- 4 files changed, 80 insertions(+), 123 deletions(-) diff --git a/pytest.ini b/pytest.ini index d0f41948b..7f4a58de3 100644 --- a/pytest.ini +++ b/pytest.ini @@ -34,7 +34,6 @@ markers = no_invalid_lines: Don't fail on unparsable lines in end2end tests fake_os: Fake utils.is_* to a fake operating system unicode_locale: Tests which need a unicode locale to work - qtwebkit6021_xfail: Tests which would fail on WebKit version 602.1 js_headers: Sets JS headers dynamically on QtWebEngine (unsupported on some versions) qtwebkit_pdf_imageformat_skip: Broken on QtWebKit with PDF image format plugin installed windows_skip: Tests which should be skipped on Windows diff --git a/tests/conftest.py b/tests/conftest.py index ea7381a2f..ee945ac4c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -109,12 +109,6 @@ def _apply_platform_markers(config, item): pytest.mark.skipif, sys.getfilesystemencoding() == 'ascii', "Skipped because of ASCII locale"), - - ('qtwebkit6021_xfail', - pytest.mark.xfail, - version.qWebKitVersion and # type: ignore[unreachable] - version.qWebKitVersion() == '602.1', - "Broken on WebKit 602.1") ] for searched_marker, new_marker_kind, condition, default_reason in markers: diff --git a/tests/unit/javascript/conftest.py b/tests/unit/javascript/conftest.py index 47884687d..85d5ebe0a 100644 --- a/tests/unit/javascript/conftest.py +++ b/tests/unit/javascript/conftest.py @@ -28,6 +28,7 @@ import jinja2 from PyQt5.QtCore import QUrl import qutebrowser +from qutebrowser.utils import usertypes class JSTester: @@ -113,7 +114,7 @@ class JSTester: source = f.read() self.run(source, expected) - def run(self, source: str, expected, world=None) -> None: + def run(self, source: str, expected=usertypes.UNSET, world=None) -> None: """Run the given javascript source. Args: @@ -123,7 +124,9 @@ class JSTester: """ with self.qtbot.wait_callback() as callback: self.tab.run_js_async(source, callback, world=world) - callback.assert_called_with(expected) + + if expected is not usertypes.UNSET: + callback.assert_called_with(expected) @pytest.fixture diff --git a/tests/unit/javascript/test_greasemonkey.py b/tests/unit/javascript/test_greasemonkey.py index c28b9c8f7..3a3ea0294 100644 --- a/tests/unit/javascript/test_greasemonkey.py +++ b/tests/unit/javascript/test_greasemonkey.py @@ -25,7 +25,7 @@ import pytest import py.path # pylint: disable=no-name-in-module from PyQt5.QtCore import QUrl -from qutebrowser.utils import usertypes +from qutebrowser.utils import usertypes, version from qutebrowser.browser import greasemonkey from qutebrowser.misc import objects @@ -77,8 +77,7 @@ def test_get_scripts_by_url(url, expected_matches): gm_manager = greasemonkey.GreasemonkeyManager() scripts = gm_manager.scripts_for(QUrl(url)) - assert (len(scripts.start + scripts.end + scripts.idle) == - expected_matches) + assert len(scripts.start + scripts.end + scripts.idle) == expected_matches @pytest.mark.parametrize("url, expected_matches", [ @@ -102,8 +101,7 @@ def test_regex_includes_scripts_for(url, expected_matches): gm_manager = greasemonkey.GreasemonkeyManager() scripts = gm_manager.scripts_for(QUrl(url)) - assert (len(scripts.start + scripts.end + scripts.idle) == - expected_matches) + assert len(scripts.start + scripts.end + scripts.idle) == expected_matches def test_no_metadata(caplog): @@ -229,124 +227,87 @@ def test_required_scripts_are_included(download_stub, tmpdir): assert scripts[0].excludes -class TestWindowIsolation: +def test_window_isolation(js_tester, request): """Check that greasemonkey scripts get a shadowed global scope.""" + # Change something in the global scope + setup_script = "window.$ = 'global'" - @pytest.fixture - def setup(self): - # pylint: disable=attribute-defined-outside-init - class SetupData: - pass - ret = SetupData() - - # Change something in the global scope - ret.setup_script = "window.$ = 'global'" - - # Greasemonkey script to report back on its scope. - test_script = greasemonkey.GreasemonkeyScript.parse( - textwrap.dedent(""" - // ==UserScript== - // @name scopetest - // ==/UserScript== - // Check the thing the page set is set to the expected type - result.push(window.$); - result.push($); - // Now overwrite it - window.$ = 'shadowed'; - // And check everything is how the script would expect it to be - // after just writing to the "global" scope - result.push(window.$); - result.push($); - """) - ) - - # The compiled source of that scripts with some additional setup - # bookending it. - ret.test_script = "\n".join([ - """ - const result = []; - """, - test_script.code(), - """ - // Now check that the actual global scope has - // not been overwritten + # Greasemonkey script to report back on its scope. + test_gm_script = greasemonkey.GreasemonkeyScript.parse( + textwrap.dedent(""" + // ==UserScript== + // @name scopetest + // ==/UserScript== + // Check the thing the page set is set to the expected type + result.push(window.$); + result.push($); + // Now overwrite it + window.$ = 'shadowed'; + // And check everything is how the script would expect it to be + // after just writing to the "global" scope result.push(window.$); result.push($); - // And return our findings - result; - """ - ]) + """) + ) + + # The compiled source of that scripts with some additional setup + # bookending it. + test_script = "\n".join([ + """ + const result = []; + """, + test_gm_script.code(), + """ + // Now check that the actual global scope has + // not been overwritten + result.push(window.$); + result.push($); + // And return our findings + result; + """ + ]) - # What we expect the script to report back. - ret.expected = ["global", "global", - "shadowed", "shadowed", - "global", "global"] - return ret + # What we expect the script to report back. + expected = ["global", "global", "shadowed", "shadowed", "global", "global"] - def test_webengine(self, qtbot, webengineview, setup): - page = webengineview.page() - page.runJavaScript(setup.setup_script) + # The JSCore in 602.1 doesn't fully support Proxy. + xfail = False + if (js_tester.tab.backend == usertypes.Backend.QtWebKit and + version.qWebKitVersion() == '602.1'): + expected[-1] = 'shadowed' + expected[-2] = 'shadowed' + xfail = True - with qtbot.wait_callback() as callback: - page.runJavaScript(setup.test_script, callback) - callback.assert_called_with(setup.expected) + js_tester.run(setup_script) + js_tester.run(test_script, expected=expected) - # The JSCore in 602.1 doesn't fully support Proxy. - @pytest.mark.qtwebkit6021_xfail - def test_webkit(self, webview, setup): - elem = webview.page().mainFrame().documentElement() - elem.evaluateJavaScript(setup.setup_script) - result = elem.evaluateJavaScript(setup.test_script) - assert result == setup.expected + if xfail: + pytest.xfail("Broken on WebKit 602.1") -class TestSharedWindowProxy: +def test_shared_window_proxy(js_tester): """Check that all scripts have access to the same window proxy.""" + # Greasemonkey script to add a property to the window proxy. + test_script_a = greasemonkey.GreasemonkeyScript.parse( + textwrap.dedent(""" + // ==UserScript== + // @name a + // ==/UserScript== + // Set a value from script a + window.$ = 'test'; + """) + ).code() + + # Greasemonkey script to retrieve a property from the window proxy. + test_script_b = greasemonkey.GreasemonkeyScript.parse( + textwrap.dedent(""" + // ==UserScript== + // @name b + // ==/UserScript== + // Check that the value is accessible from script b + return [window.$, $]; + """) + ).code() - @pytest.fixture - def setup(self): - # pylint: disable=attribute-defined-outside-init - class SetupData: - pass - ret = SetupData() - - # Greasemonkey script to add a property to the window proxy. - ret.test_script_a = greasemonkey.GreasemonkeyScript.parse( - textwrap.dedent(""" - // ==UserScript== - // @name a - // ==/UserScript== - // Set a value from script a - window.$ = 'test'; - """) - ).code() - - # Greasemonkey script to retrieve a property from the window proxy. - ret.test_script_b = greasemonkey.GreasemonkeyScript.parse( - textwrap.dedent(""" - // ==UserScript== - // @name b - // ==/UserScript== - // Check that the value is accessible from script b - return [window.$, $]; - """) - ).code() - - # What we expect the script to report back. - ret.expected = ["test", "test"] - return ret - - def test_webengine(self, qtbot, webengineview, setup): - page = webengineview.page() - - with qtbot.wait_callback() as callback: - page.runJavaScript(setup.test_script_a, callback) - with qtbot.wait_callback() as callback: - page.runJavaScript(setup.test_script_b, callback) - callback.assert_called_with(setup.expected) - - def test_webkit(self, webview, setup): - elem = webview.page().mainFrame().documentElement() - elem.evaluateJavaScript(setup.test_script_a) - result = elem.evaluateJavaScript(setup.test_script_b) - assert result == setup.expected + js_tester.run(test_script_a) + js_tester.run(test_script_b, expected=["test", "test"]) -- cgit v1.2.3-54-g00ecf