Give accurate connectionId, connectionReused, initialPriority and securityState
values.
Always set `referrerPolicy` to `unsafe-url` as the most honest answer (we should
implement proper referrer policy!).
For workers, track the underlying frame_id so that it can be used for the
`documentURL` field.
Follows the observer, user_input, etc.. pattern and relocates various
`Frame.parse*` methods into Frame.parse.* functions.
Goal is just to keep Frame neat.
Expand mime type to be aware of more XML types. DOMParser and XHR now use the
existing Parser.parseXML (via a Frame helper), rather than the HTML parser. Both
these APIs predate the addition of parseXML.
Keep worker requests visible on the page CDP session when their WorkerGlobalScope frame id is absent from the document frame tree. Add a regression fixture covering the worker script and a fetch from inside the worker.
`Form.normalizeMethod` already canonicalizes `method="dialog"`, but
`Frame.submitForm` only branched on `"post"`, so a dialog submission fell
through to the GET path: the document was reloaded, the dialog kept its
`open` attribute, `returnValue` was never set, and no `close` event fired.
A promise awaiting `close` — the idiomatic way to await a confirm dialog —
hung forever while the page reloaded underneath it.
Add the branch the HTML form-submission algorithm calls for: walk up from
the form to the nearest ancestor `<dialog>` and close it with the
submitter's value through the existing `Dialog.close()`, then return
without scheduling a navigation. With no ancestor dialog the submission is
dropped, which is also what the spec asks for — and still not a navigation.
Completes the `<dialog>` support started in #2435, which implemented
`show`/`showModal`/`close`/`returnValue` but left the form-submission
integration that drives them unwired.
Closes#3053
The JS-facing append() and set() took `value: []const u8`, so js.Bridge
coerced any object argument through toString(). Passing a Blob or File stored
the literal text "[object Blob]" / "[object File]" and the bytes never reached
the wire: a FormData carrying a 200 KB File produced a 209-byte request body,
with no filename parameter and no per-part Content-Type. The request still
succeeded with a 200, so nothing surfaced the loss.
Only the binding was string-typed. Entry.Value already carries a `file: *File`
variant with refcounting, and the multipart writer already emits filename= and
a per-part Content-Type for it — that is how <input type=file> submissions
work today.
Take a tagged union instead, following the shape body_init.zig already uses
for BodyInit (`bytes` last, so js.Bridge only falls back to a string once the
JsApi classes have been tried), and implement the entry-creation steps from
https://xhr.spec.whatwg.org/#create-an-entry: a Blob that is not a File
becomes a File named "blob", and an explicit filename produces a new File over
the same bytes rather than renaming the caller's object.
The internal append(name, value) helper keeps its string signature — it backs
form collection and the existing unit tests, neither of which goes through JS.
Closes#3050
A zig-v8-fork release names its assets after the V8 version only, so the
same filename is reused across fork tags:
v0.5.0 libc_v8_14.9.207.35_macos_aarch64.a 106944896 bytes
v0.5.1 libc_v8_14.9.207.35_macos_aarch64.a 106945416 bytes
v0.5.2 libc_v8_14.9.207.35_macos_aarch64.a 106945984 bytes
V8_CACHE was keyed on that filename alone, so a zig-v8 tag bump that
leaves V8_VERSION unchanged did not invalidate the cache: download-v8's
`test -f` guard saw the old archive and skipped the refresh. The build
then linked yesterday's bindings and failed on undefined symbols for
whatever the new tag added, e.g. after the v0.5.2 bump:
error: undefined symbol: _v8__Value__IsFloat16Array
error: undefined symbol: _v8__V8__SetFlagsFromString
error: undefined symbol: _v8__Isolate__AddNearHeapLimitCallback
Putting the tag in the cache path gives each tag its own entry, so a bump
downloads and a repeat run still hits the cache. The URL keeps using the
bare asset name, which is what the release actually publishes.
Only affects local development; CI installs V8 through the install action
and never calls download-v8.
Both aliased clientWidth/clientHeight, computed from the element's own box
without looking at its children, so any "measure, mutate, re-measure" loop
never terminated. The marquee idiom on readthetrieb.com is the reported case:
while (lane.scrollWidth < track.offsetWidth * 2)
lane.innerHTML += lane.innerHTML;
Doubling the markup left scrollWidth unchanged, so the loop spun while
innerHTML grew the DOM exponentially and wedged the page in under a second.
Return max(clientSize, contentSize), summing the direct child elements.
- No layout-mode detection: getStyle() sees only the inline style attribute,
so display:flex or white-space:nowrap from a stylesheet is invisible. Each
axis assumes the arrangement that produces overflow — width lays children in
a row, height stacks them — bounding content extent per axis rather than
modelling one layout.
- Text children are not measured. Estimating a run from its length needs a
per-character advance that tracks font-size, or shrink-to-fit loops stop
converging, and it reports overflow for nearly every element holding text.
- Direct children only, so cost is O(fan-out), with a shared VisibilityCache
collapsing N ancestor walks into one.
- <html>/<body> keep their synthetic defaults, so page-level overflow and
infinite-scroll checks are unaffected; only inner containers change.
Tests in element/position.html cover growth per child, text contributing to
neither axis, hidden elements, the root carve-out, and both loops terminating.
V8's terminate isn't pre-emptive. We can arm it, but it still hast to cross
a boundary to fire. JavaScript that triggers a near endless Zig loop won't be
able to interrupt the Zig code:
```
while (true) {
el.innerHTML += el.innerHTML;
}
```
Can generate a huge HTML input that the Parser will work on for ages. I don't
have a general solution to this. I've made the Parser (and DOMParser) check the
terminate state every 1024 appends. This only covers one specific case, but it's
possibly common enough to guard again, specifically because we don't have
rendering/dimensions, and many websites will append text until certain
dimensions are reached.
But it wouldn't for example, catch a similar:
while (true) {
el.appendChild(el.firstChild.cloneNode(true));
}
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
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.