change setHtml() to serve content via media server
- fixes https://forums.ankiweb.net/t/deck-list-is-blank/2241/2 - fixes the security warnings on Qt 6, by ensuring our pages and resources are coming from the same origin
This commit is contained in:
parent
4b5004c472
commit
d33c66e195
@ -78,6 +78,8 @@ class MediaServer(threading.Thread):
|
|||||||
def __init__(self, mw: aqt.main.AnkiQt) -> None:
|
def __init__(self, mw: aqt.main.AnkiQt) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.is_shutdown = False
|
self.is_shutdown = False
|
||||||
|
# map of webview ids to pages
|
||||||
|
self._page_html: dict[int, str] = {}
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
try:
|
try:
|
||||||
@ -119,6 +121,18 @@ class MediaServer(threading.Thread):
|
|||||||
self._ready.wait()
|
self._ready.wait()
|
||||||
return int(self.server.effective_port) # type: ignore
|
return int(self.server.effective_port) # type: ignore
|
||||||
|
|
||||||
|
def set_page_html(self, id: int, html: str) -> None:
|
||||||
|
self._page_html[id] = html
|
||||||
|
|
||||||
|
def get_page_html(self, id: int) -> str | None:
|
||||||
|
return self._page_html.get(id)
|
||||||
|
|
||||||
|
def clear_page_html(self, id: int) -> None:
|
||||||
|
try:
|
||||||
|
del self._page_html[id]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _handle_local_file_request(request: LocalFileRequest) -> Response:
|
def _handle_local_file_request(request: LocalFileRequest) -> Response:
|
||||||
directory = request.root
|
directory = request.root
|
||||||
@ -220,6 +234,8 @@ def _extract_internal_request(
|
|||||||
if dirname == "_anki":
|
if dirname == "_anki":
|
||||||
if flask.request.method == "POST":
|
if flask.request.method == "POST":
|
||||||
return _extract_collection_post_request(filename)
|
return _extract_collection_post_request(filename)
|
||||||
|
elif get_handler := _extract_dynamic_get_request(filename):
|
||||||
|
return get_handler
|
||||||
# remap legacy top-level references
|
# remap legacy top-level references
|
||||||
base, ext = os.path.splitext(filename)
|
base, ext = os.path.splitext(filename)
|
||||||
if ext == ".css":
|
if ext == ".css":
|
||||||
@ -395,6 +411,7 @@ def complete_tag() -> bytes:
|
|||||||
return aqt.mw.col.tags.complete_tag(request.data)
|
return aqt.mw.col.tags.complete_tag(request.data)
|
||||||
|
|
||||||
|
|
||||||
|
# these require a collection
|
||||||
post_handlers = {
|
post_handlers = {
|
||||||
"graphData": graph_data,
|
"graphData": graph_data,
|
||||||
"graphPreferences": graph_preferences,
|
"graphPreferences": graph_preferences,
|
||||||
@ -435,3 +452,20 @@ def _handle_dynamic_request(request: DynamicRequest) -> Response:
|
|||||||
return request()
|
return request()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return flask.make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR)
|
return flask.make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
def legacy_page_data() -> Response:
|
||||||
|
id = int(request.args["id"])
|
||||||
|
if html := aqt.mw.mediaServer.get_page_html(id):
|
||||||
|
return Response(html, mimetype="text/html")
|
||||||
|
else:
|
||||||
|
return flask.make_response("page not found", HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
# this currently only handles a single method; in the future, idempotent
|
||||||
|
# requests like i18nResources should probably be moved here
|
||||||
|
def _extract_dynamic_get_request(path: str) -> DynamicRequest | None:
|
||||||
|
if path == "legacyPageData":
|
||||||
|
return legacy_page_data
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
@ -120,7 +120,11 @@ class AnkiWebPage(QWebEnginePage):
|
|||||||
def acceptNavigationRequest(
|
def acceptNavigationRequest(
|
||||||
self, url: QUrl, navType: Any, isMainFrame: bool
|
self, url: QUrl, navType: Any, isMainFrame: bool
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if not self.open_links_externally or "_anki/pages" in url.path():
|
if (
|
||||||
|
not self.open_links_externally
|
||||||
|
or "_anki/pages" in url.path()
|
||||||
|
or url.path() == "/_anki/legacyPageData"
|
||||||
|
):
|
||||||
return super().acceptNavigationRequest(url, navType, isMainFrame)
|
return super().acceptNavigationRequest(url, navType, isMainFrame)
|
||||||
|
|
||||||
if not isMainFrame:
|
if not isMainFrame:
|
||||||
@ -315,12 +319,23 @@ class AnkiWebView(QWebEngineView):
|
|||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def _setHtml(self, html: str) -> None:
|
def _setHtml(self, html: str) -> None:
|
||||||
|
"""Send page data to media server, then surf to it.
|
||||||
|
|
||||||
|
This function used to be implemented by QWebEngine's
|
||||||
|
.setHtml() call. It is no longer used, as it has a
|
||||||
|
maximum size limit, and due to security changes, it
|
||||||
|
will stop working in the future."""
|
||||||
from aqt import mw
|
from aqt import mw
|
||||||
|
|
||||||
oldFocus = mw.app.focusWidget()
|
oldFocus = mw.app.focusWidget()
|
||||||
self._domDone = False
|
self._domDone = False
|
||||||
self._page.setHtml(html)
|
|
||||||
|
webview_id = id(self)
|
||||||
|
mw.mediaServer.set_page_html(webview_id, html)
|
||||||
|
self.load_url(QUrl(f"{mw.serverURL()}_anki/legacyPageData?id={webview_id}"))
|
||||||
|
|
||||||
# work around webengine stealing focus on setHtml()
|
# work around webengine stealing focus on setHtml()
|
||||||
|
# fixme: check which if any qt versions this is still required on
|
||||||
if oldFocus:
|
if oldFocus:
|
||||||
oldFocus.setFocus()
|
oldFocus.setFocus()
|
||||||
|
|
||||||
@ -646,4 +661,9 @@ document.head.appendChild(style);
|
|||||||
Must be done on Windows prior to changing current working directory."""
|
Must be done on Windows prior to changing current working directory."""
|
||||||
self.requiresCol = False
|
self.requiresCol = False
|
||||||
self._domReady = False
|
self._domReady = False
|
||||||
self._page.setContent(bytes("", "ascii"))
|
self._page.setContent(cast(QByteArray, bytes("", "ascii")))
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
from aqt import mw
|
||||||
|
|
||||||
|
mw.mediaServer.clear_page_html(id(self))
|
||||||
|
Loading…
Reference in New Issue
Block a user