We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
'Your internet access is blocked' when using keyboard 'f' to follow a link in a local file to a remote one, but works if clicking with mouse
**Version info**:
<!-- Please copy the first block from :version, not just the qutebrowser version -->
qutebrowser v3.0.0
Git commit: 
Backend: QtWebEngine 6.5.2, based on Chromium 108.0.5359.220 (from api)
Qt: 6.5.2

**Does the bug happen if you start with `--temp-basedir`?**:
Yes.

**Description**
I have a bookmarks.html file with commonly-used links.  I load the file into qutebrowser with ':open -t file:///home/chris/ckstemp.html'.  If I use the keyboard shortcut 'f' to follow a link, then press the link letter (say, 'a'), I get a 'Your internet access is blocked' error page.  If instead I click the link with the mouse, the page loads as expected (no error).

I have tried deleting the .cache, and I've tried clearing my browser history.

This is new behavior since I upgraded to qutebrowser 3.0.0 this morning.

**How to reproduce**
<!-- Link to the affected site, or steps to reproduce the issue (if possible/applicable). -->

See above for the steps to reproduces.  Here is the contents of my ckstemp.html file:

```
<TITLE>Bookmarks</TITLE>
<DL><p>
    <DT><H1>Bookmarks</H1>
        <DT><H3>Comics</H3>
        <DL><p>
            <DT><A HREF="https://www.gocomics.com/foxtrot">Foxtrot</A>
         </DL>
</DL>
```



