From c5334fb68367708294fe3382979a190292893074 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 3 Apr 2018 20:29:31 +1200 Subject: [PATCH] Greasemonkey: use UrlPatterns for match directives The greasemonkey `@match` directive is used to match urls against chromium url patterns (as opposed to `@include` which treats its argument as a glob expression). I was using fnmatch for both here because I am lazy and knew someone else was going to implement chromium url patterns for me eventually. Now it is done and I should switch to using them instead. The most common failing case that this will fix is something matching on `*://*.domain.com/*` because it wouldn't match the url with no subdomain. This codepath is only used on webengine 5.7.1 and webkit backends. --- qutebrowser/browser/greasemonkey.py | 16 ++++++++++++---- tests/unit/javascript/test_greasemonkey.py | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/greasemonkey.py b/qutebrowser/browser/greasemonkey.py index a43644bf6..55a2e7356 100644 --- a/qutebrowser/browser/greasemonkey.py +++ b/qutebrowser/browser/greasemonkey.py @@ -32,6 +32,7 @@ from PyQt5.QtCore import pyqtSignal, QObject, QUrl from qutebrowser.utils import (log, standarddir, jinja, objreg, utils, javascript) +from qutebrowser.utils.urlmatch import UrlPattern from qutebrowser.commands import cmdutils from qutebrowser.browser import downloads @@ -48,6 +49,7 @@ class GreasemonkeyScript: def __init__(self, properties, code): self._code = code self.includes = [] + self.matches = [] self.excludes = [] self.requires = [] self.description = None @@ -63,8 +65,10 @@ class GreasemonkeyScript: self.namespace = value elif name == 'description': self.description = value - elif name in ['include', 'match']: + elif name == 'include': self.includes.append(value) + elif name == 'match': + self.matches.append(value) elif name in ['exclude', 'exclude_match']: self.excludes.append(value) elif name == 'run-at': @@ -92,7 +96,7 @@ class GreasemonkeyScript: props = "" script = cls(re.findall(cls.PROPS_REGEX, props), source) script.script_meta = props - if not script.includes: + if not script.includes and not script.matches: script.includes = ['*'] return script @@ -117,7 +121,7 @@ class GreasemonkeyScript: return json.dumps({ 'name': self.name, 'description': self.description, - 'matches': self.includes, + 'matches': self.matches, 'includes': self.includes, 'excludes': self.excludes, 'run-at': self.run_at, @@ -324,8 +328,12 @@ class GreasemonkeyManager(QObject): # Otherwise they are glob expressions. return fnmatch.fnmatch(string_url, pattern) + def _chromium_match(pattern): + return UrlPattern(pattern).matches(url) + tester = (lambda script: - any(_match(pat) for pat in script.includes) and + (any(_match(pat) for pat in script.includes) or + any(_chromium_match(pat) for pat in script.matches)) and not any(_match(pat) for pat in script.excludes)) return MatchingScripts( diff --git a/tests/unit/javascript/test_greasemonkey.py b/tests/unit/javascript/test_greasemonkey.py index 7759f5d18..8701e8170 100644 --- a/tests/unit/javascript/test_greasemonkey.py +++ b/tests/unit/javascript/test_greasemonkey.py @@ -32,7 +32,7 @@ test_gm_script = r""" // @name qutebrowser test userscript // @namespace invalid.org // @include http://localhost:*/data/title.html -// @match http://trolol* +// @match http://*.trolol.com/* // @exclude https://badhost.xxx/* // @run-at document-start // ==/UserScript== @@ -60,7 +60,7 @@ def test_all(): @pytest.mark.parametrize("url, expected_matches", [ # included - ('http://trololololololo.com/', 1), + ('http://trolol.com/', 1), # neither included nor excluded ('http://aaaaaaaaaa.com/', 0), # excluded