#!/usr/bin/env python3
"""Exercise one-click input, native web selectors and URL sharing without sending."""
import importlib.util
import json
from pathlib import Path
import threading
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from marionette_client import Client
spec=importlib.util.spec_from_file_location('probe',Path(__file__).with_name('probe-desktop.py'))
probe=importlib.util.module_from_spec(spec);spec.loader.exec_module(probe)
class Page(BaseHTTPRequestHandler):
def do_GET(self):
data=b'''
Web controls test
Editable
'''
self.send_response(200);self.send_header('Content-Type','text/html');self.send_header('Content-Length',str(len(data)));self.end_headers();self.wfile.write(data)
def log_message(self,*args):pass
server=ThreadingHTTPServer(('127.0.0.1',0),Page)
threading.Thread(target=server.serve_forever,daemon=True).start()
url=f'http://127.0.0.1:{server.server_port}/'
checks={}
try:
with Client(2857) as c:
for _ in range(120):
if c.execute("return !!Services.wm.getMostRecentWindow('vulpes:host')?.document.getElementById('system');"):break
time.sleep(.25)
probe.register(c)
c.execute("const w=Services.wm.getMostRecentWindow('vulpes:host');w.focus();w.document.getElementById('system').focus();return true;")
def query(app,kind,data):
selector="""const w=Services.wm.getMostRecentWindow('vulpes:host');let bc;
if(APP==='web')bc=[...w.document.querySelectorAll('browser')].find(b=>b.id!=='system' && b.style.visibility==='visible')?.browsingContext;
else {function find(b){if(b.currentWindowGlobal?.documentURI.spec.startsWith('http://'+APP+'.localhost:8765/'))return b;for(const child of b.children){const v=find(child);if(v)return v;}}bc=find(w.document.getElementById('system').browsingContext);}
if(!bc){done({missing:true});return;}
bc.currentWindowGlobal.getActor('VulpesProbe').sendQuery(KIND,DATA).then(value=>done({value}),error=>done({error:String(error)}));"""
script='const done=arguments[arguments.length-1];'+selector.replace('APP',json.dumps(app)).replace('KIND',json.dumps(kind)).replace('DATA',json.dumps(data))
r=c.call('WebDriver:ExecuteAsyncScript',{'script':script,'args':[],'sandbox':'system','newSandbox':True})['value']
if r.get('missing'):return None
assert 'value' in r,r
return r['value']
def ev(app,code):return query(app,'Evaluate',code)
def wait(app,code):
for _ in range(80):
result=ev(app,code)
if result:return result
time.sleep(.15)
raise AssertionError(code)
def click(app,expression):
r=ev(app,'return ('+expression+').getBoundingClientRect().toJSON();')
query(app,'MouseClick',{'x':r['x']+r['width']/2,'y':r['y']+r['height']/2})
time.sleep(.5)
wait('system',"return document.body?.getAttribute('ready-state')==='fullyLoaded';")
ev('system','ScreenManager.turnScreenOn(true);if(Service.query("locked"))await Service.request("unlock",{forcibly:true});await VulpesCompat.call("activities.cancel",{});core.appCore.rocketbar.handleCancel();dispatchEvent(new CustomEvent("home"));return true;')
time.sleep(1)
click('system','document.querySelector("#rocketbar")')
checks['home-first-click']=bool(wait('system','return core.appCore.rocketbar.active && !document.querySelector("#vulpes-gaia-keyboard")?.hidden;'))
ev('system','core.appCore.rocketbar.handleCancel();return true;');time.sleep(.5)
ev('system',"new MozActivity({name:'view',data:{type:'url',url:"+json.dumps(url)+"}});return true;")
time.sleep(2)
wait('web','return !!document.querySelector("#text");')
click('web','document.querySelector("#text")')
checks['web-input-first-click']=bool(wait('system','return !document.querySelector("#vulpes-gaia-keyboard")?.hidden;'))
ev('keyboard',"await navigator.mozInputMethod.inputcontext.sendKey('a',0);return true;")
checks['web-input-edits']=ev('web','return document.querySelector("#text").value==="a";')
click('web','document.querySelector("#select")')
time.sleep(.3)
checks['web-select-opens']=c.execute("return Services.wm.getMostRecentWindow('vulpes:host').document.getElementById('ContentSelectDropdownPopup')?.state==='open';")
print(json.dumps(checks),flush=True)
if checks['web-select-opens']:
c.execute("const p=Services.wm.getMostRecentWindow('vulpes:host').document.getElementById('ContentSelectDropdownPopup');p.querySelectorAll('menuitem')[1].doCommand();p.hidePopup();return true;")
checks['web-select-changes']=bool(wait('web','return document.querySelector("#select").value==="two";'))
click('web','document.querySelector("#editor span")')
ev('keyboard',"await navigator.mozInputMethod.inputcontext.sendKey('b',0);return true;")
checks['nested-contenteditable']=ev('web','return document.querySelector("#editor").textContent.includes("b");')
click('web','document.querySelector("#shadow").shadowRoot.querySelector("input")')
ev('keyboard',"await navigator.mozInputMethod.inputcontext.sendKey('c',0);return true;")
checks['shadow-input']=ev('web','return document.querySelector("#shadow").shadowRoot.querySelector("input").value==="c";')
ev('system','Service.query("getActiveApp").appChrome.overflowMenu.show();return true;')
time.sleep(.3)
click('system','Service.query("getActiveApp").appChrome.shareButton')
checks['share-choices']=wait('system','return document.querySelector("#action-menu")?.checkVisibility() && document.querySelectorAll("#action-menu-list button[data-value]").length===2;')
click('system','document.querySelector("#action-menu-list button")')
time.sleep(1)
checks['share-url-to-sms']=bool(wait('sms','return document.querySelector("#messages-input")?.textContent.includes('+json.dumps(url)+');'))
print(json.dumps(checks,indent=2),flush=True)
assert all(checks.values()),checks
ev('system','await VulpesCompat.call("activities.cancel",{});dispatchEvent(new CustomEvent("home"));return true;')
finally:server.shutdown()