Preload broken qutebrowser logo resource

When qutebrowser is running but its installation has been deleted/moved, it
fails in somewhat mysterious but predictable ways. This is e.g. the case
currently, when people upgrade their Archlinux packages and upgrade from Python
3.11 to 3.12. When doing that with qutebrowser open, on the next page load, it
will:

- Have a crashed renderer process, because (assumingly) the process executable
  is gone on disk.
- Which then causes us trying to render an error page, but that fails due to
  broken_qutebrowser_logo.png being gone from disk.
- The FileNotFoundError then causes jinja2 to import jinja2.debug at runtime,
  but that *also* fails because the jinja2 package is gone.

We work around this by loading the PNG into RAM early, and then using the cached
version instead. This amends b4a2352833 which did
the same with HTML/JS resources, but never for this PNG, which (looking at crash
logs) seems to be a somewhat common breakage.

Alternatives I've considered:

- Catching the FileNotFoundError and not showing an error page at all.
- Generating a PNG with an explanatory text via QPainter and returning that.

However, with the renderer process crash happening in the first place for
unknown reasons, it's unclear if the error page ever gets actually displayed...
Let's roll with this for now, and if this causes a repeating renderer process
crash, fix that separately (also see #5108).
This commit is contained in:
Florian Bruhin 2024-04-30 15:44:53 +02:00
parent 26ef6bffd2
commit b0002ac71f
2 changed files with 11 additions and 0 deletions

View File

@ -47,6 +47,9 @@ Fixed
condition.
- Worked around qutebrowser quitting when closing a KDE file dialog due to a Qt
bug.
- Worked around a crash when trying to use qutebrowser after it's been
deleted/moved on disk (e.g. after a Python upgrade). It now shows an error
asking to run `:restart` instead.
[[v3.1.1]]
v3.1.1 (unreleased)

View File

@ -26,6 +26,7 @@ else: # pragma: no cover
import qutebrowser
_cache: Dict[str, str] = {}
_bin_cache: Dict[str, bytes] = {}
_ResourceType = Union[Traversable, pathlib.Path]
@ -88,6 +89,10 @@ def preload() -> None:
for name in _glob(resource_path, subdir, ext):
_cache[name] = read_file(name)
for name in _glob(resource_path, 'img', '.png'):
# e.g. broken_qutebrowser_logo.png
_bin_cache[name] = read_file_binary(name)
def read_file(filename: str) -> str:
"""Get the contents of a file contained with qutebrowser.
@ -115,6 +120,9 @@ def read_file_binary(filename: str) -> bytes:
Return:
The file contents as a bytes object.
"""
if filename in _bin_cache:
return _bin_cache[filename]
path = _path(filename)
with _keyerror_workaround():
return path.read_bytes()