From d2cbd15bbeadcb67922a2fc5178eed279e7ac6f7 Mon Sep 17 00:00:00 2001 From: Hikaru Y <47855854+hikaru-y@users.noreply.github.com> Date: Mon, 29 Nov 2021 11:31:37 +0900 Subject: [PATCH] Fix memory leak in AnkiWebView (#1510) --- qt/aqt/about.py | 9 +++++++++ qt/aqt/browser/card_info.py | 1 + qt/aqt/browser/previewer.py | 1 + qt/aqt/changenotetype.py | 1 + qt/aqt/clayout.py | 1 + qt/aqt/deckoptions.py | 1 + qt/aqt/editor.py | 1 + qt/aqt/emptycards.py | 3 +++ qt/aqt/stats.py | 2 ++ qt/aqt/webview.py | 2 +- 10 files changed, 21 insertions(+), 1 deletion(-) diff --git a/qt/aqt/about.py b/qt/aqt/about.py index 33168940e..5896867a4 100644 --- a/qt/aqt/about.py +++ b/qt/aqt/about.py @@ -91,6 +91,15 @@ def show(mw: aqt.AnkiQt) -> QDialog: abt.buttonBox.addButton(btn, QDialogButtonBox.ButtonRole.ActionRole) abt.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setFocus() + # WebView cleanup + ###################################################################### + + def on_dialog_destroyed() -> None: + abt.label.cleanup() + abt.label = None + + qconnect(dialog.destroyed, on_dialog_destroyed) + # WebView contents ###################################################################### abouttext = "
" diff --git a/qt/aqt/browser/card_info.py b/qt/aqt/browser/card_info.py index 61bdd2274..01424f8ec 100644 --- a/qt/aqt/browser/card_info.py +++ b/qt/aqt/browser/card_info.py @@ -77,6 +77,7 @@ class CardInfoDialog(QDialog): def reject(self) -> None: if self._on_close: self._on_close() + self.web.cleanup() self.web = None saveGeom(self, self.GEOMETRY_KEY) return QDialog.reject(self) diff --git a/qt/aqt/browser/previewer.py b/qt/aqt/browser/previewer.py index 01b6c253f..8950b9647 100644 --- a/qt/aqt/browser/previewer.py +++ b/qt/aqt/browser/previewer.py @@ -120,6 +120,7 @@ class Previewer(QDialog): def _on_close(self) -> None: self._open = False self._close_callback() + self._web.cleanup() self._web = None def _setup_web_view(self) -> None: diff --git a/qt/aqt/changenotetype.py b/qt/aqt/changenotetype.py index 4876e4013..51bfcfade 100644 --- a/qt/aqt/changenotetype.py +++ b/qt/aqt/changenotetype.py @@ -65,6 +65,7 @@ class ChangeNotetypeDialog(QDialog): self.setWindowTitle(tr.browsing_change_notetype()) def reject(self) -> None: + self.web.cleanup() self.web = None saveGeom(self, self.TITLE) QDialog.reject(self) diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py index 5771278f9..e92d05a87 100644 --- a/qt/aqt/clayout.py +++ b/qt/aqt/clayout.py @@ -825,6 +825,7 @@ class CardLayout(QDialog): av_player.stop_and_clear_queue() saveGeom(self, "CardLayout") saveSplitter(self.mainArea, "CardLayoutMainArea") + self.preview_web.cleanup() self.preview_web = None self.model = None self.rendered_card = None diff --git a/qt/aqt/deckoptions.py b/qt/aqt/deckoptions.py index 76bf44dcb..2fcb35188 100644 --- a/qt/aqt/deckoptions.py +++ b/qt/aqt/deckoptions.py @@ -60,6 +60,7 @@ class DeckOptionsDialog(QDialog): gui_hooks.deck_options_did_load(self) def reject(self) -> None: + self.web.cleanup() self.web = None saveGeom(self, self.TITLE) QDialog.reject(self) diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index ee85ad2be..63c0b9b22 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -586,6 +586,7 @@ noteEditorPromise.then(noteEditor => noteEditor.toolbar.toolbar.appendGroup({{ def cleanup(self) -> None: self.set_note(None) # prevent any remaining evalWithCallback() events from firing after C++ object deleted + self.web.cleanup() self.web = None # legacy diff --git a/qt/aqt/emptycards.py b/qt/aqt/emptycards.py index 1048caddd..5a9c08bc1 100644 --- a/qt/aqt/emptycards.py +++ b/qt/aqt/emptycards.py @@ -36,6 +36,7 @@ class EmptyCardsDialog(QDialog): def __init__(self, mw: aqt.main.AnkiQt, report: EmptyCardsReport) -> None: super().__init__(mw) self.mw = mw.weakref() + self.mw.garbage_collect_on_dialog_finish(self) self.report = report self.form = aqt.forms.emptycards.Ui_Dialog() self.form.setupUi(self) @@ -58,6 +59,8 @@ class EmptyCardsDialog(QDialog): self.form.webview.stdHtml(style + html, context=self) def on_finished(code: Any) -> None: + self.form.webview.cleanup() + self.form.webview = None saveGeom(self, "emptycards") qconnect(self.finished, on_finished) diff --git a/qt/aqt/stats.py b/qt/aqt/stats.py index cd2b32d5d..262ace180 100644 --- a/qt/aqt/stats.py +++ b/qt/aqt/stats.py @@ -54,6 +54,7 @@ class NewDeckStats(QDialog): self.activateWindow() def reject(self) -> None: + self.form.web.cleanup() self.form.web = None saveGeom(self, self.name) aqt.dialogs.markClosed("NewDeckStats") @@ -144,6 +145,7 @@ class DeckStats(QDialog): self.activateWindow() def reject(self) -> None: + self.form.web.cleanup() self.form.web = None saveGeom(self, self.name) aqt.dialogs.markClosed("DeckStats") diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index b01e8ca9c..8d515c7db 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -671,7 +671,7 @@ document.head.appendChild(style); self._domReady = False self._page.setContent(cast(QByteArray, bytes("", "ascii"))) - def __del__(self) -> None: + def cleanup(self) -> None: try: from aqt import mw except ImportError: