From c6b486ce8176a5908d261e2fc86fdb23aed04b2a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 21 Sep 2025 18:07:34 +0200 Subject: [PATCH] Fix segfault with nested download prompts For an unknown reason, if a download prompt is triple-nested, Qt segfaults here: #0 add_lazy_attrs (td=0x7ffff52e0dd1 ) at sip_core.c:6255 #1 sip_add_all_lazy_attrs (td=0x7ffff52e0dd1 ) at sip_core.c:6304 #2 0x00007ffff636598e in sip_api_is_py_method_12_8 (gil=gil@entry=0x7fffffff8b8c, pymc=pymc@entry=0x7fffffff8b8b "", sipSelfp=sipSelfp@entry=0x7fffffff8ba8, cname=cname@entry=0x0, mname=mname@entry=0x7ffff636d57b "__dtor__") at sip_core.c:7402 #3 0x00007ffff6365c2c in sip_api_is_py_method_12_8 (mname=0x7ffff636d57b "__dtor__", cname=0x0, sipSelfp=0x7fffffff8ba8, pymc=0x7fffffff8b8b "", gil=0x7fffffff8b8c) at sip_core.c:7356 #4 callPyDtor (self=) at sip_core.c:5375 #5 sip_api_instance_destroyed_ex (sipSelfp=0x7fffffff8c40) at sip_core.c:5311 #6 0x00007ffff5fc9967 in sipQEventLoop::~sipQEventLoop() () from [...]/python3.13/site-packages/PyQt6/QtCore.abi3.so #7 0x00007ffff0bcd749 in QFileInfoGatherer::getInfo (this=0x5555583f9bc0, fileInfo=...) at [...]/qt5/qtbase/src/gui/itemmodels/qfileinfogatherer.cpp:349 #8 0x00007ffff0be2629 in QFileSystemModelPrivate::fileSystemChanged (this=0x5555583f9870, path="/tmp/qbdl/download", updates=QList> (size = 3) = {...}) at [...]/qt5/qtbase/src/gui/itemmodels/qfilesystemmodel.cpp:1966 (full stacktrace has 183 frames) After a lot of experimentation, I figured out that moving the `NullIconProvider` to a class variable (or removing it entirely) seems to help. Note that making it `global` (but keeping the instanciation inside `FilenamePrompt._init_fileview()`) did not! This is semi-blind fix: It's unclear to me if this is a proper fix, or if the changed memory layout just causes the issue to temporary disappear. However, given that `QFileInfoGatherer::getInfo()` calls into the icon provider, it might very well be the real deal. Closes #8674 --- qutebrowser/mainwindow/prompt.py | 8 +++++++- tests/end2end/features/downloads.feature | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 2e797970f..3f618a279 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -654,6 +654,12 @@ class FilenamePrompt(_BasePrompt): """A prompt for a filename.""" + # Note: This *must* be a class variable! If it's not, for unknown reasons, + # we get a segfault in Qt/PyQt in QFileInfoGatherer::getInfo() if we have + # nested download prompts (i.e. trigger a download while a download prompt + # is open already). + _null_icon_provider = NullIconProvider() + def __init__(self, question, parent=None): super().__init__(question, parent) self._init_texts(question) @@ -753,7 +759,7 @@ class FilenamePrompt(_BasePrompt): self._file_model = QFileSystemModel(self) # avoid icon and mime type lookups, they are slow in Qt6 - self._file_model.setIconProvider(NullIconProvider()) + self._file_model.setIconProvider(self._null_icon_provider) self._file_view.setModel(self._file_model) self._file_view.clicked.connect(self._insert_path) diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index d07e587ed..df04625a8 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -669,6 +669,20 @@ Feature: Downloading things from a website. Then the downloaded file download.bin should exist And the downloaded file download2.bin should not exist + Scenario: Nested download prompts (#8674) + When I set downloads.location.prompt to true + And I open data/downloads/download.bin without waiting + And I wait for "Asking question option=None text=* title='Save file to:'>, *" in the log + And I open data/downloads/download.bin without waiting + And I wait for "Asking question option=None text=* title='Save file to:'>, *" in the log + And I open data/downloads/download.bin without waiting + And I wait for "Asking question option=None text=* title='Save file to:'>, *" in the log + And I run :prompt-accept + And I run :mode-leave + And I run :mode-leave + And I wait until the download is finished + Then the downloaded file download.bin should exist + @qtwebengine_skip # We can't get the UA from the page there Scenario: user-agent when using :download When I open user-agent