""" This is a fully fledged example for installing or reloading
a temporary addon with automatic refresh on file changes.
"""
import argparse
import json
from pathlib import Path
from threading import Timer
from time import sleep
from urllib.parse import quote
from watchdog.events import EVENT_TYPE_CREATED, FileSystemEvent, FileSystemEventHandler
from watchdog.observers import Observer
from geckordp.actors.addon.addons import AddonsActor
from geckordp.actors.descriptors.tab import TabActor
from geckordp.actors.descriptors.web_extension import WebExtensionActor
from geckordp.actors.root import RootActor
from geckordp.actors.targets.window_global import WindowGlobalActor
from geckordp.rdp_client import RDPClient
""" Uncomment to enable debug output
"""
# from geckordp.settings import GECKORDP
# GECKORDP.DEBUG = 1
# GECKORDP.DEBUG_REQUEST = 1
# GECKORDP.DEBUG_RESPONSE = 1
class DirectoryObserver(FileSystemEventHandler):
def __init__(self, addon_actor: WebExtensionActor):
self.__delay_sec = 0.7
self.__addon = addon_actor
self.__delay_timer = Timer(-1, self.reload)
def on_any_event(self, event: FileSystemEvent):
self.__delay_trigger(event)
def __delay_trigger(self, event: FileSystemEvent):
self.__delay_timer.cancel()
self.__delay_timer = Timer(self.__delay_sec, self.reload, args=[event])
self.__delay_timer.start()
def reload(self, event: FileSystemEvent):
response = self.__addon.reload()
print(f"reload '{event.src_path}':\n{json.dumps(response, indent=2)}")
def main():
# parse arguments
parser = argparse.ArgumentParser(description="")
parser.add_argument(
"--host", type=str, default="localhost", help="The host to connect to"
)
parser.add_argument(
"--port", type=int, default="6000", help="The port to connect to"
)
parser.add_argument(
"--addon", type=str, default="", metavar="<addonpath>", help="The addon path"
)
parser.add_argument("--list", action="store_true", help="List all addons")
parser.add_argument(
"--visit", action="store_true", help="Visit the debug page of the addon"
)
args, _ = parser.parse_known_args()
if args.addon == "":
parser.print_help()
return 1
# create client and connect to firefox
client = RDPClient()
client.connect(args.host, args.port)
# get global actors IDs from root actor
root = RootActor(client)
root_actor_ids = root.get_root()
# if user just wants to know the list of addons, print it and return
if args.list:
print(json.dumps(root.list_addons(), indent=2))
return
# check addon path
addon_path = Path(args.addon).absolute()
if not addon_path.exists():
print(f"addon path {addon_path} doesn't exist")
return
addon_path = str(addon_path)
# install temporary addon if not found, else set flag for reloading it
addons = [
addon for addon in root.list_addons() if addon_path in addon.get("url", "")
]
reload_now = False
if len(addons) == 0:
print(f"install temporary addon from '{addon_path}'")
response = AddonsActor(
client, root_actor_ids["addonsActor"]
).install_temporary_addon(addon_path)
addon_id = response.get("id", None)
if addon_id is None:
print(f"addon could not be loaded:\n'{json.dumps(response, indent=2)}'")
return
print(f"addon '{addon_id}' loaded")
else:
print("addon already installed, start reloading it")
reload_now = True
# check whether addon can be found via list_addons()
addons = [
addon for addon in root.list_addons() if addon_path in addon.get("url", "")
]
if len(addons) == 0:
print("addon wasn't found in list")
return
# retrieve the ID of the found addon and start the file watcher
event_handler = DirectoryObserver(WebExtensionActor(client, addons[0]["actor"]))
observer = Observer()
observer.schedule(event_handler, path=addon_path, recursive=True)
observer.start()
# reload now if flag was set
if reload_now:
ev = FileSystemEvent(addon_path)
ev.event_type = EVENT_TYPE_CREATED # type: ignore
ev.is_directory = True
event_handler.reload(ev)
# visit extension page with debugger
if args.visit:
tab = TabActor(client, root.current_tab()["actor"])
tab_actor_ids = tab.get_target()
web = WindowGlobalActor(client, tab_actor_ids["actor"])
web.navigate_to(
f"about:devtools-toolbox?id={quote(addons[0]['id'])}&type=extension"
)
# sleep and let the observer handle file updates
try:
while True:
sleep(1)
except KeyboardInterrupt:
observer.stop()
client.disconnect()
if __name__ == "__main__":
main()