summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2019-02-25 09:56:18 +0100
committerFlorian Bruhin <me@the-compiler.org>2019-02-25 09:56:18 +0100
commit94542c5f780aae78612a112b7be6f78da97b736f (patch)
tree56e06ce089d39b7f6f217d032b16b0737c539c93
parentb10366ec3a4e600e0851086bd200f8fb11160d5a (diff)
parent6dd978ae05a4fb0525967c390ece2e51bbdb2a1a (diff)
downloadqutebrowser-94542c5f780aae78612a112b7be6f78da97b736f.tar.gz
qutebrowser-94542c5f780aae78612a112b7be6f78da97b736f.zip
Merge branch 'greasemonkey-quirks'
-rw-r--r--qutebrowser/browser/greasemonkey.py37
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py6
-rw-r--r--tests/unit/browser/webengine/test_webenginetab.py25
-rw-r--r--tests/unit/javascript/test_greasemonkey.py51
4 files changed, 118 insertions, 1 deletions
diff --git a/qutebrowser/browser/greasemonkey.py b/qutebrowser/browser/greasemonkey.py
index 7ef1f0ff2..07fa8d581 100644
--- a/qutebrowser/browser/greasemonkey.py
+++ b/qutebrowser/browser/greasemonkey.py
@@ -31,7 +31,8 @@ import attr
from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from qutebrowser.utils import (log, standarddir, jinja, objreg, utils,
- javascript, urlmatch, version, usertypes)
+ javascript, urlmatch, version, usertypes,
+ qtutils)
from qutebrowser.api import cmdutils
from qutebrowser.browser import downloads
from qutebrowser.misc import objects
@@ -116,6 +117,40 @@ class GreasemonkeyScript:
script.includes = ['*']
return script
+ def force_document_end(self):
+ """Check whether to force @run-at document-end.
+
+ This needs to be done on QtWebEngine with Qt 5.12 for known-broken
+ scripts.
+
+ On Qt 5.12, accessing the DOM isn't possible with "@run-at
+ document-start". It was documented to be impossible before, but seems
+ to work fine.
+
+ However, some scripts do DOM access with "@run-at document-start". Fix
+ those by forcing them to use document-end instead.
+ """
+ if objects.backend != usertypes.Backend.QtWebEngine:
+ return False
+ elif not qtutils.version_check('5.12', compiled=False):
+ return False
+
+ broken_scripts = [
+ ('http://userstyles.org', None),
+ ('https://github.com/ParticleCore', 'Iridium'),
+ ]
+ return any(self._matches_id(namespace=namespace, name=name)
+ for namespace, name in broken_scripts)
+
+ def _matches_id(self, *, namespace, name):
+ """Check if this script matches the given namespace/name.
+
+ Both namespace and name can be None in order to match any script.
+ """
+ matches_namespace = namespace is None or self.namespace == namespace
+ matches_name = name is None or self.name == name
+ return matches_namespace and matches_name
+
def code(self):
"""Return the processed JavaScript code of this script.
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 88c283126..41081abb8 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -1038,9 +1038,15 @@ class _WebEngineScripts(QObject):
new_script.setSourceCode(script.code())
new_script.setName("GM-{}".format(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.force_document_end():
+ log.greasemonkey.debug("Forcing @run-at document-end for {}"
+ .format(script.name))
+ new_script.setInjectionPoint(QWebEngineScript.DocumentReady)
+
log.greasemonkey.debug('adding script: {}'
.format(new_script.name()))
page_scripts.insert(new_script)
diff --git a/tests/unit/browser/webengine/test_webenginetab.py b/tests/unit/browser/webengine/test_webenginetab.py
index 251abb84a..79ac8d37a 100644
--- a/tests/unit/browser/webengine/test_webenginetab.py
+++ b/tests/unit/browser/webengine/test_webenginetab.py
@@ -25,8 +25,10 @@ import pytest
QtWebEngineWidgets = pytest.importorskip("PyQt5.QtWebEngineWidgets")
QWebEnginePage = QtWebEngineWidgets.QWebEnginePage
QWebEngineScriptCollection = QtWebEngineWidgets.QWebEngineScriptCollection
+QWebEngineScript = QtWebEngineWidgets.QWebEngineScript
from qutebrowser.browser import greasemonkey
+from qutebrowser.utils import usertypes
pytestmark = pytest.mark.usefixtures('greasemonkey_manager')
@@ -91,3 +93,26 @@ class TestWebengineScripts:
collection = webengine_scripts._widget.page().scripts()
assert collection.toList()[-1].worldId() == worldid
+
+ def test_greasemonkey_force_document_end(self, monkeypatch,
+ webengine_scripts):
+ """Make sure document-end is forced when needed."""
+ monkeypatch.setattr(greasemonkey.objects, 'backend',
+ usertypes.Backend.QtWebEngine)
+ monkeypatch.setattr(greasemonkey.qtutils, 'version_check',
+ lambda version, exact=False, compiled=True:
+ True)
+
+ scripts = [
+ greasemonkey.GreasemonkeyScript([
+ ('name', 'Iridium'),
+ ('namespace', 'https://github.com/ParticleCore'),
+ ('run-at', 'document-start'),
+ ], None)
+ ]
+
+ webengine_scripts._inject_greasemonkey_scripts(scripts)
+
+ collection = webengine_scripts._widget.page().scripts()
+ script = collection.toList()[-1]
+ assert script.injectionPoint() == QWebEngineScript.DocumentReady
diff --git a/tests/unit/javascript/test_greasemonkey.py b/tests/unit/javascript/test_greasemonkey.py
index 6c1c35e06..48d2e075c 100644
--- a/tests/unit/javascript/test_greasemonkey.py
+++ b/tests/unit/javascript/test_greasemonkey.py
@@ -25,6 +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.browser import greasemonkey
test_gm_script = r"""
@@ -165,6 +166,56 @@ def test_utf8_bom():
assert '// ==UserScript==' in script.code().splitlines()
+class TestForceDocumentEnd:
+
+ @pytest.fixture
+ def patch(self, monkeypatch):
+ def _patch(*, backend, qt_512):
+ monkeypatch.setattr(greasemonkey.objects, 'backend', backend)
+ monkeypatch.setattr(greasemonkey.qtutils, 'version_check',
+ lambda version, exact=False, compiled=True:
+ qt_512)
+ return _patch
+
+ def _get_script(self, *, namespace, name):
+ source = textwrap.dedent("""
+ // ==UserScript==
+ // @namespace {}
+ // @name {}
+ // ==/UserScript==
+ """.format(namespace, name))
+ _save_script(source, 'force.user.js')
+
+ gm_manager = greasemonkey.GreasemonkeyManager()
+
+ scripts = gm_manager.all_scripts()
+ assert len(scripts) == 1
+ return scripts[0]
+
+ @pytest.mark.parametrize('backend, qt_512', [
+ (usertypes.Backend.QtWebKit, True),
+ (usertypes.Backend.QtWebEngine, False),
+ ])
+ def test_not_applicable(self, patch, backend, qt_512):
+ """Test backend/Qt version combinations which don't need a fix."""
+ patch(backend=backend, qt_512=qt_512)
+ script = self._get_script(namespace='https://github.com/ParticleCore',
+ name='Iridium')
+ assert not script.force_document_end()
+
+ @pytest.mark.parametrize('namespace, name, force', [
+ ('http://userstyles.org', 'foobar', True),
+ ('https://github.com/ParticleCore', 'Iridium', True),
+ ('https://github.com/ParticleCore', 'Foo', False),
+ ('https://example.org', 'Iridium', False),
+ ])
+ def test_matching(self, patch, namespace, name, force):
+ """Test matching based on namespace/name."""
+ patch(backend=usertypes.Backend.QtWebEngine, qt_512=True)
+ script = self._get_script(namespace=namespace, name=name)
+ assert script.force_document_end() == force
+
+
def test_required_scripts_are_included(download_stub, tmpdir):
test_require_script = textwrap.dedent("""
// ==UserScript==