From f77047fc4208dbf41d0408a8d9e76f7e903747fe Mon Sep 17 00:00:00 2001 From: arenekosreal <17194552+arenekosreal@users.noreply.github.com> Date: Fri, 6 Feb 2026 12:41:46 +0800 Subject: [PATCH] webext: Rewrite native app starting logic Using asyncio.run is preferred by Python. `asyncio.get_event_loop` raises a `RuntimeError` on my machine, which means that there is no event loop set. This is a change in 3.14, seems that we need to use `asyncio.set_event_loop` before running. So I use `asyncio.run` directly to avoid this. Handle `SIGTERM` signal so we can quit gracefully. Firefox will send `SIGTERM` on *nix systems, and use Windows's way to kill the native app. Not using Windows so not sure if `SIGTERM` will also be raised on Windows, but I do not find a clean way to notify killing. See also: https://docs.python.org/3.14/library/asyncio-eventloop.html#asyncio.get_event_loop https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#closing_the_native_app --- webext/app/credential_manager_shim.py | 44 +++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/webext/app/credential_manager_shim.py b/webext/app/credential_manager_shim.py index ace3ba2..764f573 100755 --- a/webext/app/credential_manager_shim.py +++ b/webext/app/credential_manager_shim.py @@ -7,6 +7,7 @@ from enum import Enum import json import logging +import signal import struct import sys from typing import Optional @@ -395,23 +396,26 @@ async def run(cmd, options, origin, top_origin): raise Exception(f"unknown cmd: {cmd}") -logging.info("starting credential_manager_shim") -while True: - logging.debug("starting event loop message") - receivedMessage = getMessage() - request_id = receivedMessage["requestId"] - try: - cmd = receivedMessage["cmd"] - - options = None - if "options" in receivedMessage: - options = receivedMessage["options"] - origin = receivedMessage["origin"] - top_origin = receivedMessage["topOrigin"] - loop = asyncio.get_event_loop() - auth_data = loop.run_until_complete(run(cmd, options, origin, top_origin)) - sendMessage(encodeMessage({"requestId": request_id, "data": auth_data})) - except Exception as e: - logging.error("Failed to send message", exc_info=e) - sendMessage(encodeMessage({"requestId": request_id, "error": str(e)})) - logging.debug("Sent error message") +quit = asyncio.Event() + +async def main(): + logging.info("starting credential_manager_shim") + while not quit.is_set(): + logging.debug("starting event loop message") + receivedMessage = getMessage() + request_id = receivedMessage["requestId"] + try: + cmd = receivedMessage["cmd"] + options = receivedMessage.get("options", None) + origin = receivedMessage["origin"] + top_origin = receivedMessage["topOrigin"] + auth_data = await run(cmd, options, origin, top_origin) + sendMessage(encodeMessage({"requestId": request_id, "data": auth_data})) + except Exception as e: + logging.error("Failed to send message", exc_info=e) + sendMessage(encodeMessage({"requestId": request_id, "error": str(e)})) + logging.debug("Sent error message") + logging.info("quitting credential_manager_shim") + +signal.signal(signal.SIGTERM, lambda _, __ : quit.set()) +asyncio.run(main())