diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 6c640d28f..3f879ce28 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -58,32 +58,6 @@ def on_mode_entered(mode, win_id): modeman.maybe_leave(win_id, usertypes.KeyMode.hint, 'insert mode') -def _resolve_url(elem, baseurl): - """Resolve a URL and check if we want to keep it. - - Args: - elem: The QWebElement to get the URL of. - baseurl: The baseurl of the current tab. - - Return: - A QUrl with the absolute URL, or None. - """ - for attr in ['href', 'src']: - if attr in elem: - text = elem[attr].strip() - break - else: - return None - - url = QUrl(text) - if not url.isValid(): - return None - if url.isRelative(): - url = baseurl.resolved(url) - qtutils.ensure_valid(url) - return url - - class HintContext: """Context namespace used for hinting. @@ -274,7 +248,7 @@ class HintActions(QObject): elem: The QWebElement to download. _context: The HintContext to use. """ - url = _resolve_url(elem, context.baseurl) + url = elem.resolve_url(context.baseurl) if url is None: raise HintingError if context.rapid: @@ -301,7 +275,7 @@ class HintActions(QObject): 'QUTE_SELECTED_TEXT': str(elem), 'QUTE_SELECTED_HTML': elem.outer_xml(), } - url = _resolve_url(elem, context.baseurl) + url = elem.resolve_url(context.baseurl) if url is not None: env['QUTE_URL'] = url.toString(QUrl.FullyEncoded) @@ -1012,7 +986,7 @@ class HintManager(QObject): handler = functools.partial(elem_handlers[self._context.target], elem, self._context) elif self._context.target in url_handlers: - url = _resolve_url(elem, self._context.baseurl) + url = elem.resolve_url(self._context.baseurl) if url is None: self._show_url_error() return diff --git a/qutebrowser/browser/webkit/webelem.py b/qutebrowser/browser/webkit/webelem.py index 213ee435c..0d62dad26 100644 --- a/qutebrowser/browser/webkit/webelem.py +++ b/qutebrowser/browser/webkit/webelem.py @@ -33,7 +33,7 @@ from PyQt5.QtCore import QRect, QUrl from PyQt5.QtWebKit import QWebElement from qutebrowser.config import config -from qutebrowser.utils import log, usertypes, utils, javascript +from qutebrowser.utils import log, usertypes, utils, javascript, qtutils Group = usertypes.enum('Group', ['all', 'links', 'images', 'url', 'prevnext', @@ -500,6 +500,30 @@ class WebElementWrapper(collections.abc.MutableMapping): visible_in_frame = visible_on_screen return all([visible_on_screen, visible_in_frame]) + def resolve_url(self, baseurl): + """Resolve the URL in the element's src/href attribute. + + Args: + baseurl: The URL to base relative URLs on as QUrl. + + Return: + A QUrl with the absolute URL, or None. + """ + for attr in ['href', 'src']: + if attr in self: + text = self[attr].strip() + break + else: + return None + + url = QUrl(text) + if not url.isValid(): + return None + if url.isRelative(): + url = baseurl.resolved(url) + qtutils.ensure_valid(url) + return url + def get_child_frames(startframe): """Get all children recursively of a given QWebFrame. diff --git a/tests/unit/browser/webkit/test_webelem.py b/tests/unit/browser/webkit/test_webelem.py index f97402a9a..b5f4006bc 100644 --- a/tests/unit/browser/webkit/test_webelem.py +++ b/tests/unit/browser/webkit/test_webelem.py @@ -24,7 +24,7 @@ import collections.abc import operator import itertools -from PyQt5.QtCore import QRect, QPoint +from PyQt5.QtCore import QRect, QPoint, QUrl from PyQt5.QtWebKit import QWebElement import pytest @@ -942,3 +942,19 @@ class TestIsEditable: stubbed_config.data['input']['insert-mode-on-plugins'] = setting elem = get_webelem(tagname=tagname, attributes=attributes) assert elem.is_editable() == editable + + +@pytest.mark.parametrize('attributes, expected', [ + # No attributes + ({}, None), + ({'href': 'foo'}, QUrl('http://www.example.com/foo')), + ({'src': 'foo'}, QUrl('http://www.example.com/foo')), + ({'href': 'foo', 'src': 'bar'}, QUrl('http://www.example.com/foo')), + ({'href': '::garbage::'}, None), + ({'href': 'http://www.example.org/'}, QUrl('http://www.example.org/')), + ({'href': ' foo '}, QUrl('http://www.example.com/foo')), +]) +def test_resolve_url(attributes, expected): + elem = get_webelem(attributes=attributes) + baseurl = QUrl('http://www.example.com/') + assert elem.resolve_url(baseurl) == expected