We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Have option to disable Mouse 4 and Mouse 5 from jumping forward and back through tab history.
When using Mouse 4 or Mouse 5 as a global hotkey for another application, for example a voice chat program such as Discord or Mumble using either button as push to talk, qutebrowser still receives the button press and goes forward and backwards through history while focused. Some way to disable Mouse 4 and Mouse 5 from being used by qutebrowser would be cool.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `qutebrowser/browser/eventfilter.py`
Content:
```
1 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2 
3 # Copyright 2016-2020 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
4 #
5 # This file is part of qutebrowser.
6 #
7 # qutebrowser is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # qutebrowser is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.
19 
20 """Event handling for a browser tab."""
21 
22 from PyQt5.QtCore import QObject, QEvent, Qt, QTimer
23 
24 from qutebrowser.config import config
25 from qutebrowser.utils import message, log, usertypes, qtutils, objreg
26 from qutebrowser.misc import objects
27 from qutebrowser.keyinput import modeman
28 
29 
30 class ChildEventFilter(QObject):
31 
32     """An event filter re-adding TabEventFilter on ChildEvent.
33 
34     This is needed because QtWebEngine likes to randomly change its
35     focusProxy...
36 
37     FIXME:qtwebengine Add a test for this happening
38 
39     Attributes:
40         _filter: The event filter to install.
41         _widget: The widget expected to send out childEvents.
42     """
43 
44     def __init__(self, eventfilter, widget, win_id, parent=None):
45         super().__init__(parent)
46         self._filter = eventfilter
47         assert widget is not None
48         self._widget = widget
49         self._win_id = win_id
50 
51     def eventFilter(self, obj, event):
52         """Act on ChildAdded events."""
53         if event.type() == QEvent.ChildAdded:
54             child = event.child()
55             log.misc.debug("{} got new child {}, installing filter".format(
56                 obj, child))
57             assert obj is self._widget
58             child.installEventFilter(self._filter)
59 
60             if qtutils.version_check('5.11', compiled=False, exact=True):
61                 # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-68076
62                 pass_modes = [usertypes.KeyMode.command,
63                               usertypes.KeyMode.prompt,
64                               usertypes.KeyMode.yesno]
65                 if modeman.instance(self._win_id).mode not in pass_modes:
66                     tabbed_browser = objreg.get('tabbed-browser',
67                                                 scope='window',
68                                                 window=self._win_id)
69                     current_index = tabbed_browser.widget.currentIndex()
70                     try:
71                         widget_index = tabbed_browser.widget.indexOf(
72                             self._widget.parent())
73                     except RuntimeError:
74                         widget_index = -1
75                     if current_index == widget_index:
76                         QTimer.singleShot(0, self._widget.setFocus)
77 
78         elif event.type() == QEvent.ChildRemoved:
79             child = event.child()
80             log.misc.debug("{}: removed child {}".format(obj, child))
81 
82         return False
83 
84 
85 class TabEventFilter(QObject):
86 
87     """Handle mouse/keyboard events on a tab.
88 
89     Attributes:
90         _tab: The browsertab object this filter is installed on.
91         _handlers: A dict of handler functions for the handled events.
92         _ignore_wheel_event: Whether to ignore the next wheelEvent.
93         _check_insertmode_on_release: Whether an insertmode check should be
94                                       done when the mouse is released.
95     """
96 
97     def __init__(self, tab, *, parent=None):
98         super().__init__(parent)
99         self._tab = tab
100         self._handlers = {
101             QEvent.MouseButtonPress: self._handle_mouse_press,
102             QEvent.MouseButtonRelease: self._handle_mouse_release,
103             QEvent.Wheel: self._handle_wheel,
104             QEvent.ContextMenu: self._handle_context_menu,
105             QEvent.KeyRelease: self._handle_key_release,
106         }
107         self._ignore_wheel_event = False
108         self._check_insertmode_on_release = False
109 
110     def _handle_mouse_press(self, e):
111         """Handle pressing of a mouse button.
112 
113         Args:
114             e: The QMouseEvent.
115 
116         Return:
117             True if the event should be filtered, False otherwise.
118         """
119         is_rocker_gesture = (config.val.input.rocker_gestures and
120                              e.buttons() == Qt.LeftButton | Qt.RightButton)
121 
122         if e.button() in [Qt.XButton1, Qt.XButton2] or is_rocker_gesture:
123             self._mousepress_backforward(e)
124             return True
125 
126         self._ignore_wheel_event = True
127 
128         pos = e.pos()
129         if pos.x() < 0 or pos.y() < 0:
130             log.mouse.warning("Ignoring invalid click at {}".format(pos))
131             return False
132 
133         if e.button() != Qt.NoButton:
134             self._tab.elements.find_at_pos(pos, self._mousepress_insertmode_cb)
135 
136         return False
137 
138     def _handle_mouse_release(self, _e):
139         """Handle releasing of a mouse button.
140 
141         Args:
142             e: The QMouseEvent.
143 
144         Return:
145             True if the event should be filtered, False otherwise.
146         """
147         # We want to make sure we check the focus element after the WebView is
148         # updated completely.
149         QTimer.singleShot(0, self._mouserelease_insertmode)
150         return False
151 
152     def _handle_wheel(self, e):
153         """Zoom on Ctrl-Mousewheel.
154 
155         Args:
156             e: The QWheelEvent.
157 
158         Return:
159             True if the event should be filtered, False otherwise.
160         """
161         if self._ignore_wheel_event:
162             # See https://github.com/qutebrowser/qutebrowser/issues/395
163             self._ignore_wheel_event = False
164             return True
165 
166         # Don't allow scrolling while hinting
167         mode = modeman.instance(self._tab.win_id).mode
168         if mode == usertypes.KeyMode.hint:
169             return True
170 
171         elif e.modifiers() & Qt.ControlModifier:
172             if mode == usertypes.KeyMode.passthrough:
173                 return False
174 
175             divider = config.val.zoom.mouse_divider
176             if divider == 0:
177                 # Disable mouse zooming
178                 return True
179 
180             factor = self._tab.zoom.factor() + (e.angleDelta().y() / divider)
181             if factor < 0:
182                 return True
183 
184             perc = int(100 * factor)
185             message.info("Zoom level: {}%".format(perc), replace=True)
186             self._tab.zoom.set_factor(factor)
187             return True
188         elif (e.modifiers() & Qt.ShiftModifier and
189               not qtutils.version_check('5.9', compiled=False)):
190             if e.angleDelta().y() > 0:
191                 self._tab.scroller.left()
192             else:
193                 self._tab.scroller.right()
194             return True
195 
196         return False
197 
198     def _handle_context_menu(self, _e):
199         """Suppress context menus if rocker gestures are turned on.
200 
201         Args:
202             e: The QContextMenuEvent.
203 
204         Return:
205             True if the event should be filtered, False otherwise.
206         """
207         return config.val.input.rocker_gestures
208 
209     def _handle_key_release(self, e):
210         """Ignore repeated key release events going to the website.
211 
212         WORKAROUND for https://bugreports.qt.io/browse/QTBUG-77208
213 
214         Args:
215             e: The QKeyEvent.
216 
217         Return:
218             True if the event should be filtered, False otherwise.
219         """
220         return (e.isAutoRepeat() and
221                 qtutils.version_check('5.10', compiled=False) and
222                 not qtutils.version_check('5.14', compiled=False) and
223                 objects.backend == usertypes.Backend.QtWebEngine)
224 
225     def _mousepress_insertmode_cb(self, elem):
226         """Check if the clicked element is editable."""
227         if elem is None:
228             # Something didn't work out, let's find the focus element after
229             # a mouse release.
230             log.mouse.debug("Got None element, scheduling check on "
231                             "mouse release")
232             self._check_insertmode_on_release = True
233             return
234 
235         if elem.is_editable():
236             log.mouse.debug("Clicked editable element!")
237             if config.val.input.insert_mode.auto_enter:
238                 modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,
239                               'click', only_if_normal=True)
240         else:
241             log.mouse.debug("Clicked non-editable element!")
242             if config.val.input.insert_mode.auto_leave:
243                 modeman.leave(self._tab.win_id, usertypes.KeyMode.insert,
244                               'click', maybe=True)
245 
246     def _mouserelease_insertmode(self):
247         """If we have an insertmode check scheduled, handle it."""
248         if not self._check_insertmode_on_release:
249             return
250         self._check_insertmode_on_release = False
251 
252         def mouserelease_insertmode_cb(elem):
253             """Callback which gets called from JS."""
254             if elem is None:
255                 log.mouse.debug("Element vanished!")
256                 return
257 
258             if elem.is_editable():
259                 log.mouse.debug("Clicked editable element (delayed)!")
260                 modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,
261                               'click-delayed', only_if_normal=True)
262             else:
263                 log.mouse.debug("Clicked non-editable element (delayed)!")
264                 if config.val.input.insert_mode.auto_leave:
265                     modeman.leave(self._tab.win_id, usertypes.KeyMode.insert,
266                                   'click-delayed', maybe=True)
267 
268         self._tab.elements.find_focused(mouserelease_insertmode_cb)
269 
270     def _mousepress_backforward(self, e):
271         """Handle back/forward mouse button presses.
272 
273         Args:
274             e: The QMouseEvent.
275 
276         Return:
277             True if the event should be filtered, False otherwise.
278         """
279         if e.button() in [Qt.XButton1, Qt.LeftButton]:
280             # Back button on mice which have it, or rocker gesture
281             if self._tab.history.can_go_back():
282                 self._tab.history.back()
283             else:
284                 message.error("At beginning of history.")
285         elif e.button() in [Qt.XButton2, Qt.RightButton]:
286             # Forward button on mice which have it, or rocker gesture
287             if self._tab.history.can_go_forward():
288                 self._tab.history.forward()
289             else:
290                 message.error("At end of history.")
291 
292     def eventFilter(self, obj, event):
293         """Filter events going to a QWeb(Engine)View.
294 
295         Return:
296             True if the event should be filtered, False otherwise.
297         """
298         evtype = event.type()
299         if evtype not in self._handlers:
300             return False
301         if obj is not self._tab.private_api.event_target():
302             log.mouse.debug("Ignoring {} to {}".format(
303                 event.__class__.__name__, obj))
304             return False
305         return self._handlers[evtype](event)
306
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/qutebrowser/browser/eventfilter.py b/qutebrowser/browser/eventfilter.py
--- a/qutebrowser/browser/eventfilter.py
+++ b/qutebrowser/browser/eventfilter.py
@@ -116,7 +116,7 @@
         Return:
             True if the event should be filtered, False otherwise.
         """
-        is_rocker_gesture = (config.val.input.rocker_gestures and
+        is_rocker_gesture = (config.val.input.mouse.rocker_gestures and
                              e.buttons() == Qt.LeftButton | Qt.RightButton)
 
         if e.button() in [Qt.XButton1, Qt.XButton2] or is_rocker_gesture:
@@ -204,7 +204,7 @@
         Return:
             True if the event should be filtered, False otherwise.
         """
-        return config.val.input.rocker_gestures
+        return config.val.input.mouse.rocker_gestures
 
     def _handle_key_release(self, e):
         """Ignore repeated key release events going to the website.
@@ -276,6 +276,11 @@
         Return:
             True if the event should be filtered, False otherwise.
         """
+        if (not config.val.input.mouse.back_forward_buttons and
+                e.button() in [Qt.XButton1, Qt.XButton2]):
+            # Back and forward on mice are disabled
+            return
+
         if e.button() in [Qt.XButton1, Qt.LeftButton]:
             # Back button on mice which have it, or rocker gesture
             if self._tab.history.can_go_back():

{"golden_diff": "diff --git a/qutebrowser/browser/eventfilter.py b/qutebrowser/browser/eventfilter.py\n--- a/qutebrowser/browser/eventfilter.py\n+++ b/qutebrowser/browser/eventfilter.py\n@@ -116,7 +116,7 @@\n         Return:\n             True if the event should be filtered, False otherwise.\n         \"\"\"\n-        is_rocker_gesture = (config.val.input.rocker_gestures and\n+        is_rocker_gesture = (config.val.input.mouse.rocker_gestures and\n                              e.buttons() == Qt.LeftButton | Qt.RightButton)\n \n         if e.button() in [Qt.XButton1, Qt.XButton2] or is_rocker_gesture:\n@@ -204,7 +204,7 @@\n         Return:\n             True if the event should be filtered, False otherwise.\n         \"\"\"\n-        return config.val.input.rocker_gestures\n+        return config.val.input.mouse.rocker_gestures\n \n     def _handle_key_release(self, e):\n         \"\"\"Ignore repeated key release events going to the website.\n@@ -276,6 +276,11 @@\n         Return:\n             True if the event should be filtered, False otherwise.\n         \"\"\"\n+        if (not config.val.input.mouse.back_forward_buttons and\n+                e.button() in [Qt.XButton1, Qt.XButton2]):\n+            # Back and forward on mice are disabled\n+            return\n+\n         if e.button() in [Qt.XButton1, Qt.LeftButton]:\n             # Back button on mice which have it, or rocker gesture\n             if self._tab.history.can_go_back():\n", "issue": "Have option to disable Mouse 4 and Mouse 5 from jumping forward and back through tab history.\nWhen using Mouse 4 or Mouse 5 as a global hotkey for another application, for example a voice chat program such as Discord or Mumble using either button as push to talk, qutebrowser still receives the button press and goes forward and backwards through history while focused. Some way to disable Mouse 4 and Mouse 5 from being used by qutebrowser would be cool.\n", "before_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2016-2020 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Event handling for a browser tab.\"\"\"\n\nfrom PyQt5.QtCore import QObject, QEvent, Qt, QTimer\n\nfrom qutebrowser.config import config\nfrom qutebrowser.utils import message, log, usertypes, qtutils, objreg\nfrom qutebrowser.misc import objects\nfrom qutebrowser.keyinput import modeman\n\n\nclass ChildEventFilter(QObject):\n\n    \"\"\"An event filter re-adding TabEventFilter on ChildEvent.\n\n    This is needed because QtWebEngine likes to randomly change its\n    focusProxy...\n\n    FIXME:qtwebengine Add a test for this happening\n\n    Attributes:\n        _filter: The event filter to install.\n        _widget: The widget expected to send out childEvents.\n    \"\"\"\n\n    def __init__(self, eventfilter, widget, win_id, parent=None):\n        super().__init__(parent)\n        self._filter = eventfilter\n        assert widget is not None\n        self._widget = widget\n        self._win_id = win_id\n\n    def eventFilter(self, obj, event):\n        \"\"\"Act on ChildAdded events.\"\"\"\n        if event.type() == QEvent.ChildAdded:\n            child = event.child()\n            log.misc.debug(\"{} got new child {}, installing filter\".format(\n                obj, child))\n            assert obj is self._widget\n            child.installEventFilter(self._filter)\n\n            if qtutils.version_check('5.11', compiled=False, exact=True):\n                # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-68076\n                pass_modes = [usertypes.KeyMode.command,\n                              usertypes.KeyMode.prompt,\n                              usertypes.KeyMode.yesno]\n                if modeman.instance(self._win_id).mode not in pass_modes:\n                    tabbed_browser = objreg.get('tabbed-browser',\n                                                scope='window',\n                                                window=self._win_id)\n                    current_index = tabbed_browser.widget.currentIndex()\n                    try:\n                        widget_index = tabbed_browser.widget.indexOf(\n                            self._widget.parent())\n                    except RuntimeError:\n                        widget_index = -1\n                    if current_index == widget_index:\n                        QTimer.singleShot(0, self._widget.setFocus)\n\n        elif event.type() == QEvent.ChildRemoved:\n            child = event.child()\n            log.misc.debug(\"{}: removed child {}\".format(obj, child))\n\n        return False\n\n\nclass TabEventFilter(QObject):\n\n    \"\"\"Handle mouse/keyboard events on a tab.\n\n    Attributes:\n        _tab: The browsertab object this filter is installed on.\n        _handlers: A dict of handler functions for the handled events.\n        _ignore_wheel_event: Whether to ignore the next wheelEvent.\n        _check_insertmode_on_release: Whether an insertmode check should be\n                                      done when the mouse is released.\n    \"\"\"\n\n    def __init__(self, tab, *, parent=None):\n        super().__init__(parent)\n        self._tab = tab\n        self._handlers = {\n            QEvent.MouseButtonPress: self._handle_mouse_press,\n            QEvent.MouseButtonRelease: self._handle_mouse_release,\n            QEvent.Wheel: self._handle_wheel,\n            QEvent.ContextMenu: self._handle_context_menu,\n            QEvent.KeyRelease: self._handle_key_release,\n        }\n        self._ignore_wheel_event = False\n        self._check_insertmode_on_release = False\n\n    def _handle_mouse_press(self, e):\n        \"\"\"Handle pressing of a mouse button.\n\n        Args:\n            e: The QMouseEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        is_rocker_gesture = (config.val.input.rocker_gestures and\n                             e.buttons() == Qt.LeftButton | Qt.RightButton)\n\n        if e.button() in [Qt.XButton1, Qt.XButton2] or is_rocker_gesture:\n            self._mousepress_backforward(e)\n            return True\n\n        self._ignore_wheel_event = True\n\n        pos = e.pos()\n        if pos.x() < 0 or pos.y() < 0:\n            log.mouse.warning(\"Ignoring invalid click at {}\".format(pos))\n            return False\n\n        if e.button() != Qt.NoButton:\n            self._tab.elements.find_at_pos(pos, self._mousepress_insertmode_cb)\n\n        return False\n\n    def _handle_mouse_release(self, _e):\n        \"\"\"Handle releasing of a mouse button.\n\n        Args:\n            e: The QMouseEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        # We want to make sure we check the focus element after the WebView is\n        # updated completely.\n        QTimer.singleShot(0, self._mouserelease_insertmode)\n        return False\n\n    def _handle_wheel(self, e):\n        \"\"\"Zoom on Ctrl-Mousewheel.\n\n        Args:\n            e: The QWheelEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        if self._ignore_wheel_event:\n            # See https://github.com/qutebrowser/qutebrowser/issues/395\n            self._ignore_wheel_event = False\n            return True\n\n        # Don't allow scrolling while hinting\n        mode = modeman.instance(self._tab.win_id).mode\n        if mode == usertypes.KeyMode.hint:\n            return True\n\n        elif e.modifiers() & Qt.ControlModifier:\n            if mode == usertypes.KeyMode.passthrough:\n                return False\n\n            divider = config.val.zoom.mouse_divider\n            if divider == 0:\n                # Disable mouse zooming\n                return True\n\n            factor = self._tab.zoom.factor() + (e.angleDelta().y() / divider)\n            if factor < 0:\n                return True\n\n            perc = int(100 * factor)\n            message.info(\"Zoom level: {}%\".format(perc), replace=True)\n            self._tab.zoom.set_factor(factor)\n            return True\n        elif (e.modifiers() & Qt.ShiftModifier and\n              not qtutils.version_check('5.9', compiled=False)):\n            if e.angleDelta().y() > 0:\n                self._tab.scroller.left()\n            else:\n                self._tab.scroller.right()\n            return True\n\n        return False\n\n    def _handle_context_menu(self, _e):\n        \"\"\"Suppress context menus if rocker gestures are turned on.\n\n        Args:\n            e: The QContextMenuEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        return config.val.input.rocker_gestures\n\n    def _handle_key_release(self, e):\n        \"\"\"Ignore repeated key release events going to the website.\n\n        WORKAROUND for https://bugreports.qt.io/browse/QTBUG-77208\n\n        Args:\n            e: The QKeyEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        return (e.isAutoRepeat() and\n                qtutils.version_check('5.10', compiled=False) and\n                not qtutils.version_check('5.14', compiled=False) and\n                objects.backend == usertypes.Backend.QtWebEngine)\n\n    def _mousepress_insertmode_cb(self, elem):\n        \"\"\"Check if the clicked element is editable.\"\"\"\n        if elem is None:\n            # Something didn't work out, let's find the focus element after\n            # a mouse release.\n            log.mouse.debug(\"Got None element, scheduling check on \"\n                            \"mouse release\")\n            self._check_insertmode_on_release = True\n            return\n\n        if elem.is_editable():\n            log.mouse.debug(\"Clicked editable element!\")\n            if config.val.input.insert_mode.auto_enter:\n                modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,\n                              'click', only_if_normal=True)\n        else:\n            log.mouse.debug(\"Clicked non-editable element!\")\n            if config.val.input.insert_mode.auto_leave:\n                modeman.leave(self._tab.win_id, usertypes.KeyMode.insert,\n                              'click', maybe=True)\n\n    def _mouserelease_insertmode(self):\n        \"\"\"If we have an insertmode check scheduled, handle it.\"\"\"\n        if not self._check_insertmode_on_release:\n            return\n        self._check_insertmode_on_release = False\n\n        def mouserelease_insertmode_cb(elem):\n            \"\"\"Callback which gets called from JS.\"\"\"\n            if elem is None:\n                log.mouse.debug(\"Element vanished!\")\n                return\n\n            if elem.is_editable():\n                log.mouse.debug(\"Clicked editable element (delayed)!\")\n                modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,\n                              'click-delayed', only_if_normal=True)\n            else:\n                log.mouse.debug(\"Clicked non-editable element (delayed)!\")\n                if config.val.input.insert_mode.auto_leave:\n                    modeman.leave(self._tab.win_id, usertypes.KeyMode.insert,\n                                  'click-delayed', maybe=True)\n\n        self._tab.elements.find_focused(mouserelease_insertmode_cb)\n\n    def _mousepress_backforward(self, e):\n        \"\"\"Handle back/forward mouse button presses.\n\n        Args:\n            e: The QMouseEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        if e.button() in [Qt.XButton1, Qt.LeftButton]:\n            # Back button on mice which have it, or rocker gesture\n            if self._tab.history.can_go_back():\n                self._tab.history.back()\n            else:\n                message.error(\"At beginning of history.\")\n        elif e.button() in [Qt.XButton2, Qt.RightButton]:\n            # Forward button on mice which have it, or rocker gesture\n            if self._tab.history.can_go_forward():\n                self._tab.history.forward()\n            else:\n                message.error(\"At end of history.\")\n\n    def eventFilter(self, obj, event):\n        \"\"\"Filter events going to a QWeb(Engine)View.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        evtype = event.type()\n        if evtype not in self._handlers:\n            return False\n        if obj is not self._tab.private_api.event_target():\n            log.mouse.debug(\"Ignoring {} to {}\".format(\n                event.__class__.__name__, obj))\n            return False\n        return self._handlers[evtype](event)\n", "path": "qutebrowser/browser/eventfilter.py"}], "after_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2016-2020 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Event handling for a browser tab.\"\"\"\n\nfrom PyQt5.QtCore import QObject, QEvent, Qt, QTimer\n\nfrom qutebrowser.config import config\nfrom qutebrowser.utils import message, log, usertypes, qtutils, objreg\nfrom qutebrowser.misc import objects\nfrom qutebrowser.keyinput import modeman\n\n\nclass ChildEventFilter(QObject):\n\n    \"\"\"An event filter re-adding TabEventFilter on ChildEvent.\n\n    This is needed because QtWebEngine likes to randomly change its\n    focusProxy...\n\n    FIXME:qtwebengine Add a test for this happening\n\n    Attributes:\n        _filter: The event filter to install.\n        _widget: The widget expected to send out childEvents.\n    \"\"\"\n\n    def __init__(self, eventfilter, widget, win_id, parent=None):\n        super().__init__(parent)\n        self._filter = eventfilter\n        assert widget is not None\n        self._widget = widget\n        self._win_id = win_id\n\n    def eventFilter(self, obj, event):\n        \"\"\"Act on ChildAdded events.\"\"\"\n        if event.type() == QEvent.ChildAdded:\n            child = event.child()\n            log.misc.debug(\"{} got new child {}, installing filter\".format(\n                obj, child))\n            assert obj is self._widget\n            child.installEventFilter(self._filter)\n\n            if qtutils.version_check('5.11', compiled=False, exact=True):\n                # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-68076\n                pass_modes = [usertypes.KeyMode.command,\n                              usertypes.KeyMode.prompt,\n                              usertypes.KeyMode.yesno]\n                if modeman.instance(self._win_id).mode not in pass_modes:\n                    tabbed_browser = objreg.get('tabbed-browser',\n                                                scope='window',\n                                                window=self._win_id)\n                    current_index = tabbed_browser.widget.currentIndex()\n                    try:\n                        widget_index = tabbed_browser.widget.indexOf(\n                            self._widget.parent())\n                    except RuntimeError:\n                        widget_index = -1\n                    if current_index == widget_index:\n                        QTimer.singleShot(0, self._widget.setFocus)\n\n        elif event.type() == QEvent.ChildRemoved:\n            child = event.child()\n            log.misc.debug(\"{}: removed child {}\".format(obj, child))\n\n        return False\n\n\nclass TabEventFilter(QObject):\n\n    \"\"\"Handle mouse/keyboard events on a tab.\n\n    Attributes:\n        _tab: The browsertab object this filter is installed on.\n        _handlers: A dict of handler functions for the handled events.\n        _ignore_wheel_event: Whether to ignore the next wheelEvent.\n        _check_insertmode_on_release: Whether an insertmode check should be\n                                      done when the mouse is released.\n    \"\"\"\n\n    def __init__(self, tab, *, parent=None):\n        super().__init__(parent)\n        self._tab = tab\n        self._handlers = {\n            QEvent.MouseButtonPress: self._handle_mouse_press,\n            QEvent.MouseButtonRelease: self._handle_mouse_release,\n            QEvent.Wheel: self._handle_wheel,\n            QEvent.ContextMenu: self._handle_context_menu,\n            QEvent.KeyRelease: self._handle_key_release,\n        }\n        self._ignore_wheel_event = False\n        self._check_insertmode_on_release = False\n\n    def _handle_mouse_press(self, e):\n        \"\"\"Handle pressing of a mouse button.\n\n        Args:\n            e: The QMouseEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        is_rocker_gesture = (config.val.input.mouse.rocker_gestures and\n                             e.buttons() == Qt.LeftButton | Qt.RightButton)\n\n        if e.button() in [Qt.XButton1, Qt.XButton2] or is_rocker_gesture:\n            self._mousepress_backforward(e)\n            return True\n\n        self._ignore_wheel_event = True\n\n        pos = e.pos()\n        if pos.x() < 0 or pos.y() < 0:\n            log.mouse.warning(\"Ignoring invalid click at {}\".format(pos))\n            return False\n\n        if e.button() != Qt.NoButton:\n            self._tab.elements.find_at_pos(pos, self._mousepress_insertmode_cb)\n\n        return False\n\n    def _handle_mouse_release(self, _e):\n        \"\"\"Handle releasing of a mouse button.\n\n        Args:\n            e: The QMouseEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        # We want to make sure we check the focus element after the WebView is\n        # updated completely.\n        QTimer.singleShot(0, self._mouserelease_insertmode)\n        return False\n\n    def _handle_wheel(self, e):\n        \"\"\"Zoom on Ctrl-Mousewheel.\n\n        Args:\n            e: The QWheelEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        if self._ignore_wheel_event:\n            # See https://github.com/qutebrowser/qutebrowser/issues/395\n            self._ignore_wheel_event = False\n            return True\n\n        # Don't allow scrolling while hinting\n        mode = modeman.instance(self._tab.win_id).mode\n        if mode == usertypes.KeyMode.hint:\n            return True\n\n        elif e.modifiers() & Qt.ControlModifier:\n            if mode == usertypes.KeyMode.passthrough:\n                return False\n\n            divider = config.val.zoom.mouse_divider\n            if divider == 0:\n                # Disable mouse zooming\n                return True\n\n            factor = self._tab.zoom.factor() + (e.angleDelta().y() / divider)\n            if factor < 0:\n                return True\n\n            perc = int(100 * factor)\n            message.info(\"Zoom level: {}%\".format(perc), replace=True)\n            self._tab.zoom.set_factor(factor)\n            return True\n        elif (e.modifiers() & Qt.ShiftModifier and\n              not qtutils.version_check('5.9', compiled=False)):\n            if e.angleDelta().y() > 0:\n                self._tab.scroller.left()\n            else:\n                self._tab.scroller.right()\n            return True\n\n        return False\n\n    def _handle_context_menu(self, _e):\n        \"\"\"Suppress context menus if rocker gestures are turned on.\n\n        Args:\n            e: The QContextMenuEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        return config.val.input.mouse.rocker_gestures\n\n    def _handle_key_release(self, e):\n        \"\"\"Ignore repeated key release events going to the website.\n\n        WORKAROUND for https://bugreports.qt.io/browse/QTBUG-77208\n\n        Args:\n            e: The QKeyEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        return (e.isAutoRepeat() and\n                qtutils.version_check('5.10', compiled=False) and\n                not qtutils.version_check('5.14', compiled=False) and\n                objects.backend == usertypes.Backend.QtWebEngine)\n\n    def _mousepress_insertmode_cb(self, elem):\n        \"\"\"Check if the clicked element is editable.\"\"\"\n        if elem is None:\n            # Something didn't work out, let's find the focus element after\n            # a mouse release.\n            log.mouse.debug(\"Got None element, scheduling check on \"\n                            \"mouse release\")\n            self._check_insertmode_on_release = True\n            return\n\n        if elem.is_editable():\n            log.mouse.debug(\"Clicked editable element!\")\n            if config.val.input.insert_mode.auto_enter:\n                modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,\n                              'click', only_if_normal=True)\n        else:\n            log.mouse.debug(\"Clicked non-editable element!\")\n            if config.val.input.insert_mode.auto_leave:\n                modeman.leave(self._tab.win_id, usertypes.KeyMode.insert,\n                              'click', maybe=True)\n\n    def _mouserelease_insertmode(self):\n        \"\"\"If we have an insertmode check scheduled, handle it.\"\"\"\n        if not self._check_insertmode_on_release:\n            return\n        self._check_insertmode_on_release = False\n\n        def mouserelease_insertmode_cb(elem):\n            \"\"\"Callback which gets called from JS.\"\"\"\n            if elem is None:\n                log.mouse.debug(\"Element vanished!\")\n                return\n\n            if elem.is_editable():\n                log.mouse.debug(\"Clicked editable element (delayed)!\")\n                modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,\n                              'click-delayed', only_if_normal=True)\n            else:\n                log.mouse.debug(\"Clicked non-editable element (delayed)!\")\n                if config.val.input.insert_mode.auto_leave:\n                    modeman.leave(self._tab.win_id, usertypes.KeyMode.insert,\n                                  'click-delayed', maybe=True)\n\n        self._tab.elements.find_focused(mouserelease_insertmode_cb)\n\n    def _mousepress_backforward(self, e):\n        \"\"\"Handle back/forward mouse button presses.\n\n        Args:\n            e: The QMouseEvent.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        if (not config.val.input.mouse.back_forward_buttons and\n                e.button() in [Qt.XButton1, Qt.XButton2]):\n            # Back and forward on mice are disabled\n            return\n\n        if e.button() in [Qt.XButton1, Qt.LeftButton]:\n            # Back button on mice which have it, or rocker gesture\n            if self._tab.history.can_go_back():\n                self._tab.history.back()\n            else:\n                message.error(\"At beginning of history.\")\n        elif e.button() in [Qt.XButton2, Qt.RightButton]:\n            # Forward button on mice which have it, or rocker gesture\n            if self._tab.history.can_go_forward():\n                self._tab.history.forward()\n            else:\n                message.error(\"At end of history.\")\n\n    def eventFilter(self, obj, event):\n        \"\"\"Filter events going to a QWeb(Engine)View.\n\n        Return:\n            True if the event should be filtered, False otherwise.\n        \"\"\"\n        evtype = event.type()\n        if evtype not in self._handlers:\n            return False\n        if obj is not self._tab.private_api.event_target():\n            log.mouse.debug(\"Ignoring {} to {}\".format(\n                event.__class__.__name__, obj))\n            return False\n        return self._handlers[evtype](event)\n", "path": "qutebrowser/browser/eventfilter.py"}]}