A Page now has a StyleManager. The StyleManager currently answers two questions:
1 - Is an element hidden
2 - Does an element have pointer-events == none
This is used in calls such as element.checkVisibility which, on some pages, can
be called tens of thousands of times (often through other methods, like
element.getBoundingClientRect). This _can_ be a bottleneck.
The StyleManager keeps a list of rules. The rules include the selector,
specificity, and properties that we care about. Rules in a stylesheet that
contain no properties of interest are ignored. This is the first and likely
most significant optimization. Presumably, most CSS rules don't have a
display/visibility/opacity or pointer-events property.
The list is rules is cached until stylesheets are modified or delete. When this
happens, the StyleManager is flagged as "dirty" and rebuilt on-demand in the
next query. This is our second major optimization.
For now, to check if an element is visible, we still need to scan all rules.
But having a pre-build subset of all the rules is a first step.
The next step might be to optimize the matching, or possibly optimizing common
cases (e.g. id and/or simple class selector)
The CDP timeout handler in httpLoop had two compounding bugs:
1. Unit mismatch: timestamp(.monotonic) returns seconds, but
ms_remaining is in milliseconds. The comparison and subtraction
mixed units.
2. Double-counting: In the .done branch, elapsed was computed as
absolute time since last_message, but last_message was never
updated in this branch. Each iteration subtracted the growing
total elapsed seconds from an already-decremented ms_remaining.
During complex page loads, Session._wait() returns .done rapidly
(due to JS macrotask execution, background tasks, or errors). Each
rapid .done return subtracted the growing elapsed (seconds) from
ms_remaining (milliseconds), draining it to zero in ~2 seconds
instead of the configured 10-second timeout.
Fix: use milliTimestamp() for consistent units, update last_message
in the .done branch for incremental elapsed tracking, and use >= for
correct boundary comparison.
Fixes#1849
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
https://github.com/lightpanda-io/browser/pull/1775 made blobs finalizable and
https://github.com/lightpanda-io/browser/pull/1795 made it possible to navigate
from blobs (important for WPT tests). This fixes a number of issues related to
both.
First, weak/strong ref'ing a value now uses the resolved value. When registering
a finalizer, we use the resolved value (the most specific type in the prototype
chain). For this reason, when toggling a weak/strong ref, we have to use the
same resolved value. This solves a segfault where a File is created, but
extended as a Blob (e.g. in createObjectURL).
Next, two issues were fixed when navigating to an invalid blob. First, the frame
is properly removed from the parent list on frame navigation error. Second,
on frame navigation error, we don't stop _all_ other navigations, we just log
the error and move on to the next frame.
processMessage no longer returns Zig errors when dispatchCommand fails —
it sends a CDP error response and continues. Update all expectError calls
to use processMessage + expectSentError instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a CDP command with an unrecognized domain (e.g. `NonExistent.method`)
was sent, the error response was correctly returned but the connection
died immediately after. This happened because dispatch() re-returned the
error after sending the error response, which propagated up through
processMessage() → handleMessage() where `catch return false` closed
the WebSocket connection.
Now the error is only propagated if sendError itself fails (e.g. broken
pipe). Otherwise dispatch() returns normally and the read loop continues.
Fixes#1843
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The CDP Accessibility spec defines AXValue.value as always being a
string, but integer values were serialized as JSON numbers. This
breaks CDP clients with strict deserialization (e.g., Rust serde).
Fixes#1822
Puppeteer's page.setCookie() internally calls Network.deleteCookies twice
before setting a cookie. The second call includes a partitionKey field for
CHIPS (partitioned cookies), which caused Lightpanda to return NotImplemented.
Since Lightpanda doesn't support partitioned cookies, we now silently ignore
the partitionKey parameter and proceed with the cookie operation based on
name/domain/path matching.
This change affects:
- Network.deleteCookies: no longer rejects requests with partitionKey
- Network.setCookie (via setCdpCookie): no longer rejects cookies with partitionKey
Fixes#1818
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The reallocation_count counter was being incremented regardless of whether
the resize/remap operations succeeded. This led to inaccurate memory
allocation statistics.
- resize: Only increment when rawResize returns true (success)
- remap: Only increment when rawRemap returns non-null (success)
This fixes the TODO comments that were present in the code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>