https://github.com/lightpanda-io/browser/pull/3000 improved the correctness of
ResizeObserver. The main changes were (a) making sure an observe results in
an initial callback and (b) invoking the callback for cases that we can
identity (e.g. visibility change).
Like the other observers, we triggered a check on domChanged. But, unlike the
other observers, the check is relatively expensive, namely because it involves
style lookups.
This commit introduces a number of performance improvements to reduce the check
and dispatch frequency.
1 - Only trigger on an allow list of attribute changed (id, class, hidden, width
...)
2 - Pre-filter only on observed elements
3 - Leverage the visibility cache for more efficient delivery
CDP driver can send multiple Network.enable which would register the same
listener multiple times. This commit makes it so that only one (the first)
callback registered for a listener+eventtype is used. Subsequent registration
for the same listener+eventtype are ignored. This is safe because all callbacks
are currently static. It's a mistake (enforced by a debug-only assertion) for
code to try to register a different callback for an already registered listener+
eventtype.
This generalizes https://github.com/lightpanda-io/browser/pull/3038
pip-installable Python API driving the bundled binary over MCP HTTP:
Browser/Session with one generated method per browser tool
(lightpanda/_methods.py, regenerated from tools/list), run_script()
replay, a CLI trampoline entry point, pytest suite against local
fixtures, and pdoc docs generation.
https://github.com/lightpanda-io/browser/pull/2999 fixed a spin loop when the
page had no i/o but had tasks, something we'd expect to see at page-load end.
But it introduced a similar spin loop as what it fixed on page-start, before the
page has anything to do.
Specifically, 2999 preventing i/o pollings when tasks were waiting. But, the
code doesn't run tasks until there's a "runnable" task. On Frame start, we
register a background task. Net result is that, for the first 200ms, everything
is fine, but then the task is due, so we don't poll, but we also don't run the
tasks, repeating a tick(0) loop.
The solution is simply to disable the 2999 optimization when there's no
runnable page.
std.Io.Threaded.init_single_threaded sets .allocator = .failing, which
spawnPosix uses to build the child's argv/env, so
std.process.spawn(lp.io, ...) always returns OutOfMemory and printPaged
silently fell back to plain output. Spawn the pager through a local
Threaded instance with a real allocator and the real environ (the
single-threaded one is empty, breaking PATH lookup of the less
fallback).
Centralize empty-pattern filtering in UrlBlocklist.init/initPatterns so
the CDP setBlockedURLs path matches the CLI --block-urls behavior. Keep
blocks aligned with the filtered patterns.
Reimplement request blocking on the non-deprecated urlPatterns shape:
each pattern carries an explicit block/allow flag (first match wins),
UrlBlocklist owns the compiled patterns plus their block flags, and the
legacy setBlockedUrls path stays for back-compat. Tests updated.
Include parentId on child frame navigation events and recursively serialize child frames in Page.getFrameTree using their live frame and loader IDs.
Fixes#3014
Currently, Runner assumes that HttpClient.tick did something. But it's possible
that HttpClient had nothing to do, and thus didn't poll. In that case, Runner
would return .{.ok = 0} and Runner's caller would also certainly call
Runner.tick again, resulting in a spin-loop.
The reason Runner allows this to happen is because it can still have macrotasks
to run. So now, when HttpClient.tick has done nothing, Runner will return its
ms_to_next_task, rather than 0.
On sites where all i/o is completed, and only macrotasks are waiting, this
significantly reduces CPU usage.
This gives ResizeObserver a touch up that brings it on par with how the more
flushed out IntersectionObserver behaves. While it's impossible to fully
implement this in a headless world, we can correctly emit ResizeObserverEntry
in a number of important cases, e.g. visibility change, and style width/height
changes.
Most importantly, we not execute the callback on the initial observe, which can
unlock some cases.
1 - Blocks connection to "bad" ports
2 - new WebSocket('...'); mostly returns the instance, and errors are emitted
in a close event (1)
3 - More validation (no # allowed, ...);
4 - http -> ws, https -> wss (yes, per spec)
There's also some groundwork for better float16 support, but it turns out this
will require a new v8 build to complete. So I left the harmless float16 mapping
in (but, without the matching Float16Array, it doesn't help much).
(1) Some things cause an exception to be raised, but most don't