VulpesOS/tools/marionette_client.py
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

70 lines
2.2 KiB
Python

"""Small client for the Marionette protocol shipped with current Firefox."""
import json
import socket
import time
class Client:
def __init__(self, port=2856):
deadline = time.monotonic() + 30
while True:
try:
self.socket = socket.create_connection(("127.0.0.1", port), timeout=20)
break
except ConnectionRefusedError:
if time.monotonic() >= deadline:
raise
time.sleep(0.2)
self.sequence = 0
self.quit_requested = False
self.receive()
self.call("WebDriver:NewSession", {"capabilities": {}})
self.call("Marionette:SetContext", {"value": "chrome"})
def receive(self):
length = bytearray()
while not length.endswith(b":"):
chunk = self.socket.recv(1)
if not chunk:
raise ConnectionError("Marionette disconnected")
length += chunk
remaining = int(length[:-1])
payload = bytearray()
while len(payload) < remaining:
chunk = self.socket.recv(remaining - len(payload))
if not chunk:
raise ConnectionError("Marionette disconnected")
payload += chunk
return json.loads(payload)
def call(self, command, arguments=None):
self.sequence += 1
payload = json.dumps([0, self.sequence, command, arguments or {}]).encode()
self.socket.sendall(str(len(payload)).encode() + b":" + payload)
response = self.receive()
if response[2]:
raise RuntimeError(response[2])
if command == "Marionette:Quit":
self.quit_requested = True
return response[3]
def execute(self, script):
result = self.call(
"WebDriver:ExecuteScript",
{"script": script, "args": [], "newSandbox": True, "sandbox": "system"},
)
return result.get("value", result)
def close(self):
try:
if not self.quit_requested:
self.call("WebDriver:DeleteSession")
finally:
self.socket.close()
def __enter__(self):
return self
def __exit__(self, *args):
self.close()