diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 36656b3df..afa175a26 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -14,6 +14,15 @@ This project adheres to http://semver.org/[Semantic Versioning]. // `Fixed` for any bug fixes. // `Security` to invite users to upgrade in case of vulnerabilities. +v0.11.0 (unreleased) +-------------------- + +Changed +~~~~~~~ + +- When using QtWebEngine, the underlying Chromium version is now shown in the + version info. + v0.10.1 (unreleased) -------------------- diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index 1aaf6c7c1..04067d584 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -38,6 +38,11 @@ try: except ImportError: # pragma: no cover qWebKitVersion = None +try: + from PyQt5.QtWebEngineWidgets import QWebEngineProfile +except ImportError: # pragma: no cover + QWebEngineProfile = None + import qutebrowser from qutebrowser.utils import log, utils, standarddir, usertypes, qtutils from qutebrowser.misc import objects @@ -134,6 +139,7 @@ def _module_versions(): ('cssutils', ['__version__']), ('typing', []), ('PyQt5.QtWebEngineWidgets', []), + ('PyQt5.QtWebKitWidgets', []), ]) for name, attributes in modules.items(): try: @@ -233,6 +239,31 @@ def qt_version(): return qVersion() +def _chromium_version(): + """Get the Chromium version for QtWebEngine.""" + if QWebEngineProfile is None: + # This should never happen + return 'unavailable' + profile = QWebEngineProfile() + ua = profile.httpUserAgent() + match = re.search(r' Chrome/([^ ]*) ', ua) + if not match: + log.misc.error("Could not get Chromium version from: {}".format(ua)) + return 'unknown' + return match.group(1) + + +def _backend(): + """Get the backend line with relevant information.""" + if objects.backend == usertypes.Backend.QtWebKit: + return 'QtWebKit{} (WebKit {})'.format( + '-NG' if qtutils.is_qtwebkit_ng(qWebKitVersion()) else '', + qWebKitVersion()) + else: + assert objects.backend == usertypes.Backend.QtWebEngine, objects.backend + return 'QtWebEngine (Chromium {})'.format(_chromium_version()) + + def version(): """Return a string with various version informations.""" lines = ["qutebrowser v{}".format(qutebrowser.__version__)] @@ -240,12 +271,7 @@ def version(): if gitver is not None: lines.append("Git commit: {}".format(gitver)) - backend = objects.backend.name - if (qWebKitVersion is not None and - objects.backend == usertypes.Backend.QtWebKit and - qtutils.is_qtwebkit_ng(qWebKitVersion())): - backend = 'QtWebKit-NG' - lines.append("Backend: {}".format(backend)) + lines.append("Backend: {}".format(_backend())) lines += [ '', @@ -260,11 +286,6 @@ def version(): lines += ['pdf.js: {}'.format(_pdfjs_version())] - if qWebKitVersion is None: - lines.append('Webkit: no') - else: - lines.append('Webkit: {}'.format(qWebKitVersion())) - lines += [ 'SSL: {}'.format(QSslSocket.sslLibraryVersionString()), '', diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index 7f93f174c..c6e629ef1 100644 --- a/tests/unit/utils/test_version.py +++ b/tests/unit/utils/test_version.py @@ -359,6 +359,7 @@ class ImportFake: 'cssutils': True, 'typing': True, 'PyQt5.QtWebEngineWidgets': True, + 'PyQt5.QtWebKitWidgets': True, } self.version_attribute = '__version__' self.version = '1.2.3' @@ -419,7 +420,8 @@ class TestModuleVersions: expected = ['sip: yes', 'colorama: 1.2.3', 'pypeg2: 1.2.3', 'jinja2: 1.2.3', 'pygments: 1.2.3', 'yaml: 1.2.3', 'cssutils: 1.2.3', 'typing: yes', - 'PyQt5.QtWebEngineWidgets: yes'] + 'PyQt5.QtWebEngineWidgets: yes', + 'PyQt5.QtWebKitWidgets: yes'] assert version._module_versions() == expected @pytest.mark.parametrize('module, idx, expected', [ @@ -442,14 +444,17 @@ class TestModuleVersions: ('VERSION', ['sip: yes', 'colorama: 1.2.3', 'pypeg2: yes', 'jinja2: yes', 'pygments: yes', 'yaml: yes', 'cssutils: yes', 'typing: yes', - 'PyQt5.QtWebEngineWidgets: yes']), + 'PyQt5.QtWebEngineWidgets: yes', + 'PyQt5.QtWebKitWidgets: yes']), ('SIP_VERSION_STR', ['sip: 1.2.3', 'colorama: yes', 'pypeg2: yes', 'jinja2: yes', 'pygments: yes', 'yaml: yes', 'cssutils: yes', 'typing: yes', - 'PyQt5.QtWebEngineWidgets: yes']), + 'PyQt5.QtWebEngineWidgets: yes', + 'PyQt5.QtWebKitWidgets: yes']), (None, ['sip: yes', 'colorama: yes', 'pypeg2: yes', 'jinja2: yes', 'pygments: yes', 'yaml: yes', 'cssutils: yes', 'typing: yes', - 'PyQt5.QtWebEngineWidgets: yes']), + 'PyQt5.QtWebEngineWidgets: yes', + 'PyQt5.QtWebKitWidgets: yes']), ]) def test_version_attribute(self, value, expected, import_fake): """Test with a different version attribute. @@ -646,6 +651,30 @@ def test_qt_version(monkeypatch, same): assert version.qt_version() == expected +@pytest.mark.parametrize('ua, expected', [ + (None, 'unavailable'), # No QWebEngineProfile + ('Mozilla/5.0', 'unknown'), + ('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'QtWebEngine/5.8.0 Chrome/53.0.2785.148 Safari/537.36', '53.0.2785.148'), +]) +def test_chromium_version(monkeypatch, caplog, ua, expected): + if ua is None: + monkeypatch.setattr(version, 'QWebEngineProfile', None) + else: + class FakeWebEngineProfile: + def httpUserAgent(self): + return ua + monkeypatch.setattr(version, 'QWebEngineProfile', FakeWebEngineProfile) + + with caplog.at_level(logging.ERROR): + assert version._chromium_version() == expected + + +def test_chromium_version_unpatched(qapp): + pytest.importorskip('PyQt5.QtWebEngineWidgets') + assert version._chromium_version() not in ['', 'unknown', 'unavailable'] + + @pytest.mark.parametrize(['git_commit', 'frozen', 'style', 'with_webkit'], [ (True, False, True, True), # normal (False, False, True, True), # no git commit @@ -657,6 +686,10 @@ def test_qt_version(monkeypatch, same): def test_version_output(git_commit, frozen, style, with_webkit, stubs, monkeypatch): """Test version.version().""" + class FakeWebEngineProfile: + def httpUserAgent(self): + return 'Toaster/4.0.4 Chrome/CHROMIUMVERSION Teapot/4.1.8' + import_path = os.path.abspath('/IMPORTPATH') patches = { 'qutebrowser.__file__': os.path.join(import_path, '__init__.py'), @@ -669,7 +702,6 @@ def test_version_output(git_commit, frozen, style, with_webkit, stubs, 'qVersion': lambda: 'QT VERSION', '_module_versions': lambda: ['MODULE VERSION 1', 'MODULE VERSION 2'], '_pdfjs_version': lambda: 'PDFJS VERSION', - 'qWebKitVersion': (lambda: 'WEBKIT VERSION') if with_webkit else None, 'QSslSocket': FakeQSslSocket('SSL VERSION'), 'platform.platform': lambda: 'PLATFORM', 'platform.architecture': lambda: ('ARCHITECTURE', ''), @@ -677,13 +709,33 @@ def test_version_output(git_commit, frozen, style, with_webkit, stubs, '_path_info': lambda: {'PATH DESC': 'PATH NAME'}, 'QApplication': (stubs.FakeQApplication(style='STYLE') if style else stubs.FakeQApplication(instance=None)), - 'objects.backend': (usertypes.Backend.QtWebKit if with_webkit - else usertypes.Backend.QtWebEngine), - 'qtutils.is_qtwebkit_ng': (lambda v: - True if with_webkit == 'ng' else False), 'QLibraryInfo.location': (lambda _loc: 'QT PATH') } + substitutions = { + 'git_commit': '\nGit commit: GIT COMMIT' if git_commit else '', + 'style': '\nStyle: STYLE' if style else '', + 'qt': 'QT VERSION', + 'frozen': str(frozen), + 'import_path': import_path, + } + + if with_webkit: + patches['qWebKitVersion'] = lambda: 'WEBKIT VERSION' + patches['objects.backend'] = usertypes.Backend.QtWebKit + patches['QWebEngineProfile'] = None + if with_webkit == 'ng': + patches['qtutils.is_qtwebkit_ng'] = lambda v: True + substitutions['backend'] = 'QtWebKit-NG (WebKit WEBKIT VERSION)' + else: + patches['qtutils.is_qtwebkit_ng'] = lambda v: False + substitutions['backend'] = 'QtWebKit (WebKit WEBKIT VERSION)' + else: + patches['qWebKitVersion'] = None + patches['objects.backend'] = usertypes.Backend.QtWebEngine + patches['QWebEngineProfile'] = FakeWebEngineProfile + substitutions['backend'] = 'QtWebEngine (Chromium CHROMIUMVERSION)' + for attr, val in patches.items(): monkeypatch.setattr('qutebrowser.utils.version.' + attr, val) @@ -703,7 +755,6 @@ def test_version_output(git_commit, frozen, style, with_webkit, stubs, MODULE VERSION 1 MODULE VERSION 2 pdf.js: PDFJS VERSION - Webkit: {webkit} SSL: SSL VERSION {style} Platform: PLATFORM, ARCHITECTURE @@ -717,20 +768,5 @@ def test_version_output(git_commit, frozen, style, with_webkit, stubs, PATH DESC: PATH NAME """.lstrip('\n')) - substitutions = { - 'git_commit': '\nGit commit: GIT COMMIT' if git_commit else '', - 'style': '\nStyle: STYLE' if style else '', - 'qt': 'QT VERSION', - 'frozen': str(frozen), - 'import_path': import_path, - 'webkit': 'WEBKIT VERSION' if with_webkit else 'no', - } - backends = { - True: 'QtWebKit', - False: 'QtWebEngine', - 'ng': 'QtWebKit-NG', - } - substitutions['backend'] = backends[with_webkit] - expected = template.rstrip('\n').format(**substitutions) assert version.version() == expected