Install MouseEventFilter on new WebEngine children

For some reason, when e.g. visiting duckduckgo and then heise.de,
QtWebEngine suddenly gets a new QOpenGLWidget as focusProxy.

We install an extra eventFilter observing the ChildAdded event and
re-adding the MouseEventFilter when that happens.
This commit is contained in:
Florian Bruhin 2016-08-10 19:42:24 +02:00
parent d4f30bd100
commit 64afc562b6
5 changed files with 44 additions and 17 deletions

View File

@ -485,9 +485,6 @@ class AbstractTab(QWidget):
self._mouse_event_filter = mouse.MouseEventFilter(self, parent=self) self._mouse_event_filter = mouse.MouseEventFilter(self, parent=self)
self.backend = None self.backend = None
def _event_filter_target(self):
raise NotImplementedError
def _set_widget(self, widget): def _set_widget(self, widget):
# pylint: disable=protected-access # pylint: disable=protected-access
self._widget = widget self._widget = widget
@ -499,8 +496,10 @@ class AbstractTab(QWidget):
self.search._widget = widget self.search._widget = widget
self.printing._widget = widget self.printing._widget = widget
widget.mouse_wheel_zoom.connect(self.zoom._on_mouse_wheel_zoom) widget.mouse_wheel_zoom.connect(self.zoom._on_mouse_wheel_zoom)
event_filter_target = self._event_filter_target() self._install_event_filter()
event_filter_target.installEventFilter(self._mouse_event_filter)
def _install_event_filter(self):
raise NotImplementedError
def _set_load_status(self, val): def _set_load_status(self, val):
"""Setter for load_status.""" """Setter for load_status."""

View File

@ -21,12 +21,38 @@
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.utils import message from qutebrowser.utils import message, debug, log
from PyQt5.QtCore import QObject, QEvent, Qt from PyQt5.QtCore import QObject, QEvent, Qt
class ChildEventFilter(QObject):
"""An event filter re-adding MouseEventFilter on ChildEvent.
This is needed because QtWebEngine likes to randomly change its
focusProxy...
FIXME:qtwebengine Add a test for this happening
"""
def __init__(self, eventfilter, widget, parent=None):
super().__init__(parent)
self._filter = eventfilter
assert widget is not None
self._widget = widget
def eventFilter(self, obj, event):
if event.type() == QEvent.ChildAdded:
child = event.child()
log.mouse.debug("{} got new child {}, installing filter".format(
obj, child))
assert obj is self._widget
child.installEventFilter(self._filter)
return False
class MouseEventFilter(QObject): class MouseEventFilter(QObject):
"""Handle mouse events on a tab.""" """Handle mouse events on a tab."""
@ -38,7 +64,7 @@ class MouseEventFilter(QObject):
QEvent.MouseButtonPress: self._handle_mouse_press, QEvent.MouseButtonPress: self._handle_mouse_press,
} }
def _handle_mouse_press(self, e): def _handle_mouse_press(self, _obj, e):
"""Handle pressing of a mouse button.""" """Handle pressing of a mouse button."""
is_rocker_gesture = (config.get('input', 'rocker-gestures') and is_rocker_gesture = (config.get('input', 'rocker-gestures') and
e.buttons() == Qt.LeftButton | Qt.RightButton) e.buttons() == Qt.LeftButton | Qt.RightButton)
@ -69,9 +95,9 @@ class MouseEventFilter(QObject):
message.error(self._tab.win_id, "At end of history.", message.error(self._tab.win_id, "At end of history.",
immediately=True) immediately=True)
def eventFilter(self, _obj, event): def eventFilter(self, obj, event):
"""Filter events going to a QWeb(Engine)View.""" """Filter events going to a QWeb(Engine)View."""
evtype = event.type() evtype = event.type()
if evtype not in self._handlers: if evtype not in self._handlers:
return False return False
return self._handlers[evtype](event) return self._handlers[evtype](obj, event)

View File

@ -31,7 +31,7 @@ from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript
# pylint: enable=no-name-in-module,import-error,useless-suppression # pylint: enable=no-name-in-module,import-error,useless-suppression
from qutebrowser.browser import browsertab from qutebrowser.browser import browsertab, mouse
from qutebrowser.browser.webengine import webview, webengineelem from qutebrowser.browser.webengine import webview, webengineelem
from qutebrowser.utils import usertypes, qtutils, log, javascript, utils from qutebrowser.utils import usertypes, qtutils, log, javascript, utils
@ -335,6 +335,7 @@ class WebEngineTab(browsertab.AbstractTab):
self.backend = usertypes.Backend.QtWebEngine self.backend = usertypes.Backend.QtWebEngine
# init js stuff # init js stuff
self._init_js() self._init_js()
self._child_event_filter = None
def _init_js(self): def _init_js(self):
js_code = '\n'.join([ js_code = '\n'.join([
@ -359,8 +360,12 @@ class WebEngineTab(browsertab.AbstractTab):
# FIXME:qtwebengine what about runsOnSubFrames? # FIXME:qtwebengine what about runsOnSubFrames?
page.scripts().insert(script) page.scripts().insert(script)
def _event_filter_target(self): def _install_event_filter(self):
return self._widget.focusProxy() self._widget.focusProxy().installEventFilter(self._mouse_event_filter)
self._child_event_filter = mouse.ChildEventFilter(
eventfilter=self._mouse_event_filter, widget=self._widget,
parent=self)
self._widget.installEventFilter(self._child_event_filter)
def openurl(self, url): def openurl(self, url):
self._openurl_prepare(url) self._openurl_prepare(url)

View File

@ -510,8 +510,8 @@ class WebKitTab(browsertab.AbstractTab):
self.zoom.set_default() self.zoom.set_default()
self.backend = usertypes.Backend.QtWebKit self.backend = usertypes.Backend.QtWebKit
def _event_filter_target(self): def _install_event_filter(self):
return self._widget self._widget.installEventFilter(self._mouse_event_filter)
def openurl(self, url): def openurl(self, url):
self._openurl_prepare(url) self._openurl_prepare(url)

View File

@ -107,9 +107,6 @@ class Tab(browsertab.AbstractTab):
self.search = browsertab.AbstractSearch(parent=self) self.search = browsertab.AbstractSearch(parent=self)
self.printing = browsertab.AbstractPrinting() self.printing = browsertab.AbstractPrinting()
def _event_filter_target(self):
return self._widget
@pytest.mark.skipif(PYQT_VERSION < 0x050600, @pytest.mark.skipif(PYQT_VERSION < 0x050600,
reason='Causes segfaults, see #1638') reason='Causes segfaults, see #1638')