--- 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/webengine/webengineelem.py`
Content:
```
1 # SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org>
2 #
3 # SPDX-License-Identifier: GPL-3.0-or-later
4 
5 """QtWebEngine specific part of the web element API."""
6 
7 from typing import (
8     TYPE_CHECKING, Any, Callable, Dict, Iterator, Optional, Set, Tuple, Union)
9 
10 from qutebrowser.qt.core import QRect, QEventLoop
11 from qutebrowser.qt.widgets import QApplication
12 from qutebrowser.qt.webenginecore import QWebEngineSettings
13 
14 from qutebrowser.utils import log, javascript, urlutils, usertypes, utils, version
15 from qutebrowser.browser import webelem
16 
17 if TYPE_CHECKING:
18     from qutebrowser.browser.webengine import webenginetab
19 
20 
21 class WebEngineElement(webelem.AbstractWebElement):
22 
23     """A web element for QtWebEngine, using JS under the hood."""
24 
25     _tab: "webenginetab.WebEngineTab"
26 
27     def __init__(self, js_dict: Dict[str, Any],
28                  tab: 'webenginetab.WebEngineTab') -> None:
29         super().__init__(tab)
30         # Do some sanity checks on the data we get from JS
31         js_dict_types: Dict[str, Union[type, Tuple[type, ...]]] = {
32             'id': int,
33             'text': str,
34             'value': (str, int, float),
35             'tag_name': str,
36             'outer_xml': str,
37             'class_name': str,
38             'rects': list,
39             'attributes': dict,
40             'is_content_editable': bool,
41             'caret_position': (int, type(None)),
42         }
43         assert set(js_dict.keys()).issubset(js_dict_types.keys())
44         for name, typ in js_dict_types.items():
45             if name in js_dict and not isinstance(js_dict[name], typ):
46                 raise TypeError("Got {} for {} from JS but expected {}: "
47                                 "{}".format(type(js_dict[name]), name, typ,
48                                             js_dict))
49         for name, value in js_dict['attributes'].items():
50             if not isinstance(name, str):
51                 raise TypeError("Got {} ({}) for attribute name from JS: "
52                                 "{}".format(name, type(name), js_dict))
53             if not isinstance(value, str):
54                 raise TypeError("Got {} ({}) for attribute {} from JS: "
55                                 "{}".format(value, type(value), name, js_dict))
56         for rect in js_dict['rects']:
57             assert set(rect.keys()) == {'top', 'right', 'bottom', 'left',
58                                         'height', 'width'}, rect.keys()
59             for value in rect.values():
60                 if not isinstance(value, (int, float)):
61                     raise TypeError("Got {} ({}) for rect from JS: "
62                                     "{}".format(value, type(value), js_dict))
63 
64         self._id = js_dict['id']
65         self._js_dict = js_dict
66 
67     def __str__(self) -> str:
68         return self._js_dict.get('text', '')
69 
70     def __eq__(self, other: object) -> bool:
71         if not isinstance(other, WebEngineElement):
72             return NotImplemented
73         return self._id == other._id
74 
75     def __getitem__(self, key: str) -> str:
76         attrs = self._js_dict['attributes']
77         return attrs[key]
78 
79     def __setitem__(self, key: str, val: str) -> None:
80         self._js_dict['attributes'][key] = val
81         self._js_call('set_attribute', key, val)
82 
83     def __delitem__(self, key: str) -> None:
84         utils.unused(key)
85         log.stub()
86 
87     def __iter__(self) -> Iterator[str]:
88         return iter(self._js_dict['attributes'])
89 
90     def __len__(self) -> int:
91         return len(self._js_dict['attributes'])
92 
93     def _js_call(self, name: str, *args: webelem.JsValueType,
94                  callback: Callable[[Any], None] = None) -> None:
95         """Wrapper to run stuff from webelem.js."""
96         if self._tab.is_deleted():
97             raise webelem.OrphanedError("Tab containing element vanished")
98         js_code = javascript.assemble('webelem', name, self._id, *args)
99         self._tab.run_js_async(js_code, callback=callback)
100 
101     def has_frame(self) -> bool:
102         return True
103 
104     def geometry(self) -> QRect:
105         log.stub()
106         return QRect()
107 
108     def classes(self) -> Set[str]:
109         """Get a list of classes assigned to this element."""
110         return set(self._js_dict['class_name'].split())
111 
112     def tag_name(self) -> str:
113         """Get the tag name of this element.
114 
115         The returned name will always be lower-case.
116         """
117         tag = self._js_dict['tag_name']
118         assert isinstance(tag, str), tag
119         return tag.lower()
120 
121     def outer_xml(self) -> str:
122         """Get the full HTML representation of this element."""
123         return self._js_dict['outer_xml']
124 
125     def is_content_editable_prop(self) -> bool:
126         return self._js_dict['is_content_editable']
127 
128     def value(self) -> webelem.JsValueType:
129         return self._js_dict.get('value', None)
130 
131     def set_value(self, value: webelem.JsValueType) -> None:
132         self._js_call('set_value', value)
133 
134     def dispatch_event(self, event: str,
135                        bubbles: bool = False,
136                        cancelable: bool = False,
137                        composed: bool = False) -> None:
138         self._js_call('dispatch_event', event, bubbles, cancelable, composed)
139 
140     def caret_position(self) -> Optional[int]:
141         """Get the text caret position for the current element.
142 
143         If the element is not a text element, None is returned.
144         """
145         return self._js_dict.get('caret_position', None)
146 
147     def insert_text(self, text: str) -> None:
148         if not self.is_editable(strict=True):
149             raise webelem.Error("Element is not editable!")
150         log.webelem.debug("Inserting text into element {!r}".format(self))
151         self._js_call('insert_text', text)
152 
153     def rect_on_view(self, *, elem_geometry: QRect = None,
154                      no_js: bool = False) -> QRect:
155         """Get the geometry of the element relative to the webview.
156 
157         Skipping of small rectangles is due to <a> elements containing other
158         elements with "display:block" style, see
159         https://github.com/qutebrowser/qutebrowser/issues/1298
160 
161         Args:
162             elem_geometry: The geometry of the element, or None.
163                            Ignored with QtWebEngine.
164             no_js: Fall back to the Python implementation.
165                    Ignored with QtWebEngine.
166         """
167         utils.unused(elem_geometry)
168         utils.unused(no_js)
169         rects = self._js_dict['rects']
170         for rect in rects:
171             # FIXME:qtwebengine
172             # width = rect.get("width", 0)
173             # height = rect.get("height", 0)
174             width = rect['width']
175             height = rect['height']
176             left = rect['left']
177             top = rect['top']
178             if width > 1 and height > 1:
179                 # Fix coordinates according to zoom level
180                 # We're not checking for zoom.text_only here as that doesn't
181                 # exist for QtWebEngine.
182                 zoom = self._tab.zoom.factor()
183                 rect = QRect(int(left * zoom), int(top * zoom),
184                              int(width * zoom), int(height * zoom))
185                 # FIXME:qtwebengine
186                 # frame = self._elem.webFrame()
187                 # while frame is not None:
188                 #     # Translate to parent frames' position (scroll position
189                 #     # is taken care of inside getClientRects)
190                 #     rect.translate(frame.geometry().topLeft())
191                 #     frame = frame.parentFrame()
192                 return rect
193         log.webelem.debug("Couldn't find rectangle for {!r} ({})".format(
194             self, rects))
195         return QRect()
196 
197     def remove_blank_target(self) -> None:
198         if self._js_dict['attributes'].get('target') == '_blank':
199             self._js_dict['attributes']['target'] = '_top'
200         self._js_call('remove_blank_target')
201 
202     def delete(self) -> None:
203         self._js_call('delete')
204 
205     def _move_text_cursor(self) -> None:
206         if self.is_text_input() and self.is_editable():
207             self._js_call('move_cursor_to_end')
208 
209     def _requires_user_interaction(self) -> bool:
210         baseurl = self._tab.url()
211         url = self.resolve_url(baseurl)
212         if url is None:
213             return True
214         if baseurl.scheme() == url.scheme():  # e.g. a qute:// link
215             return False
216 
217         # Qt 6.3+ needs a user interaction to allow navigations from qute:// to
218         # outside qute:// (like e.g. on qute://bookmarks).
219         versions = version.qtwebengine_versions()
220         if (
221             baseurl.scheme() == "qute" and
222             url.scheme() != "qute" and
223             versions.webengine >= utils.VersionNumber(6, 3)
224         ):
225             return True
226 
227         return url.scheme() not in urlutils.WEBENGINE_SCHEMES
228 
229     def _click_editable(self, click_target: usertypes.ClickTarget) -> None:
230         # This actually "clicks" the element by calling focus() on it in JS.
231         self._js_call('focus')
232         self._move_text_cursor()
233 
234     def _click_js(self, _click_target: usertypes.ClickTarget) -> None:
235         # FIXME:qtwebengine Have a proper API for this
236         # pylint: disable=protected-access
237         view = self._tab._widget
238         assert view is not None
239         # pylint: enable=protected-access
240         attribute = QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows
241         could_open_windows = view.settings().testAttribute(attribute)
242         view.settings().setAttribute(attribute, True)
243 
244         # Get QtWebEngine do apply the settings
245         # (it does so with a 0ms QTimer...)
246         # This is also used in Qt's tests:
247         # https://github.com/qt/qtwebengine/commit/5e572e88efa7ba7c2b9138ec19e606d3e345ac90
248         QApplication.processEvents(
249             QEventLoop.ProcessEventsFlag.ExcludeSocketNotifiers |
250             QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
251 
252         def reset_setting(_arg: Any) -> None:
253             """Set the JavascriptCanOpenWindows setting to its old value."""
254             assert view is not None
255             try:
256                 view.settings().setAttribute(attribute, could_open_windows)
257             except RuntimeError:
258                 # Happens if this callback gets called during QWebEnginePage
259                 # destruction, i.e. if the tab was closed in the meantime.
260                 pass
261 
262         self._js_call('click', callback=reset_setting)
263
```
--- 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/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py
--- a/qutebrowser/browser/webengine/webengineelem.py
+++ b/qutebrowser/browser/webengine/webengineelem.py
@@ -215,14 +215,16 @@
             return False
 
         # Qt 6.3+ needs a user interaction to allow navigations from qute:// to
-        # outside qute:// (like e.g. on qute://bookmarks).
+        # outside qute:// (like e.g. on qute://bookmarks), as well as from file:// to
+        # outside of file:// (e.g. users having a local bookmarks.html).
         versions = version.qtwebengine_versions()
-        if (
-            baseurl.scheme() == "qute" and
-            url.scheme() != "qute" and
-            versions.webengine >= utils.VersionNumber(6, 3)
-        ):
-            return True
+        for scheme in ["qute", "file"]:
+            if (
+                baseurl.scheme() == scheme and
+                url.scheme() != scheme and
+                versions.webengine >= utils.VersionNumber(6, 3)
+            ):
+                return True
 
         return url.scheme() not in urlutils.WEBENGINE_SCHEMES

{"golden_diff": "diff --git a/qutebrowser/browser/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py\n--- a/qutebrowser/browser/webengine/webengineelem.py\n+++ b/qutebrowser/browser/webengine/webengineelem.py\n@@ -215,14 +215,16 @@\n             return False\n \n         # Qt 6.3+ needs a user interaction to allow navigations from qute:// to\n-        # outside qute:// (like e.g. on qute://bookmarks).\n+        # outside qute:// (like e.g. on qute://bookmarks), as well as from file:// to\n+        # outside of file:// (e.g. users having a local bookmarks.html).\n         versions = version.qtwebengine_versions()\n-        if (\n-            baseurl.scheme() == \"qute\" and\n-            url.scheme() != \"qute\" and\n-            versions.webengine >= utils.VersionNumber(6, 3)\n-        ):\n-            return True\n+        for scheme in [\"qute\", \"file\"]:\n+            if (\n+                baseurl.scheme() == scheme and\n+                url.scheme() != scheme and\n+                versions.webengine >= utils.VersionNumber(6, 3)\n+            ):\n+                return True\n \n         return url.scheme() not in urlutils.WEBENGINE_SCHEMES\n", "issue": "'Your internet access is blocked' when using keyboard 'f' to follow a link in a local file to a remote one, but works if clicking with mouse\n**Version info**:\r\n<!-- Please copy the first block from :version, not just the qutebrowser version -->\r\nqutebrowser v3.0.0\r\nGit commit: \r\nBackend: QtWebEngine 6.5.2, based on Chromium 108.0.5359.220 (from api)\r\nQt: 6.5.2\r\n\r\n**Does the bug happen if you start with `--temp-basedir`?**:\r\nYes.\r\n\r\n**Description**\r\nI have a bookmarks.html file with commonly-used links.  I load the file into qutebrowser with ':open -t file:///home/chris/ckstemp.html'.  If I use the keyboard shortcut 'f' to follow a link, then press the link letter (say, 'a'), I get a 'Your internet access is blocked' error page.  If instead I click the link with the mouse, the page loads as expected (no error).\r\n\r\nI have tried deleting the .cache, and I've tried clearing my browser history.\r\n\r\nThis is new behavior since I upgraded to qutebrowser 3.0.0 this morning.\r\n\r\n**How to reproduce**\r\n<!-- Link to the affected site, or steps to reproduce the issue (if possible/applicable). -->\r\n\r\nSee above for the steps to reproduces.  Here is the contents of my ckstemp.html file:\r\n\r\n```\r\n<TITLE>Bookmarks</TITLE>\r\n<DL><p>\r\n    <DT><H1>Bookmarks</H1>\r\n        <DT><H3>Comics</H3>\r\n        <DL><p>\r\n            <DT><A HREF=\"https://www.gocomics.com/foxtrot\">Foxtrot</A>\r\n         </DL>\r\n</DL>\r\n```\r\n\r\n\n", "before_files": [{"content": "# SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\n\"\"\"QtWebEngine specific part of the web element API.\"\"\"\n\nfrom typing import (\n    TYPE_CHECKING, Any, Callable, Dict, Iterator, Optional, Set, Tuple, Union)\n\nfrom qutebrowser.qt.core import QRect, QEventLoop\nfrom qutebrowser.qt.widgets import QApplication\nfrom qutebrowser.qt.webenginecore import QWebEngineSettings\n\nfrom qutebrowser.utils import log, javascript, urlutils, usertypes, utils, version\nfrom qutebrowser.browser import webelem\n\nif TYPE_CHECKING:\n    from qutebrowser.browser.webengine import webenginetab\n\n\nclass WebEngineElement(webelem.AbstractWebElement):\n\n    \"\"\"A web element for QtWebEngine, using JS under the hood.\"\"\"\n\n    _tab: \"webenginetab.WebEngineTab\"\n\n    def __init__(self, js_dict: Dict[str, Any],\n                 tab: 'webenginetab.WebEngineTab') -> None:\n        super().__init__(tab)\n        # Do some sanity checks on the data we get from JS\n        js_dict_types: Dict[str, Union[type, Tuple[type, ...]]] = {\n            'id': int,\n            'text': str,\n            'value': (str, int, float),\n            'tag_name': str,\n            'outer_xml': str,\n            'class_name': str,\n            'rects': list,\n            'attributes': dict,\n            'is_content_editable': bool,\n            'caret_position': (int, type(None)),\n        }\n        assert set(js_dict.keys()).issubset(js_dict_types.keys())\n        for name, typ in js_dict_types.items():\n            if name in js_dict and not isinstance(js_dict[name], typ):\n                raise TypeError(\"Got {} for {} from JS but expected {}: \"\n                                \"{}\".format(type(js_dict[name]), name, typ,\n                                            js_dict))\n        for name, value in js_dict['attributes'].items():\n            if not isinstance(name, str):\n                raise TypeError(\"Got {} ({}) for attribute name from JS: \"\n                                \"{}\".format(name, type(name), js_dict))\n            if not isinstance(value, str):\n                raise TypeError(\"Got {} ({}) for attribute {} from JS: \"\n                                \"{}\".format(value, type(value), name, js_dict))\n        for rect in js_dict['rects']:\n            assert set(rect.keys()) == {'top', 'right', 'bottom', 'left',\n                                        'height', 'width'}, rect.keys()\n            for value in rect.values():\n                if not isinstance(value, (int, float)):\n                    raise TypeError(\"Got {} ({}) for rect from JS: \"\n                                    \"{}\".format(value, type(value), js_dict))\n\n        self._id = js_dict['id']\n        self._js_dict = js_dict\n\n    def __str__(self) -> str:\n        return self._js_dict.get('text', '')\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, WebEngineElement):\n            return NotImplemented\n        return self._id == other._id\n\n    def __getitem__(self, key: str) -> str:\n        attrs = self._js_dict['attributes']\n        return attrs[key]\n\n    def __setitem__(self, key: str, val: str) -> None:\n        self._js_dict['attributes'][key] = val\n        self._js_call('set_attribute', key, val)\n\n    def __delitem__(self, key: str) -> None:\n        utils.unused(key)\n        log.stub()\n\n    def __iter__(self) -> Iterator[str]:\n        return iter(self._js_dict['attributes'])\n\n    def __len__(self) -> int:\n        return len(self._js_dict['attributes'])\n\n    def _js_call(self, name: str, *args: webelem.JsValueType,\n                 callback: Callable[[Any], None] = None) -> None:\n        \"\"\"Wrapper to run stuff from webelem.js.\"\"\"\n        if self._tab.is_deleted():\n            raise webelem.OrphanedError(\"Tab containing element vanished\")\n        js_code = javascript.assemble('webelem', name, self._id, *args)\n        self._tab.run_js_async(js_code, callback=callback)\n\n    def has_frame(self) -> bool:\n        return True\n\n    def geometry(self) -> QRect:\n        log.stub()\n        return QRect()\n\n    def classes(self) -> Set[str]:\n        \"\"\"Get a list of classes assigned to this element.\"\"\"\n        return set(self._js_dict['class_name'].split())\n\n    def tag_name(self) -> str:\n        \"\"\"Get the tag name of this element.\n\n        The returned name will always be lower-case.\n        \"\"\"\n        tag = self._js_dict['tag_name']\n        assert isinstance(tag, str), tag\n        return tag.lower()\n\n    def outer_xml(self) -> str:\n        \"\"\"Get the full HTML representation of this element.\"\"\"\n        return self._js_dict['outer_xml']\n\n    def is_content_editable_prop(self) -> bool:\n        return self._js_dict['is_content_editable']\n\n    def value(self) -> webelem.JsValueType:\n        return self._js_dict.get('value', None)\n\n    def set_value(self, value: webelem.JsValueType) -> None:\n        self._js_call('set_value', value)\n\n    def dispatch_event(self, event: str,\n                       bubbles: bool = False,\n                       cancelable: bool = False,\n                       composed: bool = False) -> None:\n        self._js_call('dispatch_event', event, bubbles, cancelable, composed)\n\n    def caret_position(self) -> Optional[int]:\n        \"\"\"Get the text caret position for the current element.\n\n        If the element is not a text element, None is returned.\n        \"\"\"\n        return self._js_dict.get('caret_position', None)\n\n    def insert_text(self, text: str) -> None:\n        if not self.is_editable(strict=True):\n            raise webelem.Error(\"Element is not editable!\")\n        log.webelem.debug(\"Inserting text into element {!r}\".format(self))\n        self._js_call('insert_text', text)\n\n    def rect_on_view(self, *, elem_geometry: QRect = None,\n                     no_js: bool = False) -> QRect:\n        \"\"\"Get the geometry of the element relative to the webview.\n\n        Skipping of small rectangles is due to <a> elements containing other\n        elements with \"display:block\" style, see\n        https://github.com/qutebrowser/qutebrowser/issues/1298\n\n        Args:\n            elem_geometry: The geometry of the element, or None.\n                           Ignored with QtWebEngine.\n            no_js: Fall back to the Python implementation.\n                   Ignored with QtWebEngine.\n        \"\"\"\n        utils.unused(elem_geometry)\n        utils.unused(no_js)\n        rects = self._js_dict['rects']\n        for rect in rects:\n            # FIXME:qtwebengine\n            # width = rect.get(\"width\", 0)\n            # height = rect.get(\"height\", 0)\n            width = rect['width']\n            height = rect['height']\n            left = rect['left']\n            top = rect['top']\n            if width > 1 and height > 1:\n                # Fix coordinates according to zoom level\n                # We're not checking for zoom.text_only here as that doesn't\n                # exist for QtWebEngine.\n                zoom = self._tab.zoom.factor()\n                rect = QRect(int(left * zoom), int(top * zoom),\n                             int(width * zoom), int(height * zoom))\n                # FIXME:qtwebengine\n                # frame = self._elem.webFrame()\n                # while frame is not None:\n                #     # Translate to parent frames' position (scroll position\n                #     # is taken care of inside getClientRects)\n                #     rect.translate(frame.geometry().topLeft())\n                #     frame = frame.parentFrame()\n                return rect\n        log.webelem.debug(\"Couldn't find rectangle for {!r} ({})\".format(\n            self, rects))\n        return QRect()\n\n    def remove_blank_target(self) -> None:\n        if self._js_dict['attributes'].get('target') == '_blank':\n            self._js_dict['attributes']['target'] = '_top'\n        self._js_call('remove_blank_target')\n\n    def delete(self) -> None:\n        self._js_call('delete')\n\n    def _move_text_cursor(self) -> None:\n        if self.is_text_input() and self.is_editable():\n            self._js_call('move_cursor_to_end')\n\n    def _requires_user_interaction(self) -> bool:\n        baseurl = self._tab.url()\n        url = self.resolve_url(baseurl)\n        if url is None:\n            return True\n        if baseurl.scheme() == url.scheme():  # e.g. a qute:// link\n            return False\n\n        # Qt 6.3+ needs a user interaction to allow navigations from qute:// to\n        # outside qute:// (like e.g. on qute://bookmarks).\n        versions = version.qtwebengine_versions()\n        if (\n            baseurl.scheme() == \"qute\" and\n            url.scheme() != \"qute\" and\n            versions.webengine >= utils.VersionNumber(6, 3)\n        ):\n            return True\n\n        return url.scheme() not in urlutils.WEBENGINE_SCHEMES\n\n    def _click_editable(self, click_target: usertypes.ClickTarget) -> None:\n        # This actually \"clicks\" the element by calling focus() on it in JS.\n        self._js_call('focus')\n        self._move_text_cursor()\n\n    def _click_js(self, _click_target: usertypes.ClickTarget) -> None:\n        # FIXME:qtwebengine Have a proper API for this\n        # pylint: disable=protected-access\n        view = self._tab._widget\n        assert view is not None\n        # pylint: enable=protected-access\n        attribute = QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows\n        could_open_windows = view.settings().testAttribute(attribute)\n        view.settings().setAttribute(attribute, True)\n\n        # Get QtWebEngine do apply the settings\n        # (it does so with a 0ms QTimer...)\n        # This is also used in Qt's tests:\n        # https://github.com/qt/qtwebengine/commit/5e572e88efa7ba7c2b9138ec19e606d3e345ac90\n        QApplication.processEvents(\n            QEventLoop.ProcessEventsFlag.ExcludeSocketNotifiers |\n            QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)\n\n        def reset_setting(_arg: Any) -> None:\n            \"\"\"Set the JavascriptCanOpenWindows setting to its old value.\"\"\"\n            assert view is not None\n            try:\n                view.settings().setAttribute(attribute, could_open_windows)\n            except RuntimeError:\n                # Happens if this callback gets called during QWebEnginePage\n                # destruction, i.e. if the tab was closed in the meantime.\n                pass\n\n        self._js_call('click', callback=reset_setting)\n", "path": "qutebrowser/browser/webengine/webengineelem.py"}], "after_files": [{"content": "# SPDX-FileCopyrightText: Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\n\"\"\"QtWebEngine specific part of the web element API.\"\"\"\n\nfrom typing import (\n    TYPE_CHECKING, Any, Callable, Dict, Iterator, Optional, Set, Tuple, Union)\n\nfrom qutebrowser.qt.core import QRect, QEventLoop\nfrom qutebrowser.qt.widgets import QApplication\nfrom qutebrowser.qt.webenginecore import QWebEngineSettings\n\nfrom qutebrowser.utils import log, javascript, urlutils, usertypes, utils, version\nfrom qutebrowser.browser import webelem\n\nif TYPE_CHECKING:\n    from qutebrowser.browser.webengine import webenginetab\n\n\nclass WebEngineElement(webelem.AbstractWebElement):\n\n    \"\"\"A web element for QtWebEngine, using JS under the hood.\"\"\"\n\n    _tab: \"webenginetab.WebEngineTab\"\n\n    def __init__(self, js_dict: Dict[str, Any],\n                 tab: 'webenginetab.WebEngineTab') -> None:\n        super().__init__(tab)\n        # Do some sanity checks on the data we get from JS\n        js_dict_types: Dict[str, Union[type, Tuple[type, ...]]] = {\n            'id': int,\n            'text': str,\n            'value': (str, int, float),\n            'tag_name': str,\n            'outer_xml': str,\n            'class_name': str,\n            'rects': list,\n            'attributes': dict,\n            'is_content_editable': bool,\n            'caret_position': (int, type(None)),\n        }\n        assert set(js_dict.keys()).issubset(js_dict_types.keys())\n        for name, typ in js_dict_types.items():\n            if name in js_dict and not isinstance(js_dict[name], typ):\n                raise TypeError(\"Got {} for {} from JS but expected {}: \"\n                                \"{}\".format(type(js_dict[name]), name, typ,\n                                            js_dict))\n        for name, value in js_dict['attributes'].items():\n            if not isinstance(name, str):\n                raise TypeError(\"Got {} ({}) for attribute name from JS: \"\n                                \"{}\".format(name, type(name), js_dict))\n            if not isinstance(value, str):\n                raise TypeError(\"Got {} ({}) for attribute {} from JS: \"\n                                \"{}\".format(value, type(value), name, js_dict))\n        for rect in js_dict['rects']:\n            assert set(rect.keys()) == {'top', 'right', 'bottom', 'left',\n                                        'height', 'width'}, rect.keys()\n            for value in rect.values():\n                if not isinstance(value, (int, float)):\n                    raise TypeError(\"Got {} ({}) for rect from JS: \"\n                                    \"{}\".format(value, type(value), js_dict))\n\n        self._id = js_dict['id']\n        self._js_dict = js_dict\n\n    def __str__(self) -> str:\n        return self._js_dict.get('text', '')\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, WebEngineElement):\n            return NotImplemented\n        return self._id == other._id\n\n    def __getitem__(self, key: str) -> str:\n        attrs = self._js_dict['attributes']\n        return attrs[key]\n\n    def __setitem__(self, key: str, val: str) -> None:\n        self._js_dict['attributes'][key] = val\n        self._js_call('set_attribute', key, val)\n\n    def __delitem__(self, key: str) -> None:\n        utils.unused(key)\n        log.stub()\n\n    def __iter__(self) -> Iterator[str]:\n        return iter(self._js_dict['attributes'])\n\n    def __len__(self) -> int:\n        return len(self._js_dict['attributes'])\n\n    def _js_call(self, name: str, *args: webelem.JsValueType,\n                 callback: Callable[[Any], None] = None) -> None:\n        \"\"\"Wrapper to run stuff from webelem.js.\"\"\"\n        if self._tab.is_deleted():\n            raise webelem.OrphanedError(\"Tab containing element vanished\")\n        js_code = javascript.assemble('webelem', name, self._id, *args)\n        self._tab.run_js_async(js_code, callback=callback)\n\n    def has_frame(self) -> bool:\n        return True\n\n    def geometry(self) -> QRect:\n        log.stub()\n        return QRect()\n\n    def classes(self) -> Set[str]:\n        \"\"\"Get a list of classes assigned to this element.\"\"\"\n        return set(self._js_dict['class_name'].split())\n\n    def tag_name(self) -> str:\n        \"\"\"Get the tag name of this element.\n\n        The returned name will always be lower-case.\n        \"\"\"\n        tag = self._js_dict['tag_name']\n        assert isinstance(tag, str), tag\n        return tag.lower()\n\n    def outer_xml(self) -> str:\n        \"\"\"Get the full HTML representation of this element.\"\"\"\n        return self._js_dict['outer_xml']\n\n    def is_content_editable_prop(self) -> bool:\n        return self._js_dict['is_content_editable']\n\n    def value(self) -> webelem.JsValueType:\n        return self._js_dict.get('value', None)\n\n    def set_value(self, value: webelem.JsValueType) -> None:\n        self._js_call('set_value', value)\n\n    def dispatch_event(self, event: str,\n                       bubbles: bool = False,\n                       cancelable: bool = False,\n                       composed: bool = False) -> None:\n        self._js_call('dispatch_event', event, bubbles, cancelable, composed)\n\n    def caret_position(self) -> Optional[int]:\n        \"\"\"Get the text caret position for the current element.\n\n        If the element is not a text element, None is returned.\n        \"\"\"\n        return self._js_dict.get('caret_position', None)\n\n    def insert_text(self, text: str) -> None:\n        if not self.is_editable(strict=True):\n            raise webelem.Error(\"Element is not editable!\")\n        log.webelem.debug(\"Inserting text into element {!r}\".format(self))\n        self._js_call('insert_text', text)\n\n    def rect_on_view(self, *, elem_geometry: QRect = None,\n                     no_js: bool = False) -> QRect:\n        \"\"\"Get the geometry of the element relative to the webview.\n\n        Skipping of small rectangles is due to <a> elements containing other\n        elements with \"display:block\" style, see\n        https://github.com/qutebrowser/qutebrowser/issues/1298\n\n        Args:\n            elem_geometry: The geometry of the element, or None.\n                           Ignored with QtWebEngine.\n            no_js: Fall back to the Python implementation.\n                   Ignored with QtWebEngine.\n        \"\"\"\n        utils.unused(elem_geometry)\n        utils.unused(no_js)\n        rects = self._js_dict['rects']\n        for rect in rects:\n            # FIXME:qtwebengine\n            # width = rect.get(\"width\", 0)\n            # height = rect.get(\"height\", 0)\n            width = rect['width']\n            height = rect['height']\n            left = rect['left']\n            top = rect['top']\n            if width > 1 and height > 1:\n                # Fix coordinates according to zoom level\n                # We're not checking for zoom.text_only here as that doesn't\n                # exist for QtWebEngine.\n                zoom = self._tab.zoom.factor()\n                rect = QRect(int(left * zoom), int(top * zoom),\n                             int(width * zoom), int(height * zoom))\n                # FIXME:qtwebengine\n                # frame = self._elem.webFrame()\n                # while frame is not None:\n                #     # Translate to parent frames' position (scroll position\n                #     # is taken care of inside getClientRects)\n                #     rect.translate(frame.geometry().topLeft())\n                #     frame = frame.parentFrame()\n                return rect\n        log.webelem.debug(\"Couldn't find rectangle for {!r} ({})\".format(\n            self, rects))\n        return QRect()\n\n    def remove_blank_target(self) -> None:\n        if self._js_dict['attributes'].get('target') == '_blank':\n            self._js_dict['attributes']['target'] = '_top'\n        self._js_call('remove_blank_target')\n\n    def delete(self) -> None:\n        self._js_call('delete')\n\n    def _move_text_cursor(self) -> None:\n        if self.is_text_input() and self.is_editable():\n            self._js_call('move_cursor_to_end')\n\n    def _requires_user_interaction(self) -> bool:\n        baseurl = self._tab.url()\n        url = self.resolve_url(baseurl)\n        if url is None:\n            return True\n        if baseurl.scheme() == url.scheme():  # e.g. a qute:// link\n            return False\n\n        # Qt 6.3+ needs a user interaction to allow navigations from qute:// to\n        # outside qute:// (like e.g. on qute://bookmarks), as well as from file:// to\n        # outside of file:// (e.g. users having a local bookmarks.html).\n        versions = version.qtwebengine_versions()\n        for scheme in [\"qute\", \"file\"]:\n            if (\n                baseurl.scheme() == scheme and\n                url.scheme() != scheme and\n                versions.webengine >= utils.VersionNumber(6, 3)\n            ):\n                return True\n\n        return url.scheme() not in urlutils.WEBENGINE_SCHEMES\n\n    def _click_editable(self, click_target: usertypes.ClickTarget) -> None:\n        # This actually \"clicks\" the element by calling focus() on it in JS.\n        self._js_call('focus')\n        self._move_text_cursor()\n\n    def _click_js(self, _click_target: usertypes.ClickTarget) -> None:\n        # FIXME:qtwebengine Have a proper API for this\n        # pylint: disable=protected-access\n        view = self._tab._widget\n        assert view is not None\n        # pylint: enable=protected-access\n        attribute = QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows\n        could_open_windows = view.settings().testAttribute(attribute)\n        view.settings().setAttribute(attribute, True)\n\n        # Get QtWebEngine do apply the settings\n        # (it does so with a 0ms QTimer...)\n        # This is also used in Qt's tests:\n        # https://github.com/qt/qtwebengine/commit/5e572e88efa7ba7c2b9138ec19e606d3e345ac90\n        QApplication.processEvents(\n            QEventLoop.ProcessEventsFlag.ExcludeSocketNotifiers |\n            QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)\n\n        def reset_setting(_arg: Any) -> None:\n            \"\"\"Set the JavascriptCanOpenWindows setting to its old value.\"\"\"\n            assert view is not None\n            try:\n                view.settings().setAttribute(attribute, could_open_windows)\n            except RuntimeError:\n                # Happens if this callback gets called during QWebEnginePage\n                # destruction, i.e. if the tab was closed in the meantime.\n                pass\n\n        self._js_call('click', callback=reset_setting)\n", "path": "qutebrowser/browser/webengine/webengineelem.py"}]}