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: