diff options
author | Florian Bruhin <me@the-compiler.org> | 2022-05-17 11:08:32 +0200 |
---|---|---|
committer | Florian Bruhin <me@the-compiler.org> | 2022-06-22 17:41:11 +0200 |
commit | c47eca57b9fc39d5ce3d9cb54d6b2109643e0aef (patch) | |
tree | 623ba3a268066228a420a00d0b6ae5915ad6647e | |
parent | f84c70019046540c48068dc5274ed96b2bc33ff1 (diff) | |
download | qutebrowser-c47eca57b9fc39d5ce3d9cb54d6b2109643e0aef.tar.gz qutebrowser-c47eca57b9fc39d5ce3d9cb54d6b2109643e0aef.zip |
Warn on QtWebEngine downgrade and Qt 5 -> 6 upgrade
-rw-r--r-- | qutebrowser/config/configfiles.py | 73 | ||||
-rw-r--r-- | qutebrowser/misc/backendproblem.py | 42 | ||||
-rw-r--r-- | qutebrowser/utils/version.py | 11 | ||||
-rw-r--r-- | tests/unit/config/test_configfiles.py | 207 |
4 files changed, 269 insertions, 64 deletions
diff --git a/qutebrowser/config/configfiles.py b/qutebrowser/config/configfiles.py index 4395e51af..d800f3330 100644 --- a/qutebrowser/config/configfiles.py +++ b/qutebrowser/config/configfiles.py @@ -91,6 +91,7 @@ class StateConfig(configparser.ConfigParser): self.qt_version_changed = False self.qtwe_version_changed = False self.qutebrowser_version_changed = VersionChange.unknown + self.chromium_version_changed = VersionChange.unknown self._set_changed_attributes() for sect in ['general', 'geometry', 'inspector']: @@ -111,18 +112,39 @@ class StateConfig(configparser.ConfigParser): self['general']['qt_version'] = qVersion() self['general']['qtwe_version'] = self._qtwe_version_str() + self['general']['chromium_version'] = self._chromium_version_str() self['general']['version'] = qutebrowser.__version__ - def _qtwe_version_str(self) -> str: - """Get the QtWebEngine version string. + def _has_webengine(self) -> bool: + """Check if QtWebEngine is available. Note that it's too early to use objects.backend here... """ try: import qutebrowser.qt.webenginewidgets # pylint: disable=unused-import except ImportError: + return False + return True + + def _qtwe_versions(self) -> Optional[version.WebEngineVersions]: + """Get the QtWebEngine versions.""" + if not self._has_webengine(): + return None + return version.qtwebengine_versions(avoid_init=True) + + def _qtwe_version_str(self) -> str: + """Get the QtWebEngine version string.""" + versions = self._qtwe_versions() + if versions is None: + return 'no' + return str(versions.webengine) + + def _chromium_version_str(self) -> str: + """Get the Chromium major version string.""" + versions = self._qtwe_versions() + if versions is None: return 'no' - return str(version.qtwebengine_versions(avoid_init=True).webengine) + return str(versions.chromium_major) def _set_changed_attributes(self) -> None: """Set qt_version_changed/qutebrowser_version_changed attributes. @@ -140,6 +162,11 @@ class StateConfig(configparser.ConfigParser): old_qtwe_version = self['general'].get('qtwe_version', None) self.qtwe_version_changed = old_qtwe_version != self._qtwe_version_str() + self._set_qutebrowser_changed_attribute() + self._set_chromium_changed_attribute() + + def _set_qutebrowser_changed_attribute(self) -> None: + """Detect a qutebrowser version change.""" old_qutebrowser_version = self['general'].get('version', None) if old_qutebrowser_version is None: # https://github.com/python/typeshed/issues/2093 @@ -164,6 +191,46 @@ class StateConfig(configparser.ConfigParser): else: self.qutebrowser_version_changed = VersionChange.major + def _set_chromium_changed_attribute(self) -> None: + if not self._has_webengine(): + return + + old_chromium_version_str = self['general'].get('chromium_version', None) + if old_chromium_version_str in ['no', None]: + old_qtwe_version = self['general'].get('qtwe_version', None) + if old_qtwe_version in ['no', None]: + return + + try: + old_chromium_version = version.WebEngineVersions.from_webengine( + old_qtwe_version, source='config').chromium_major + except ValueError: + log.init.warning( + f"Unable to parse old QtWebEngine version {old_qtwe_version}") + return + else: + try: + old_chromium_version = int(old_chromium_version_str) + except ValueError: + log.init.warning( + f"Unable to parse old Chromium version {old_chromium_version_str}") + return + + new_versions = version.qtwebengine_versions(avoid_init=True) + new_chromium_version = new_versions.chromium_major + + if old_chromium_version is None or new_chromium_version is None: + return + + if old_chromium_version <= 87 and new_chromium_version >= 90: # Qt 5 -> Qt 6 + self.chromium_version_changed = VersionChange.major + elif old_chromium_version > new_chromium_version: + self.chromium_version_changed = VersionChange.downgrade + elif old_chromium_version == new_chromium_version: + self.chromium_version_changed = VersionChange.equal + else: + self.chromium_version_changed = VersionChange.minor + def init_save_manager(self, save_manager: 'savemanager.SaveManager') -> None: """Make sure the config gets saved properly. diff --git a/qutebrowser/misc/backendproblem.py b/qutebrowser/misc/backendproblem.py index 69303d70e..7ab6b551e 100644 --- a/qutebrowser/misc/backendproblem.py +++ b/qutebrowser/misc/backendproblem.py @@ -323,6 +323,47 @@ class _BackendProblemChecker: shutil.move(service_worker_dir, bak_dir) + def _confirm_chromium_version_changes(self) -> None: + """Ask if there are Chromium downgrades or a Qt 5 -> 6 upgrade.""" + versions = version.qtwebengine_versions(avoid_init=True) + change = configfiles.state.chromium_version_changed + if change == configfiles.VersionChange.major: + # FIXME:qt6 Remove this before the release, as it typically should + # not concern users? + text = ( + "Chromium/QtWebEngine upgrade detected:<br>" + f"You are <b>upgrading to QtWebEngine {versions.webengine}</b> but " + "used Qt 5 for the last qutebrowser launch.<br><br>" + "Data managed by Chromium will be upgraded. This is a <b>one-way " + "operation:</b> If you open qutebrowser with Qt 5 again later, any " + "Chromium data will be invalid and discarded.<br><br>" + "This affects page data such as cookies, but not data managed by " + "qutebrowser, such as your configuration or <tt>:open</tt> history." + ) + elif change == configfiles.VersionChange.downgrade: + text = ( + "Chromium/QtWebEngine downgrade detected:<br>" + f"You are <b>downgrading to QtWebEngine {versions.webengine}</b>." + "<br><br>" + "Data managed by Chromium will be discarded if you continue.<br><br>" + "This affects page data such as cookies, but not data managed by " + "qutebrowser, such as your configuration or <tt>:open</tt> history." + ) + else: + return + + box = msgbox.msgbox( + parent=None, + title="QtWebEngine version change", + text=text, + icon=QMessageBox.Icon.Warning, + plain_text=False, + buttons=QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Abort, + ) + response = box.exec() + if response != QMessageBox.StandardButton.Ok: + sys.exit(usertypes.Exit.err_init) + def _check_webengine_version(self) -> None: versions = version.qtwebengine_versions(avoid_init=True) if versions.webengine < utils.VersionNumber(5, 15, 2): @@ -379,6 +420,7 @@ class _BackendProblemChecker: self._handle_ssl_support() self._handle_serviceworker_nuking() self._check_software_rendering() + self._confirm_chromium_version_changes() else: self._assert_backend(usertypes.Backend.QtWebKit) self._handle_ssl_support(fatal=True) diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index fccd43ea8..0fb2e1a10 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -674,7 +674,11 @@ class WebEngineVersions: ) @classmethod - def from_importlib(cls, pyqt_webengine_qt_version: str) -> 'WebEngineVersions': + def from_webengine( + cls, + pyqt_webengine_qt_version: str, + source: str, + ) -> 'WebEngineVersions': """Get the versions based on the PyQtWebEngine version. This is called if we don't want to fully initialize QtWebEngine (so @@ -685,7 +689,7 @@ class WebEngineVersions: return cls( webengine=parsed, chromium=cls._infer_chromium_version(parsed), - source='importlib', + source=source, ) @classmethod @@ -784,7 +788,8 @@ def qtwebengine_versions(*, avoid_init: bool = False) -> WebEngineVersions: pyqt_webengine_qt_version = _get_pyqt_webengine_qt_version() if pyqt_webengine_qt_version is not None: - return WebEngineVersions.from_importlib(pyqt_webengine_qt_version) + return WebEngineVersions.from_webengine( + pyqt_webengine_qt_version, source='importlib') assert PYQT_WEBENGINE_VERSION_STR is not None return WebEngineVersions.from_pyqt(PYQT_WEBENGINE_VERSION_STR) diff --git a/tests/unit/config/test_configfiles.py b/tests/unit/config/test_configfiles.py index 9822c6a78..9a56a1b1f 100644 --- a/tests/unit/config/test_configfiles.py +++ b/tests/unit/config/test_configfiles.py @@ -77,54 +77,74 @@ def autoconfig(config_tmpdir): @pytest.mark.parametrize('old_data, insert, new_data', [ - (None, - False, - '[general]\n' - 'qt_version = 5.6.7\n' - 'qtwe_version = 7.8.9\n' - 'version = 1.2.3\n' - '\n' - '[geometry]\n' - '\n' - '[inspector]\n' - '\n'), - ('[general]\n' - 'fooled = true', - False, - '[general]\n' - 'qt_version = 5.6.7\n' - 'qtwe_version = 7.8.9\n' - 'version = 1.2.3\n' - '\n' - '[geometry]\n' - '\n' - '[inspector]\n' - '\n'), - ('[general]\n' - 'foobar = 42', - False, - '[general]\n' - 'foobar = 42\n' - 'qt_version = 5.6.7\n' - 'qtwe_version = 7.8.9\n' - 'version = 1.2.3\n' - '\n' - '[geometry]\n' - '\n' - '[inspector]\n' - '\n'), - (None, - True, - '[general]\n' - 'qt_version = 5.6.7\n' - 'qtwe_version = 7.8.9\n' - 'version = 1.2.3\n' - 'newval = 23\n' - '\n' - '[geometry]\n' - '\n' - '[inspector]\n' - '\n'), + ( + None, + + False, + + '[general]\n' + 'qt_version = 5.6.7\n' + 'qtwe_version = 7.8.9\n' + 'chromium_version = 123\n' + 'version = 1.2.3\n' + '\n' + '[geometry]\n' + '\n' + '[inspector]\n' + '\n', + ), + ( + '[general]\n' + 'fooled = true', + + False, + + '[general]\n' + 'qt_version = 5.6.7\n' + 'qtwe_version = 7.8.9\n' + 'chromium_version = 123\n' + 'version = 1.2.3\n' + '\n' + '[geometry]\n' + '\n' + '[inspector]\n' + '\n' + ), + ( + '[general]\n' + 'foobar = 42', + + False, + + '[general]\n' + 'foobar = 42\n' + 'qt_version = 5.6.7\n' + 'qtwe_version = 7.8.9\n' + 'chromium_version = 123\n' + 'version = 1.2.3\n' + '\n' + '[geometry]\n' + '\n' + '[inspector]\n' + '\n' + ), + ( + None, + + True, + + '[general]\n' + 'qt_version = 5.6.7\n' + 'qtwe_version = 7.8.9\n' + 'chromium_version = 123\n' + 'version = 1.2.3\n' + 'newval = 23\n' + '\n' + '[geometry]\n' + '\n' + '[inspector]\n' + '\n' + ), ]) def test_state_config( fake_save_manager, data_tmpdir, monkeypatch, qtwe_version_patcher, @@ -132,7 +152,7 @@ def test_state_config( ): monkeypatch.setattr(configfiles.qutebrowser, '__version__', '1.2.3') monkeypatch.setattr(configfiles, 'qVersion', lambda: '5.6.7') - qtwe_version_patcher('7.8.9') + qtwe_version_patcher('7.8.9', chromium_version='123.4.5.6') statefile = data_tmpdir / 'state' if old_data is not None: @@ -171,14 +191,14 @@ def qtwe_version_patcher(monkeypatch): except ImportError: pytest.skip("QtWebEngine not available") - def patch(ver): + def patch(ver, chromium_version=None): monkeypatch.setattr( configfiles.version, 'qtwebengine_versions', lambda avoid_init=False: version.WebEngineVersions( webengine=utils.VersionNumber.parse(ver), - chromium=None, + chromium=chromium_version, source='test', ) ) @@ -222,13 +242,21 @@ def test_qtwe_version_changed(state_writer, qtwe_version_patcher, assert state.qtwe_version_changed == changed -def test_qtwe_version_changed_webkit(stubs, monkeypatch, state_writer): +@pytest.mark.parametrize("key, attribute, expected", [ + ("qtwe_version", "qtwe_version_changed", False), + ("chromium_version", "chromium_version_changed", configfiles.VersionChange.unknown), +]) +@pytest.mark.parametrize("value", ["no", None]) +def test_version_changed_webkit(stubs, monkeypatch, state_writer, + key, value, attribute, expected): fake = stubs.ImportFake({'qutebrowser.qt.webenginewidgets': False}, monkeypatch) fake.patch() - state_writer('qtwe_version', 'no') + if value is not None: + state_writer(key, value) + state = configfiles.StateConfig() - assert not state.qtwe_version_changed + assert getattr(state, attribute) == expected @pytest.mark.parametrize('old_version, new_version, expected', [ @@ -260,14 +288,77 @@ def test_qutebrowser_version_changed( assert state.qutebrowser_version_changed == expected -def test_qutebrowser_version_unparsable(state_writer, monkeypatch, caplog): - state_writer('version', 'blabla') +@pytest.mark.parametrize('old_version, new_version, expected', [ + (None, '90', configfiles.VersionChange.unknown), + ('90', '90', configfiles.VersionChange.equal), + + ('90', '94', configfiles.VersionChange.minor), + ('83', '87', configfiles.VersionChange.minor), + + ('83', '90', configfiles.VersionChange.major), + ('87', '90', configfiles.VersionChange.major), + ('83', '94', configfiles.VersionChange.major), + ('87', '94', configfiles.VersionChange.major), + + ('94', '90', configfiles.VersionChange.downgrade), + ('94', '87', configfiles.VersionChange.downgrade), + ('90', '83', configfiles.VersionChange.downgrade), +]) +def test_chromium_version_changed( + state_writer, qtwe_version_patcher, + old_version, new_version, expected): + qtwe_version_patcher('6.2', chromium_version=new_version) + + if old_version is not None: + state_writer('chromium_version', old_version) + + state = configfiles.StateConfig() + assert state.chromium_version_changed == expected + + +@pytest.mark.parametrize('old_qtwe_version, new_chromium_version, expected', [ + (None, '90', configfiles.VersionChange.unknown), + ('6.2.0', '90', configfiles.VersionChange.equal), + ('6.2.1', '94', configfiles.VersionChange.minor), + ('5.15.2', '90', configfiles.VersionChange.major), + ('6.3.0', '90', configfiles.VersionChange.downgrade), + +]) +def test_chromium_version_changed_inferring( + state_writer, qtwe_version_patcher, + old_qtwe_version, new_chromium_version, expected): + qtwe_version_patcher('6.2', chromium_version=new_chromium_version) + + if old_qtwe_version is not None: + state_writer('qtwe_version', old_qtwe_version) + + state = configfiles.StateConfig() + assert state.chromium_version_changed == expected + + +@pytest.mark.parametrize("key, msg, attribute", [ + ("version", "old version", "qutebrowser_version_changed"), + pytest.param( + "qtwe_version", + "old QtWebEngine version", + "chromium_version_changed", + marks=pytest.mark.qtwebkit_skip, + ), + pytest.param( + "chromium_version", + "old Chromium version", + "chromium_version_changed", + marks=pytest.mark.qtwebkit_skip, + ), +]) +def test_version_unparsable(state_writer, caplog, key, msg, attribute): + state_writer(key, 'blabla') with caplog.at_level(logging.WARNING): state = configfiles.StateConfig() - assert caplog.messages == ['Unable to parse old version blabla'] - assert state.qutebrowser_version_changed == configfiles.VersionChange.unknown + assert caplog.messages == [f'Unable to parse {msg} blabla'] + assert getattr(state, attribute) == configfiles.VersionChange.unknown @pytest.mark.parametrize('value, filterstr, matches', [ |