117 lines
3.8 KiB
Python
117 lines
3.8 KiB
Python
"""Probes Firefox's web-platform support by loading a page that reports back.
|
|
|
|
Usage: python3 tools/probe_firefox.py
|
|
Launches headless Firefox against a local page whose script POSTs its feature
|
|
detection results back to this server, then prints them.
|
|
"""
|
|
|
|
import http.server
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import threading
|
|
|
|
PORT = 8766
|
|
|
|
# Snap-confined Firefox cannot see /tmp, so the profile lives next to the repo.
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
PROFILE = os.path.join(ROOT, 'tmp-ffprof-probe')
|
|
|
|
USER_JS = '''
|
|
user_pref("browser.shell.checkDefaultBrowser", false);
|
|
user_pref("browser.startup.homepage_override.mstone", "ignore");
|
|
user_pref("datareporting.policy.dataSubmissionEnabled", false);
|
|
user_pref("toolkit.telemetry.enabled", false);
|
|
user_pref("app.update.auto", false);
|
|
'''
|
|
|
|
PAGE = """<!doctype html>
|
|
<html><head><link rel="manifest" href="/manifest.webmanifest"></head>
|
|
<body><script>
|
|
const info = {
|
|
userAgent: navigator.userAgent,
|
|
beforeinstallprompt: 'onbeforeinstallprompt' in window,
|
|
serviceWorker: 'serviceWorker' in navigator,
|
|
wakeLock: 'wakeLock' in navigator,
|
|
vibrate: typeof navigator.vibrate === 'function',
|
|
audioContext: !!(window.AudioContext || window.webkitAudioContext),
|
|
localStorage: (() => { try { localStorage.setItem('t', '1'); return true; } catch { return false; } })(),
|
|
standaloneDisplayMode: matchMedia('(display-mode: standalone)').matches,
|
|
secureContext: window.isSecureContext,
|
|
manifestParsed: (() => {
|
|
const m = document.querySelector('link[rel=manifest]');
|
|
return m ? m.href : null;
|
|
})(),
|
|
};
|
|
fetch('/report', { method: 'POST', body: JSON.stringify(info) });
|
|
</script></body></html>"""
|
|
|
|
report = {}
|
|
done = threading.Event()
|
|
|
|
|
|
class Handler(http.server.BaseHTTPRequestHandler):
|
|
def do_GET(self):
|
|
if self.path == '/manifest.webmanifest':
|
|
body = json.dumps({
|
|
"name": "Probe", "short_name": "Probe", "start_url": "./",
|
|
"display": "standalone",
|
|
"icons": [{"src": "/icon.png", "sizes": "192x192", "type": "image/png"}],
|
|
}).encode()
|
|
self.send_response(200)
|
|
self.send_header('Content-Type', 'application/manifest+json')
|
|
self.send_header('Content-Length', str(len(body)))
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
return
|
|
body = PAGE.encode()
|
|
self.send_response(200)
|
|
self.send_header('Content-Type', 'text/html')
|
|
self.send_header('Content-Length', str(len(body)))
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
|
|
def do_POST(self):
|
|
length = int(self.headers.get('Content-Length', 0))
|
|
report.update(json.loads(self.rfile.read(length) or b'{}'))
|
|
self.send_response(204)
|
|
self.end_headers()
|
|
done.set()
|
|
|
|
def log_message(self, *args):
|
|
pass
|
|
|
|
|
|
def main():
|
|
server = http.server.ThreadingHTTPServer(('127.0.0.1', PORT), Handler)
|
|
threading.Thread(target=server.serve_forever, daemon=True).start()
|
|
|
|
shutil.rmtree(PROFILE, ignore_errors=True)
|
|
os.makedirs(PROFILE, exist_ok=True)
|
|
with open(os.path.join(PROFILE, 'user.js'), 'w') as fh:
|
|
fh.write(USER_JS)
|
|
|
|
proc = subprocess.Popen(
|
|
['firefox', '--headless', '--no-remote', '--new-instance',
|
|
'--profile', PROFILE, f'http://127.0.0.1:{PORT}/'],
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
)
|
|
|
|
ok = done.wait(timeout=30)
|
|
proc.terminate()
|
|
try:
|
|
proc.wait(timeout=10)
|
|
except subprocess.TimeoutExpired:
|
|
proc.kill()
|
|
server.shutdown()
|
|
shutil.rmtree(PROFILE, ignore_errors=True)
|
|
|
|
if not ok:
|
|
print('probe: no report received (Firefox did not run the page)')
|
|
return
|
|
print(json.dumps(report, indent=2, sort_keys=True))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main() |