bindings/python was extracted to its own repository (imported there at
full fidelity from ed966e15). Development docs, wheel CI, and the
benchmarks' editable dependency now live against that repo.
1 - Split out test to have more granular units
2 - Try to make our test and implementation consistent with Chrome and Firefox
- Chrome and Firefox diverge here in a lot of ways (e.g. serialization),
and there isn't always a WPT tests to break the tie.
3 - Removed some defensive code around OOM. OOM isn't generally recoverable
4 - SVGTransform no longer pretends to be 3D. #D writes drop the z/w component
and stays SVG_TRANSFORM_MATRIX
Small improvement to WPT correctness with no regression (though, the WPT tests
here seem to be a little lacking from what I can tell, which probably helps
explain why Chrome and Firefox disagree on things).
Keep SVGTransform.matrix synchronized with its owning transform and reflected attribute, including read-only animVal and 3D matrix state. Reserve collection storage before attribute commits so allocation failures cannot detach live objects or leave snapshots inconsistent.
Applies the same dictionary lookup pattern we did in https://github.com/lightpanda-io/browser/pull/3034
to the new Graphics, Polyline and Polygon fields.
Caps total memory from snapshot and retired items by collecting them in a buffer
rather than the frame arena. This allows the memory to be re-used at each sync.
Remove the Frame dependency from DOMPointReadOnly and DOMPoint. These types are
also accessible from a Worker (which has no Frame). The frame is captured at
creation time which the callback can access.
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.
HTMLSelectElement collected its options from its direct children only, so an
<option> inside an <optgroup> was invisible to options, length, value,
selectedIndex, selectedOptions and to form submission. A grouped select could
not be read, set or submitted, while querySelectorAll("option") still found
every option.
Per HTML §the-select-element a select's list of options is its option element
descendants: its option children plus the option children of its optgroup
children.
- add a select_options collection mode, and move selected_options onto the
same walk, matching an option whose parent is the select or an optgroup
child of the select
- add one OptionIterator in Select.zig, used by effectiveOption, setValue,
getSelectedIndex, setSelectedIndex, getLength and add
- effectiveOption skips options disabled through <optgroup disabled>
(concept-option-disabled); those options only become reachable now that
grouped options are part of the list
- select.add(option, index) inserts into the optgroup when the option at that
index lives in one, per §dom-select-add
Closes#3057
JS controller.enqueue stores chunks as .js_value via enqueueValue, so
collectBodyBytes must extract TypedArray/ArrayBuffer/string bytes with
local scope and arena.dupe before buffering. Add multi-chunk success and
open/locked reject fixtures in fetch.html.
BodyInit.extract returned an empty body for ReadableStream request
bodies, so fetch POST with duplex:half silently sent Content-Length: 0.
Drain closed streams synchronously via collectBodyBytes; reject open,
locked, or errored streams with TypeError instead of sending nothing.
Fixeslightpanda-io/browser#3052
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.
PR_SET_PDEATHSIG on the spawned lightpanda mcp process (Linux) so a
SIGKILLed or crashed interpreter cannot leak a browser process — a
2h44m orphan surfaced during the v6 benchmark campaign. Regression
test kills a parent and asserts the sidecar exits.
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));
}
Browser(args=[...]) forwards flags like --http-cache-dir to the spawned
lightpanda mcp process; needed by the pandascript-vs-cdp benchmark leg
and useful for cookies/robots flags generally.