We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error page does not fully work without JS
When a page cannot load due to an error (e.g., renderer process killed), an error page with the text "Unable to load page" is displayed with the URL of the page that I tried to load and a button to "Try again"; however, clicking on this button has no effect. Further inspection reveals that this button relies on JavaScript (I have `content.javascript.enabled` set to false by default).

As a small quality of life improvement, would it be feasible to change the button to a simple link that would work without JS? I would be happy to open a pull request with this change (it seems that this would only entail changing a few lines in [`error.html`](https://github.com/qutebrowser/qutebrowser/blob/master/qutebrowser/html/error.html)); I just wanted to check first if that would be a good idea.

--- 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/utils/jinja.py`
Content:
```
1 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2 
3 # Copyright 2014-2021 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 <https://www.gnu.org/licenses/>.
19 
20 """Utilities related to jinja2."""
21 
22 import os
23 import os.path
24 import posixpath
25 import functools
26 import contextlib
27 import html
28 from typing import Any, Callable, FrozenSet, Iterator, List, Set, Tuple
29 
30 import jinja2
31 import jinja2.nodes
32 from PyQt5.QtCore import QUrl
33 
34 from qutebrowser.utils import utils, urlutils, log, qtutils, javascript
35 from qutebrowser.misc import debugcachestats
36 
37 
38 html_fallback = """
39 <!DOCTYPE html>
40 <html>
41   <head>
42     <meta charset="utf-8">
43     <title>Error while loading template</title>
44   </head>
45   <body>
46     <p><span style="font-size:120%;color:red">
47     The %FILE% template could not be found!<br>
48     Please check your qutebrowser installation
49       </span><br>
50       %ERROR%
51     </p>
52   </body>
53 </html>
54 """
55 
56 
57 class Loader(jinja2.BaseLoader):
58 
59     """Jinja loader which uses utils.read_file to load templates.
60 
61     Attributes:
62         _subdir: The subdirectory to find templates in.
63     """
64 
65     def __init__(self, subdir: str) -> None:
66         self._subdir = subdir
67 
68     def get_source(
69             self,
70             _env: jinja2.Environment,
71             template: str
72     ) -> Tuple[str, str, Callable[[], bool]]:
73         path = os.path.join(self._subdir, template)
74         try:
75             source = utils.read_file(path)
76         except OSError as e:
77             source = html_fallback.replace("%ERROR%", html.escape(str(e)))
78             source = source.replace("%FILE%", html.escape(template))
79             log.misc.exception("The {} template could not be loaded from {}"
80                                .format(template, path))
81         # Currently we don't implement auto-reloading, so we always return True
82         # for up-to-date.
83         return source, path, lambda: True
84 
85 
86 class Environment(jinja2.Environment):
87 
88     """Our own jinja environment which is more strict."""
89 
90     def __init__(self) -> None:
91         super().__init__(loader=Loader('html'),
92                          autoescape=lambda _name: self._autoescape,
93                          undefined=jinja2.StrictUndefined)
94         self.globals['resource_url'] = self._resource_url
95         self.globals['file_url'] = urlutils.file_url
96         self.globals['data_url'] = self._data_url
97         self.globals['qcolor_to_qsscolor'] = qtutils.qcolor_to_qsscolor
98         self.filters['js_string_escape'] = javascript.string_escape
99         self._autoescape = True
100 
101     @contextlib.contextmanager
102     def no_autoescape(self) -> Iterator[None]:
103         """Context manager to temporarily turn off autoescaping."""
104         self._autoescape = False
105         yield
106         self._autoescape = True
107 
108     def _resource_url(self, path: str) -> str:
109         """Load qutebrowser resource files.
110 
111         Arguments:
112             path: The relative path to the resource.
113         """
114         assert not posixpath.isabs(path), path
115         url = QUrl('qute://resource')
116         url.setPath('/' + path)
117         urlutils.ensure_valid(url)
118         urlstr = url.toString(QUrl.FullyEncoded)  # type: ignore[arg-type]
119         return urlstr
120 
121     def _data_url(self, path: str) -> str:
122         """Get a data: url for the broken qutebrowser logo."""
123         data = utils.read_file_binary(path)
124         mimetype = utils.guess_mimetype(path)
125         return urlutils.data_url(mimetype, data).toString()
126 
127     def getattr(self, obj: Any, attribute: str) -> Any:
128         """Override jinja's getattr() to be less clever.
129 
130         This means it doesn't fall back to __getitem__, and it doesn't hide
131         AttributeError.
132         """
133         return getattr(obj, attribute)
134 
135 
136 def render(template: str, **kwargs: Any) -> str:
137     """Render the given template and pass the given arguments to it."""
138     return environment.get_template(template).render(**kwargs)
139 
140 
141 environment = Environment()
142 js_environment = jinja2.Environment(loader=Loader('javascript'))
143 
144 
145 @debugcachestats.register()
146 @functools.lru_cache()
147 def template_config_variables(template: str) -> FrozenSet[str]:
148     """Return the config variables used in the template."""
149     unvisted_nodes = [environment.parse(template)]
150     result: Set[str] = set()
151     while unvisted_nodes:
152         node = unvisted_nodes.pop()
153         if not isinstance(node, jinja2.nodes.Getattr):
154             unvisted_nodes.extend(node.iter_child_nodes())
155             continue
156 
157         # List of attribute names in reverse order.
158         # For example it's ['ab', 'c', 'd'] for 'conf.d.c.ab'.
159         attrlist: List[str] = []
160         while isinstance(node, jinja2.nodes.Getattr):
161             attrlist.append(node.attr)  # type: ignore[attr-defined]
162             node = node.node  # type: ignore[attr-defined]
163 
164         if isinstance(node, jinja2.nodes.Name):
165             if node.name == 'conf':  # type: ignore[attr-defined]
166                 result.add('.'.join(reversed(attrlist)))
167             # otherwise, the node is a Name node so it doesn't have any
168             # child nodes
169         else:
170             unvisted_nodes.append(node)
171 
172     from qutebrowser.config import config
173     for option in result:
174         config.instance.ensure_has_opt(option)
175 
176     return frozenset(result)
177
```
--- 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/utils/jinja.py b/qutebrowser/utils/jinja.py
--- a/qutebrowser/utils/jinja.py
+++ b/qutebrowser/utils/jinja.py
@@ -31,7 +31,7 @@
 import jinja2.nodes
 from PyQt5.QtCore import QUrl
 
-from qutebrowser.utils import utils, urlutils, log, qtutils, javascript
+from qutebrowser.utils import utils, urlutils, log, qtutils
 from qutebrowser.misc import debugcachestats
 
 
@@ -95,7 +95,6 @@
         self.globals['file_url'] = urlutils.file_url
         self.globals['data_url'] = self._data_url
         self.globals['qcolor_to_qsscolor'] = qtutils.qcolor_to_qsscolor
-        self.filters['js_string_escape'] = javascript.string_escape
         self._autoescape = True
 
     @contextlib.contextmanager

{"golden_diff": "diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py\n--- a/qutebrowser/utils/jinja.py\n+++ b/qutebrowser/utils/jinja.py\n@@ -31,7 +31,7 @@\n import jinja2.nodes\n from PyQt5.QtCore import QUrl\n \n-from qutebrowser.utils import utils, urlutils, log, qtutils, javascript\n+from qutebrowser.utils import utils, urlutils, log, qtutils\n from qutebrowser.misc import debugcachestats\n \n \n@@ -95,7 +95,6 @@\n         self.globals['file_url'] = urlutils.file_url\n         self.globals['data_url'] = self._data_url\n         self.globals['qcolor_to_qsscolor'] = qtutils.qcolor_to_qsscolor\n-        self.filters['js_string_escape'] = javascript.string_escape\n         self._autoescape = True\n \n     @contextlib.contextmanager\n", "issue": "Error page does not fully work without JS\nWhen a page cannot load due to an error (e.g., renderer process killed), an error page with the text \"Unable to load page\" is displayed with the URL of the page that I tried to load and a button to \"Try again\"; however, clicking on this button has no effect. Further inspection reveals that this button relies on JavaScript (I have `content.javascript.enabled` set to false by default).\r\n\r\nAs a small quality of life improvement, would it be feasible to change the button to a simple link that would work without JS? I would be happy to open a pull request with this change (it seems that this would only entail changing a few lines in [`error.html`](https://github.com/qutebrowser/qutebrowser/blob/master/qutebrowser/html/error.html)); I just wanted to check first if that would be a good idea.\n", "before_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2021 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 <https://www.gnu.org/licenses/>.\n\n\"\"\"Utilities related to jinja2.\"\"\"\n\nimport os\nimport os.path\nimport posixpath\nimport functools\nimport contextlib\nimport html\nfrom typing import Any, Callable, FrozenSet, Iterator, List, Set, Tuple\n\nimport jinja2\nimport jinja2.nodes\nfrom PyQt5.QtCore import QUrl\n\nfrom qutebrowser.utils import utils, urlutils, log, qtutils, javascript\nfrom qutebrowser.misc import debugcachestats\n\n\nhtml_fallback = \"\"\"\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Error while loading template</title>\n  </head>\n  <body>\n    <p><span style=\"font-size:120%;color:red\">\n    The %FILE% template could not be found!<br>\n    Please check your qutebrowser installation\n      </span><br>\n      %ERROR%\n    </p>\n  </body>\n</html>\n\"\"\"\n\n\nclass Loader(jinja2.BaseLoader):\n\n    \"\"\"Jinja loader which uses utils.read_file to load templates.\n\n    Attributes:\n        _subdir: The subdirectory to find templates in.\n    \"\"\"\n\n    def __init__(self, subdir: str) -> None:\n        self._subdir = subdir\n\n    def get_source(\n            self,\n            _env: jinja2.Environment,\n            template: str\n    ) -> Tuple[str, str, Callable[[], bool]]:\n        path = os.path.join(self._subdir, template)\n        try:\n            source = utils.read_file(path)\n        except OSError as e:\n            source = html_fallback.replace(\"%ERROR%\", html.escape(str(e)))\n            source = source.replace(\"%FILE%\", html.escape(template))\n            log.misc.exception(\"The {} template could not be loaded from {}\"\n                               .format(template, path))\n        # Currently we don't implement auto-reloading, so we always return True\n        # for up-to-date.\n        return source, path, lambda: True\n\n\nclass Environment(jinja2.Environment):\n\n    \"\"\"Our own jinja environment which is more strict.\"\"\"\n\n    def __init__(self) -> None:\n        super().__init__(loader=Loader('html'),\n                         autoescape=lambda _name: self._autoescape,\n                         undefined=jinja2.StrictUndefined)\n        self.globals['resource_url'] = self._resource_url\n        self.globals['file_url'] = urlutils.file_url\n        self.globals['data_url'] = self._data_url\n        self.globals['qcolor_to_qsscolor'] = qtutils.qcolor_to_qsscolor\n        self.filters['js_string_escape'] = javascript.string_escape\n        self._autoescape = True\n\n    @contextlib.contextmanager\n    def no_autoescape(self) -> Iterator[None]:\n        \"\"\"Context manager to temporarily turn off autoescaping.\"\"\"\n        self._autoescape = False\n        yield\n        self._autoescape = True\n\n    def _resource_url(self, path: str) -> str:\n        \"\"\"Load qutebrowser resource files.\n\n        Arguments:\n            path: The relative path to the resource.\n        \"\"\"\n        assert not posixpath.isabs(path), path\n        url = QUrl('qute://resource')\n        url.setPath('/' + path)\n        urlutils.ensure_valid(url)\n        urlstr = url.toString(QUrl.FullyEncoded)  # type: ignore[arg-type]\n        return urlstr\n\n    def _data_url(self, path: str) -> str:\n        \"\"\"Get a data: url for the broken qutebrowser logo.\"\"\"\n        data = utils.read_file_binary(path)\n        mimetype = utils.guess_mimetype(path)\n        return urlutils.data_url(mimetype, data).toString()\n\n    def getattr(self, obj: Any, attribute: str) -> Any:\n        \"\"\"Override jinja's getattr() to be less clever.\n\n        This means it doesn't fall back to __getitem__, and it doesn't hide\n        AttributeError.\n        \"\"\"\n        return getattr(obj, attribute)\n\n\ndef render(template: str, **kwargs: Any) -> str:\n    \"\"\"Render the given template and pass the given arguments to it.\"\"\"\n    return environment.get_template(template).render(**kwargs)\n\n\nenvironment = Environment()\njs_environment = jinja2.Environment(loader=Loader('javascript'))\n\n\n@debugcachestats.register()\n@functools.lru_cache()\ndef template_config_variables(template: str) -> FrozenSet[str]:\n    \"\"\"Return the config variables used in the template.\"\"\"\n    unvisted_nodes = [environment.parse(template)]\n    result: Set[str] = set()\n    while unvisted_nodes:\n        node = unvisted_nodes.pop()\n        if not isinstance(node, jinja2.nodes.Getattr):\n            unvisted_nodes.extend(node.iter_child_nodes())\n            continue\n\n        # List of attribute names in reverse order.\n        # For example it's ['ab', 'c', 'd'] for 'conf.d.c.ab'.\n        attrlist: List[str] = []\n        while isinstance(node, jinja2.nodes.Getattr):\n            attrlist.append(node.attr)  # type: ignore[attr-defined]\n            node = node.node  # type: ignore[attr-defined]\n\n        if isinstance(node, jinja2.nodes.Name):\n            if node.name == 'conf':  # type: ignore[attr-defined]\n                result.add('.'.join(reversed(attrlist)))\n            # otherwise, the node is a Name node so it doesn't have any\n            # child nodes\n        else:\n            unvisted_nodes.append(node)\n\n    from qutebrowser.config import config\n    for option in result:\n        config.instance.ensure_has_opt(option)\n\n    return frozenset(result)\n", "path": "qutebrowser/utils/jinja.py"}], "after_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2021 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 <https://www.gnu.org/licenses/>.\n\n\"\"\"Utilities related to jinja2.\"\"\"\n\nimport os\nimport os.path\nimport posixpath\nimport functools\nimport contextlib\nimport html\nfrom typing import Any, Callable, FrozenSet, Iterator, List, Set, Tuple\n\nimport jinja2\nimport jinja2.nodes\nfrom PyQt5.QtCore import QUrl\n\nfrom qutebrowser.utils import utils, urlutils, log, qtutils\nfrom qutebrowser.misc import debugcachestats\n\n\nhtml_fallback = \"\"\"\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Error while loading template</title>\n  </head>\n  <body>\n    <p><span style=\"font-size:120%;color:red\">\n    The %FILE% template could not be found!<br>\n    Please check your qutebrowser installation\n      </span><br>\n      %ERROR%\n    </p>\n  </body>\n</html>\n\"\"\"\n\n\nclass Loader(jinja2.BaseLoader):\n\n    \"\"\"Jinja loader which uses utils.read_file to load templates.\n\n    Attributes:\n        _subdir: The subdirectory to find templates in.\n    \"\"\"\n\n    def __init__(self, subdir: str) -> None:\n        self._subdir = subdir\n\n    def get_source(\n            self,\n            _env: jinja2.Environment,\n            template: str\n    ) -> Tuple[str, str, Callable[[], bool]]:\n        path = os.path.join(self._subdir, template)\n        try:\n            source = utils.read_file(path)\n        except OSError as e:\n            source = html_fallback.replace(\"%ERROR%\", html.escape(str(e)))\n            source = source.replace(\"%FILE%\", html.escape(template))\n            log.misc.exception(\"The {} template could not be loaded from {}\"\n                               .format(template, path))\n        # Currently we don't implement auto-reloading, so we always return True\n        # for up-to-date.\n        return source, path, lambda: True\n\n\nclass Environment(jinja2.Environment):\n\n    \"\"\"Our own jinja environment which is more strict.\"\"\"\n\n    def __init__(self) -> None:\n        super().__init__(loader=Loader('html'),\n                         autoescape=lambda _name: self._autoescape,\n                         undefined=jinja2.StrictUndefined)\n        self.globals['resource_url'] = self._resource_url\n        self.globals['file_url'] = urlutils.file_url\n        self.globals['data_url'] = self._data_url\n        self.globals['qcolor_to_qsscolor'] = qtutils.qcolor_to_qsscolor\n        self._autoescape = True\n\n    @contextlib.contextmanager\n    def no_autoescape(self) -> Iterator[None]:\n        \"\"\"Context manager to temporarily turn off autoescaping.\"\"\"\n        self._autoescape = False\n        yield\n        self._autoescape = True\n\n    def _resource_url(self, path: str) -> str:\n        \"\"\"Load qutebrowser resource files.\n\n        Arguments:\n            path: The relative path to the resource.\n        \"\"\"\n        assert not posixpath.isabs(path), path\n        url = QUrl('qute://resource')\n        url.setPath('/' + path)\n        urlutils.ensure_valid(url)\n        urlstr = url.toString(QUrl.FullyEncoded)  # type: ignore[arg-type]\n        return urlstr\n\n    def _data_url(self, path: str) -> str:\n        \"\"\"Get a data: url for the broken qutebrowser logo.\"\"\"\n        data = utils.read_file_binary(path)\n        mimetype = utils.guess_mimetype(path)\n        return urlutils.data_url(mimetype, data).toString()\n\n    def getattr(self, obj: Any, attribute: str) -> Any:\n        \"\"\"Override jinja's getattr() to be less clever.\n\n        This means it doesn't fall back to __getitem__, and it doesn't hide\n        AttributeError.\n        \"\"\"\n        return getattr(obj, attribute)\n\n\ndef render(template: str, **kwargs: Any) -> str:\n    \"\"\"Render the given template and pass the given arguments to it.\"\"\"\n    return environment.get_template(template).render(**kwargs)\n\n\nenvironment = Environment()\njs_environment = jinja2.Environment(loader=Loader('javascript'))\n\n\n@debugcachestats.register()\n@functools.lru_cache()\ndef template_config_variables(template: str) -> FrozenSet[str]:\n    \"\"\"Return the config variables used in the template.\"\"\"\n    unvisted_nodes = [environment.parse(template)]\n    result: Set[str] = set()\n    while unvisted_nodes:\n        node = unvisted_nodes.pop()\n        if not isinstance(node, jinja2.nodes.Getattr):\n            unvisted_nodes.extend(node.iter_child_nodes())\n            continue\n\n        # List of attribute names in reverse order.\n        # For example it's ['ab', 'c', 'd'] for 'conf.d.c.ab'.\n        attrlist: List[str] = []\n        while isinstance(node, jinja2.nodes.Getattr):\n            attrlist.append(node.attr)  # type: ignore[attr-defined]\n            node = node.node  # type: ignore[attr-defined]\n\n        if isinstance(node, jinja2.nodes.Name):\n            if node.name == 'conf':  # type: ignore[attr-defined]\n                result.add('.'.join(reversed(attrlist)))\n            # otherwise, the node is a Name node so it doesn't have any\n            # child nodes\n        else:\n            unvisted_nodes.append(node)\n\n    from qutebrowser.config import config\n    for option in result:\n        config.instance.ensure_has_opt(option)\n\n    return frozenset(result)\n", "path": "qutebrowser/utils/jinja.py"}]}