Merge branch 'master' into Add-utils/resources.py

This commit is contained in:
Lembrun 2021-03-09 21:08:39 +01:00
commit 6d9c28ce10
23 changed files with 233 additions and 228 deletions

View File

@ -60,6 +60,12 @@ 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.
Fixed
~~~~~
@ -87,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)

View File

@ -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

View File

@ -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

View File

@ -2,4 +2,4 @@
altgraph==0.17
pyinstaller==4.2
pyinstaller-hooks-contrib==2020.11
pyinstaller-hooks-contrib==2021.1

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -57,6 +57,9 @@ with codecs.open(os.environ['QUTE_HTML'], 'r', 'utf-8') as source:
title = doc.title()
content = doc.summary().replace('<html>', HEADER % title)
# add a class to make styling the page easier
content = content.replace('<body>', '<body class="qute-readability">')
with codecs.open(tmpfile, 'w', 'utf-8') as target:
target.write(content.lstrip())

View File

@ -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('<body>', '<body class="qute-readability">')
fs.writeFile(tmpFile, content, (err) => {
if (err) {
qute.messageError([`"${err}"`])

View File

@ -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

View File

@ -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,49 +1095,49 @@ 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()
try:
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)
# 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("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):

View File

@ -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:

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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 %}

View File

@ -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:

View File

@ -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
@ -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

View File

@ -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

View File

@ -20,6 +20,7 @@
"""Test webenginetab."""
import logging
import textwrap
import pytest
QtWebEngineWidgets = pytest.importorskip("PyQt5.QtWebEngineWidgets")
@ -35,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(
@ -51,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(
@ -70,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(
@ -91,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)
@ -109,13 +128,42 @@ 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', [
# 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_greasemonkey_run_at_values(self, scripts_helper, 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)]
scripts_helper.inject(scripts)
assert scripts_helper.get_script().injectionPoint() == expected
def test_notification_permission_workaround():
"""Make sure the value for QWebEnginePage::Notifications is correct."""

View File

@ -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

View File

@ -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($);
// And return our findings
result;
"""
])
// 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($);
""")
)
# What we expect the script to report back.
ret.expected = ["global", "global",
"shadowed", "shadowed",
"global", "global"]
return ret
# 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;
"""
])
def test_webengine(self, qtbot, webengineview, setup):
page = webengineview.page()
page.runJavaScript(setup.setup_script)
with qtbot.wait_callback() as callback:
page.runJavaScript(setup.test_script, callback)
callback.assert_called_with(setup.expected)
# What we expect the script to report back.
expected = ["global", "global", "shadowed", "shadowed", "global", "global"]
# 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
xfail = False
if (js_tester.tab.backend == usertypes.Backend.QtWebKit and
version.qWebKitVersion() == '602.1'):
expected[-1] = 'shadowed'
expected[-2] = 'shadowed'
xfail = True
js_tester.run(setup_script)
js_tester.run(test_script, expected=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()
@pytest.fixture
def setup(self):
# pylint: disable=attribute-defined-outside-init
class SetupData:
pass
ret = SetupData()
# 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()
# 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"])