From e09a8c77e9688c4610367ab689aa4067004e8bbe Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Wed, 29 Nov 2017 07:01:48 -0500 Subject: [PATCH] Simplify check for orphaned editor. Instead of rewiring signals on tab.shutting_down, have the webelem check if its parent tab is deleted, and throw a specific exception. This is only necessary in WebEngine, Webkit does not crash when the editor is orphaned. I tried to write a test for is_deleted, but could not get it to pass: ``` def test_is_deleted(qtbot, view, config_stub, tab_registry, mode_manager): tab_w = Tab(win_id=0, mode_manager=mode_manager) qtbot.add_widget(tab_w) tab_w._set_widget(view) assert not tab_w.is_deleted() sip.delete(view) #assert tab_w.is_deleted() ``` The qtbot post-test cleanup would error due to the deleted view. --- qutebrowser/browser/browsertab.py | 4 ++++ qutebrowser/browser/commands.py | 10 ++-------- qutebrowser/browser/webelem.py | 7 +++++++ qutebrowser/browser/webengine/webengineelem.py | 2 ++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 547e276db..c8e595baf 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -22,6 +22,7 @@ import enum import itertools +import sip import attr from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt from PyQt5.QtGui import QIcon @@ -864,3 +865,6 @@ class AbstractTab(QWidget): except (AttributeError, RuntimeError) as exc: url = '<{}>'.format(exc.__class__.__name__) return utils.get_repr(self, tab_id=self.tab_id, url=url) + + def is_deleted(self): + return sip.isdeleted(self._widget) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 94825882a..781dfeb24 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1621,9 +1621,6 @@ class CommandDispatcher: ed = editor.ExternalEditor(self._tabbed_browser) ed.editing_finished.connect(functools.partial( self.on_editing_finished, elem)) - tab = self._current_widget() - tab.shutting_down.connect(functools.partial( - self.on_editor_orphaned, ed)) ed.edit(text, caret_position) @cmdutils.register(instance='command-dispatcher', scope='window') @@ -1647,14 +1644,11 @@ class CommandDispatcher: """ try: elem.set_value(text) + except webelem.OrphanedError as e: + message.warning('Edited element vanished') except webelem.Error as e: raise cmdexc.CommandError(str(e)) - def on_editor_orphaned(self, ed): - ed.editing_finished.disconnect() - ed.editing_finished.connect( - lambda: message.warning('Edited element vanished')) - @cmdutils.register(instance='command-dispatcher', maxsplit=0, scope='window') def insert_text(self, text): diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index 067f33cff..10496cac7 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -60,6 +60,13 @@ class Error(Exception): pass +class OrphanedError(Exception): + + """Raised when a webelement's parent has vanished.""" + + pass + + class AbstractWebElement(collections.abc.MutableMapping): """A wrapper around QtWebKit/QtWebEngine web element. diff --git a/qutebrowser/browser/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py index bdb8b4192..0e54ca80e 100644 --- a/qutebrowser/browser/webengine/webengineelem.py +++ b/qutebrowser/browser/webengine/webengineelem.py @@ -100,6 +100,8 @@ class WebEngineElement(webelem.AbstractWebElement): def _js_call(self, name, *args, callback=None): """Wrapper to run stuff from webelem.js.""" + if self._tab.is_deleted(): + raise webelem.OrphanedError("Tab containing element vanished") js_code = javascript.assemble('webelem', name, self._id, *args) self._tab.run_js_async(js_code, callback=callback)