webapi¶
The webapi package groups browser-flavoured APIs that sit alongside the DOM surface.
console¶
from domonic.webapi.console import console
console.log("Hello World")
encoding¶
from domonic.webapi.encoding import TextEncoder, TextDecoder
encoded = TextEncoder().encode("hello")
decoded = TextDecoder("utf-8").decode(encoded)
print(decoded)
fetch¶
from domonic.webapi.fetch import fetch
# fetch() returns a Promise, same as in the browser
fetch("https://example.com").then(lambda response: print(response.text()[:15]))
# <!doctype html>
XHR¶
XMLHttpRequest and FormData provide browser-shaped request helpers for
code that was originally written against web APIs.
from domonic.webapi.xhr import FormData, XMLHttpRequest
data = FormData()
data.append("name", "domonic")
request = XMLHttpRequest()
request.open("POST", "https://example.com/api")
request.send(data)
URLPattern¶
URLPattern matches URLs against path, hostname, protocol, and search
patterns.
from domonic.webapi.urlpattern import URLPattern
pattern = URLPattern({"pathname": "/users/:id"})
print(pattern.test("https://example.com/users/42"))
Window¶
Window – location, history, screen, matchMedia, animation frames,
scrolling and native host attachment – has its own page: window.
Geolocation¶
The geolocation helper is deterministic and test-friendly: set coordinates, read the current position, or watch for position changes.
from domonic.webapi.geo import Geolocation
geo = Geolocation()
geo.setPosition({"latitude": 51.5072, "longitude": -0.1276, "accuracy": 10})
geo.getCurrentPosition(lambda position: print(position.coords.latitude))
# 51.5072
Web Crypto¶
Crypto provides secure random values, UUIDs, and digest hashes through a
browser-like crypto object.
from domonic.javascript import Uint8Array
from domonic.webapi.crypto import crypto
token = Uint8Array(16)
crypto.getRandomValues(token)
print(crypto.randomUUID())
print(crypto.subtle.digest("SHA-256", b"domonic").data.hex())
Messaging¶
MessageChannel and BroadcastChannel provide browser-style in-process
message wiring for worker-like code and tests.
from domonic.webapi.messaging import BroadcastChannel, MessageChannel
channel = MessageChannel()
channel.port1.onmessage = lambda event: print(event.data)
channel.port2.postMessage("hello")
updates = BroadcastChannel("updates")
updates.onmessage = lambda event: print(event.data)
BroadcastChannel("updates").postMessage({"ok": True})
Web Workers¶
Worker runs a local Python script or callable in a daemon thread with a
browser-style DedicatedWorkerGlobalScope. Messages are cloned between the
parent and worker, and both sides support onmessage, messageerror and
error events.
from threading import Event
from domonic.webapi.webworkers import Worker
done = Event()
def worker_main(scope):
scope.onmessage = lambda event: scope.postMessage(event.data.upper())
worker = Worker(worker_main)
worker.onmessage = lambda event: (print(event.data), done.set())
worker.postMessage("hello")
done.wait(2)
worker.terminate()
Scheduler¶
Scheduler and TaskController provide a small Prioritized Task Scheduling
surface for ordered server-side work.
from domonic.webapi.scheduler import scheduler
scheduler.postTask(lambda: "done", {"priority": "user-visible"})
Streams¶
Readable, writable, transform, compression, and decompression streams are available for Web Streams-style examples and tests.
from domonic.webapi.streams import ReadableStream
stream = ReadableStream(b"hello world")
print(stream.getReader())
# b'hello world'
print(stream.read(5))
# b'hello'
print(stream.read())
# b' world'
ReadableStream also accepts an underlying source dict/object with
start/pull/cancel callbacks. In that mode getReader() returns a
ReadableStreamDefaultReader whose read() yields {"value": ...,
"done": ...} (the reader is a plain Python iterator too), pull is driven by
backpressure derived from an optional queuing strategy, and cancel(),
tee() and locked behave as in the browser.
from domonic.webapi.streams import ReadableStream, CountQueuingStrategy
def make_source(items):
def pull(controller):
if items:
controller.enqueue(items.pop(0))
else:
controller.close()
return {"pull": pull}
stream = ReadableStream(
make_source([b"a", b"b", b"c"]),
CountQueuingStrategy({"highWaterMark": 2}),
)
reader = stream.getReader()
print(reader.read())
# {'value': b'a', 'done': False}
print(list(reader))
# [b'b', b'c']
CountQueuingStrategy measures the internal queue by chunk count and
ByteLengthQueuingStrategy measures it by byteLength; both expose
highWaterMark and size() and can be passed to ReadableStream or
WritableStream. WritableStream.getWriter() returns a
WritableStreamDefaultWriter with write(), close(), abort(),
releaseLock() and a desiredSize derived from the strategy.
Canvas and WebGL¶
HTMLCanvasElement.getContext() supports inspectable 2d, webgl and
webgl2 contexts. These contexts record drawing/setup commands so generated
examples and tests can verify canvas output without a browser renderer.
from domonic.html import canvas
surface = canvas(width=320, height=180)
ctx = surface.getContext("2d")
ctx.fillStyle = "#f00"
ctx.fillRect(0, 0, 20, 20)
print(ctx.commands[-1]["name"], ctx.commands[-1]["args"])
# fillRect [0, 0, 20, 20]
Each recorded command also carries a state snapshot (fillStyle,
strokeStyle, globalAlpha, transform, …) of the paint state active
when it was issued, so replaying ctx.commands later draws with the right
style even if later commands changed it.
CSS Font Loading¶
FontFace and FontFaceSet model the browser font-loading surface and are
available through document.fonts.
from domonic.dom import Document
from domonic.webapi.cssfontloading import FontFace
doc = Document()
doc.fonts.add(FontFace("Demo", "url(/demo.woff2)")).load("16px Demo")
File API¶
Blob, File, FileList and FileReader mirror the browser File API and
work with fetch, FormData and drag-and-drop helpers.
from domonic.webapi.file import Blob, File, FileReader
from domonic.webapi.url import URL
file = File([b"hello"], "hello.txt", {"type": "text/plain"})
reader = FileReader()
reader.onload = lambda event: print(reader.result)
reader.readAsText(file)
object_url = URL.createObjectURL(file)
Sanitizer API¶
Sanitizer cleans HTML fragments into domonic nodes without evaluating the
input. It supports the current elements/removeElements and
attributes/removeAttributes configuration names, plus the older domonic
aliases.
from domonic.html import div
from domonic.webapi.sanitizer import Sanitizer
clean = Sanitizer().sanitizeToString(
'<p onclick="evil()">Hello <script>bad()</script></p>'
)
assert clean == "<p>Hello </p>"
target = div()
target.setHTML('<a href="javascript:evil()">link</a>')
assert str(target) == "<div><a>link</a></div>"
Notifications and Gamepad¶
Notification provides browser-style notification objects without OS side
effects, while GamepadManager backs navigator.getGamepads() for tests and
interactive examples.
from domonic.webapi.gamepad import Gamepad
from domonic.webapi.notifications import Notification
from domonic.window import Window
Notification.requestPermission()
notice = Notification("Done", {"body": "Build finished"})
notice.show()
win = Window()
win.navigator.connectGamepad(Gamepad("Pad"))
print(win.navigator.getGamepads())
Performance¶
performance.now() is milliseconds since the time origin, from a monotonic
clock. requestAnimationFrame timestamps, Event.timeStamp,
document.timeline.currentTime and Gamepad.timestamp are read from the
same clock, so they compare with each other. Marks and measures feed
PerformanceObserver.
from domonic.webapi.performance import PerformanceObserver, performance
performance.mark("parse-start")
# ... work ...
measure = performance.measure("parse", "parse-start")
print(round(measure.duration, 2), "ms")
observer = PerformanceObserver(lambda entries, obs: print(entries.getEntriesByType("measure")))
observer.observe({"entryTypes": ["measure"]})
Service Workers¶
ServiceWorkerContainer and registrations model the lifecycle enough for DOM
tests and worker-style examples without requiring a browser runtime.
from domonic.webapi.serviceworker import ServiceWorkerContainer
container = ServiceWorkerContainer("https://example.com/app/")
registration = container.register("/sw.js")
URL¶
URL is a wrapper around Python’s urlparse and urlencode helpers.
from domonic.webapi.url import URL
myurl = URL("http://www.google.com/search?q=domonic")
print(myurl.host)
# www.google.com
print(myurl.search)
# ?q=domonic
print(myurl.searchParams)
# q=domonic
print(myurl.searchParams.get("q"))
# domonic
Object URLs work with the File API:
from domonic.webapi.file import Blob
from domonic.webapi.url import URL
blob = Blob(["hello"], {"type": "text/plain"})
url = URL.createObjectURL(blob)
print(url)
# blob:domonic/<uuid>
URL.revokeObjectURL(url)
For more information see the MDN URL API docs: https://developer.mozilla.org/en-US/docs/Web/API/URL
XPATH¶
Here’s a quick example of using XPath:
from domonic import domonic
from domonic.webapi.xpath import XPathEvaluator, XPathResult
somehtml = '''
<div>XPath example</div>
<div>Number of <div>s: <output></output></div>
'''
page = domonic.parseString(somehtml)
evaluator = XPathEvaluator()
expression = evaluator.createExpression("//div")
result = expression.evaluate(page, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE)
print(result.snapshotLength)
# 2
assert result.snapshotLength == 2
For more information, see the MDN Web API docs: https://developer.mozilla.org/en-US/docs/Web/API