summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2022-05-17 11:08:32 +0200
committerFlorian Bruhin <me@the-compiler.org>2022-06-22 17:41:11 +0200
commitc47eca57b9fc39d5ce3d9cb54d6b2109643e0aef (patch)
tree623ba3a268066228a420a00d0b6ae5915ad6647e
parentf84c70019046540c48068dc5274ed96b2bc33ff1 (diff)
downloadqutebrowser-c47eca57b9fc39d5ce3d9cb54d6b2109643e0aef.tar.gz
qutebrowser-c47eca57b9fc39d5ce3d9cb54d6b2109643e0aef.zip
Warn on QtWebEngine downgrade and Qt 5 -> 6 upgrade
-rw-r--r--qutebrowser/config/configfiles.py73
-rw-r--r--qutebrowser/misc/backendproblem.py42
-rw-r--r--qutebrowser/utils/version.py11
-rw-r--r--tests/unit/config/test_configfiles.py207
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', [