Depends on: https://github.com/lightpanda-io/zig-v8-fork/pull/153
In some ways this is an extension of
https://github.com/lightpanda-io/browser/pull/1635 but it has more implications
with respect to correctness.
A js.Context wraps a v8::Context. One of the important thing it adds is the
identity_map so that, given a Zig instance we always return the same v8::Object.
But imagine code running in a frame. This frame has its own Context, and thus
its own identity_map. What happens when that frame does:
```js
window.top.frame_loaded = true;
```
From Zig's point of view, `Window.getTop` will return the correct Zig instance.
It will return the *Window references by the "root" page. When that instance is
passed to the bridge, we'll look for the v8::Object in the Context's
`identity_map` but wont' find it. The mapping exists in the root context
`identity_map`, but not within this frame. So we create a new v8::Object and now
our 1 zig instance has N v8::Objects for every page/frame that tries to access
it.
This breaks cross-frame scripting which should work, at least to some degree,
even when frames are on the same origin.
This commit adds a `js.Origin` which contains the `identity_map`, along with our
other `v8::Global` storage. The `Env` now contains a `*js.Origin` lookup,
mapping an origin string (e.g. lightpanda.io:443) to an *Origin. When a Page's
URL is changed, we call `self.js.setOrigin(new_url)` which will then either get
or create an origin from the Env's origin lookup map.
js.Origin is reference counted so that it remains valid so long as at least 1
frame references them.
There's some special handling for null-origins (i.e. about:blank). At the root,
null origins get a distinct/isolated Origin. For a frame, the parent's origin
is used.
Above, we talked about `identity_map`, but a `js.Context` has 8 other fields
to track v8 values, e.g. `global_objects`, `global_functions`,
`global_values_temp`, etc. These all must be shared by frames on the same
origin. So all of these have also been moved to js.Origin. They've also been
merged so that we now have 3 fields: `identity_map`, `globals` and `temps`.
Finally, when the origin of a context is changed, we set the v8::Context's
SecurityToken (to that origin). This is a key part of how v8 allows cross-
context access.
- Remove scale, contain-intrinsic-size, animation-range, text-box-edge
from isTwoValueShorthand: these have asymmetric or 3-value semantics
that make "X X" → "X" collapse incorrect.
- Remove line-height from isLengthProperty: bare 0 is the unitless
number multiplier, not a length (Chrome serializes as "0" not "0px").
- Fix test: background-size "cover cover" is invalid CSS, use "auto auto".
Add missing properties to isLengthProperty (0→0px) and
isTwoValueShorthand (duplicate value collapse) maps based
on WPT test failures in css/css-sizing, css/css-align,
css/css-scroll-snap, css/css-logical, and others.
New length properties: scroll-margin/padding-*, column-width,
column-rule-width, grid-column/row-gap, outline, shape-margin,
offset-distance, translate, animation-range-*, block-step-size,
text-decoration-inset, and *-rule-*-inset (CSS Gaps).
New two-value shorthands: scroll-padding-block/inline,
scroll-snap-align, background-size, border-image-repeat,
mask-repeat/size, contain-intrinsic-size, scale, text-box-edge,
animation-range, grid-gap.
Calling it here ensures that the inspector gets reset on internal page
navigation. We were seeing intermittent segfaults on a problematic WPT tests
(/encoding/legacy-mb-japanese/euc-jp/) which I believe this solves.
(The tests are still broken. Because we don't support form targets, they cause
the root page to reload in a tight cycle, causing a lot of context creation /
destruction, which I thin was the issue. This commit doesn't fix the broken test
but it hopefully fixes the crash).
Also, clear out the Inspector's default_context when the default context is
destroyed. (This was the first thing I did to try to fix the crash, it didn't
work, but I believe it's correct).
- TreeWalker.Full instead of FullExcludeSelf so querying a specific
nodeId evaluates the root element itself
- resolve href to absolute URL via URL.resolve
- isDisabled checks ancestor <fieldset disabled> with legend exemption
- parameter order: allocator before *Page per convention
Allows checking if a direct listener exists, if it doesn't, event creation can
be skipped.
I looked at a couple sites, the benefits of this is small.
Most sites don't seem to trigger that many direct dispatches and when they do,
they seem to have a listener 50-75% of the time.
Returns a structured list of all interactive elements on a page:
buttons, links, inputs, ARIA widgets, contenteditable regions, and
elements with event listeners. Includes accessible names, roles,
listener types, and key attributes.
Event listener introspection (both addEventListener and inline
handlers) is unique to LP — no other browser exposes this to
automation code.