Commit Graph

6690 Commits

Author SHA1 Message Date
Muki Kiboigo
5dd15aa2cf use layers for Cache, Robots and WebBotAuth 2026-04-28 07:01:39 -07:00
Pierre Tachoire
86217dd78f Merge pull request #2295 from lightpanda-io/e2e-matrix
ci: create a matrix for e2e tests
2026-04-28 16:00:52 +02:00
Karl Seguin
8ac769bb10 Merge pull request #2298 from lightpanda-io/nikneym/fix-log-level-regression
`Config`: add a custom validator for `--log-level`
2026-04-28 17:49:29 +08:00
Halil Durak
c5b16cb18e Config: add a custom validator for --log-level 2026-04-28 12:37:16 +03:00
Karl Seguin
d46be2dc2b Merge pull request #2283 from navidemad/fix-a18-navigation-referer
browser: send Referer on cross-page navigation requests
2026-04-28 16:01:10 +08:00
Karl Seguin
70c7b7aa9a Merge pull request #2292 from navidemad/fix-b5-keyboard-keycode
events: implement KeyboardEvent.keyCode and charCode legacy attributes
2026-04-28 15:46:54 +08:00
Pierre Tachoire
ece2ec3753 ci: create a matrix for e2e tests 2026-04-28 09:31:54 +02:00
Karl Seguin
1df011bb2b Improve correctness of generateKey error
Previously, the only error generateKey would throw as SyntaxError. This commit
adds validation to the input so that the correct error can be returned. This
helps a couple thousands of WPT tests to pass, e.g.

/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.html

Goes from 210 / 775 to  775/775.

This does not add more crypto capabilities / algos, just validation of the
provided parameters.
2026-04-28 15:13:33 +08:00
Navid EMAD
73a007f88e events: gate KeyboardEvent.keyCode/charCode on isTrusted, add Enter charCode
Address review feedback on the legacy KeyboardEvent.keyCode and
KeyboardEvent.charCode getters:

* `getKeyCode` and `getCharCode` now early-return 0 when the event is
  not trusted, matching Chrome's behavior. Synthetic events created via
  `new KeyboardEvent(...)` from script have `isTrusted === false` and
  therefore expose 0 for both legacy attributes; only events dispatched
  by the user agent itself surface the legacy mapping.
* `getCharCode` now uses `_type_string.eql(comptime .wrap("keypress"))`
  to match the idiom used elsewhere in the project.
* The charCode mapping is moved into a pure `Key.charCode()` helper that
  mirrors `Key.keyCode()`, including a `.Enter => 13` arm so a trusted
  `keypress` for Enter exposes `\r` (U+000D) per spec.

The JS test fixture is consolidated into a single block asserting the
Chrome-correct behavior for synthetic events. The full per-key mapping
table is now exercised via two pure-function Zig unit tests on
`Key.keyCode()` and `Key.charCode()`.
2026-04-28 07:12:29 +02:00
Navid EMAD
88b38d240d Merge remote-tracking branch 'origin/main' into fix-b5-keyboard-keycode 2026-04-28 07:00:17 +02:00
Navid EMAD
0a4c2a2743 css: apply UA stylesheet display:none defaults for unrendered elements
`StyleManager.hasDisplayNone` honored only the `[hidden]` UA-stylesheet
rule; the rest of HTML Rendering §15.3.1 ("Hidden elements") was
unimplemented. As a result `getComputedStyle(headEl).display === "block"`
and `el.checkVisibility()` returned `true` for `<head>`, `<script>`,
`<style>`, `<link>`, `<meta>`, `<title>`, `<noscript>`, `<template>`,
`<param>`, `<source>`, `<track>`, `<area>`, `<datalist>`,
`<input type="hidden">`, and the non-`<summary>` direct children of a
closed `<details>`.

Add a `matchesUaDisplayNoneRule` helper consulted at the top of
`isElementHidden` so both `getComputedStyle().display` (via
`hasDisplayNone`) and `el.checkVisibility()` (via `isHidden`'s ancestor
walker) honor the same UA-stylesheet truth. The helper covers the
`[hidden]` attribute, a new `Tag.isHiddenByUaStylesheet()` predicate
mapping the spec's unrendered tag list, the
`input[type="hidden" i]` rule, and the `details:not([open]) > *:not(summary)`
parent-relationship rule. Inline `display` overrides still win per CSS
cascade.

Closes #2293
2026-04-28 06:31:14 +02:00
Navid EMAD
7feae5a958 events: implement KeyboardEvent.keyCode and charCode legacy attributes
Both getters were stubs returning 0. Per W3C UI Events § Annex C, keyCode
should report the legacy fixed virtual key code for the key being pressed
(e.g. 84 for 't'/'T', 16 for Shift, 13 for Enter), and charCode should
report the Unicode code point of the character produced on keypress events.

Adds a keyCode() method to the Key union that maps each named variant to
its spec-defined value and computes the uppercase-ASCII fallback for
.standard printable characters. getKeyCode delegates to it. getCharCode
checks the event type and returns the first byte of _key.standard for
keypress, 0 elsewhere.

For shift-modified symbol keys (e.g. shift+1='!'), keyCode falls back to
the modified char's ASCII rather than the unmodified key's value, since
KeyboardEvent doesn't currently store the unmodified key. Spec-strict
behavior would need plumbing unmodifiedText through KeyboardEventOptions
— left as a follow-up.

Closes #2291
2026-04-28 05:25:47 +02:00
Karl Seguin
827626db67 Merge pull request #2287 from sunguru98/chore/tests-htmlcollection-empty-name
browser: fix HTMLCollection empty name WPT
2026-04-28 11:16:40 +08:00
Karl Seguin
2bbf23b3bd Merge pull request #2280 from navidemad/fix-2277-input-range-step-matching
forms: round <input type=range> value to nearest step on the step ladder
2026-04-28 10:55:48 +08:00
Navid EMAD
7f865551ca page: implement Page.getNavigationHistory and Page.navigateToHistoryEntry
Both CDP methods returned -31998 UnknownMethod even though the underlying
browsing-context history is fully tracked in webapi/navigation/Navigation.zig.
Add the two methods to the Page dispatch enum + switch and wire them to the
existing Navigation._entries / navigateInner traverse path.

getNavigationHistory walks _entries and reports {currentIndex, entries:
[{id, url, userTypedURL, title, transitionType}]}. navigateToHistoryEntry
looks up the entry by id and calls frame.navigate(url, .{ .reason = .history,
.kind = .{ .traverse = idx } }), reusing commitNavigation's traverse arm
which only updates _index without pushing a new entry. URL is duplicated
into the new frame's arena because replacePage releases the prior arena.

Closes #2288
2026-04-28 04:48:29 +02:00
Sundeep Charan Ramkumar
325aebb231 browser: fix HTMLCollection empty name WPT 2026-04-28 08:07:57 +05:30
Navid EMAD
577a405f61 forms: address review feedback on range step matching
- Drop redundant `step_attr.len == 3` guard in `snapToStep`. Zig's
  `std.ascii.eqlIgnoreCase` already short-circuits on length mismatch
  (`ascii.zig:329`), so the outer length check is wasted work.

- Implement the `value` content attribute fallback for step base.
  Per https://html.spec.whatwg.org/multipage/input.html#concept-input-min
  the chain is `min` content attr -> `value` content attr -> 0. The
  fallback was previously skipped (step base went straight from `min`
  to 0), which meant `<input type=range value="3.5" max=10>` then
  `el.value = '5.3'` snapped to `5` instead of the spec-required `5.5`.
  Threads `value_attr` through `sanitizeRange` so the fallback engages
  whenever `min` is absent or unparseable but a parseable `value`
  content attr exists.

Three new test cases cover the fallback path (s10), the unparseable
`value` attr that falls through to 0 (s11), and `min` taking precedence
over `value` attr (s12).
2026-04-28 04:19:06 +02:00
Navid EMAD
b08f9b4abd Merge remote-tracking branch 'origin/main' into fix-2277-input-range-step-matching 2026-04-28 04:08:58 +02:00
Navid EMAD
60ce8540bf forms: implement HTML constraint validation API
Adds the ValidityState interface plus validity / validationMessage /
willValidate / checkValidity / reportValidity / setCustomValidity on
HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement,
HTMLButtonElement, and HTMLFormElement (form-level checkValidity /
reportValidity iterate the form's listed elements). checkValidity
fires a cancelable `invalid` event on each element whose
ValidityState.valid is false, matching the HTML Living Standard
constraint validation algorithm.

Validity flags implemented per element type follow the spec's
per-element constraint set: Input covers valueMissing / typeMismatch
(email + url) / tooLong / tooShort / rangeUnderflow / rangeOverflow /
customError; Select covers valueMissing / customError; TextArea covers
valueMissing / tooLong / tooShort / customError; Button covers
customError (only when type=submit). patternMismatch and stepMismatch
are placeholders that return false (pattern needs JS RegExp from Zig;
step matching for <input type=range> is being landed in PR #2280);
badInput is always false because headless never receives raw user
keystrokes.

Closes #2284
2026-04-28 04:03:10 +02:00
Karl Seguin
716b6f338b Merge pull request #2279 from navidemad/fix-a20-form-attrs-on-submitter
forms: honor formaction / formmethod / formenctype on submit button
2026-04-28 09:49:48 +08:00
Navid EMAD
7237c377d3 browser: send Referer on cross-page navigation requests
Anchor click, form submit, and `location.href = ...` assignments queue a
navigation through `Frame.scheduleNavigation`, which then tears down the
originating page and rebuilds the frame in `Session.processRootQueuedNavigation`
before `Frame.navigate` issues the HTTP request. The originator's URL was
discarded with the old arena, so the request went out without a Referer
header — even though the HTML "navigate" algorithm and Fetch §4.5 require
one. `Frame.headersForRequest` (#1449) handled subresource fetches but was
never called from the navigation path.

Capture the originating frame's URL into a new `referer` field on
`NavigateOpts` at scheduling time, dup'd into the `QueuedNavigation` arena
so it survives the page tear-down. `Frame.navigate` adds it as a
`Referer:` header alongside the existing per-request headers. Iframe
initial navigation (`Frame.zig:1282`) also sets `referer = parent.url`
since the parent frame outlives that direct `navigate` call. CDP
`Page.navigate` (`.reason = .address_bar`) and `Page.reload` continue to
omit Referer — matches Chrome.

Closes #2281
2026-04-28 03:21:12 +02:00
Karl Seguin
321601b3d4 Merge pull request #2273 from lightpanda-io/constructor_arity
Add constructor arity
2026-04-28 09:12:17 +08:00
Karl Seguin
2f6ce92db2 update v8 dep 2026-04-28 08:56:22 +08:00
Karl Seguin
f3ab1dcf78 Add constructor arity
Depends on https://github.com/lightpanda-io/zig-v8-fork/pull/174

Builds ontop of https://github.com/lightpanda-io/browser/pull/2272 largely
because it's driven by the same uievents WPT tests.

https://github.com/lightpanda-io/browser/pull/1498 added the length to
functions, and this does it for constructors.
2026-04-28 08:54:48 +08:00
Navid EMAD
1149a19411 forms: round <input type=range> value to nearest step on the step ladder
Per WHATWG HTML "Suffering from a step mismatch", value sanitization for
<input type=range> rounds the value to the nearest valid `step base + step * n`
after the existing min/max clamp. Ties round toward positive infinity; if the
rounded-up neighbor exceeds max, the rounded-down neighbor is used instead.
`step` defaults to 1; `step="any"` (case-insensitive) disables step matching.

When the post-snap value matches the parsed input value exactly, the original
string is returned unchanged so assignments like `el.value = "1.0"` round-trip
without canonicalization.

Closes #2277
2026-04-28 02:23:40 +02:00
Karl Seguin
ae8013f967 Merge pull request #2275 from lightpanda-io/nikneym/cli-variants
`cli`: introduce `variants` + fix `--wait-script-file` regression
2026-04-28 07:32:35 +08:00
Karl Seguin
b0d99a3a1b Merge pull request #2264 from navidemad/fix-a7-formdata-empty-select
forms: skip FormData entry for <select> with no selectedness candidate
2026-04-28 07:30:17 +08:00
Karl Seguin
0aa79e95fd Merge pull request #2267 from navidemad/fix-a17-input-range-clamp
forms: clamp <input type=range> value to min/max
2026-04-28 07:27:31 +08:00
Karl Seguin
407b4ae91f Merge pull request #2269 from navidemad/fix-b7-attribute-value-escape-decode
selector: decode CSS escape sequences inside quoted attribute values
2026-04-28 07:27:16 +08:00
Karl Seguin
42f9abcdbf Merge pull request #2259 from navidemad/fix-a6-page-reload-replay-post
page: replay POST method/body/header on Page.reload
2026-04-28 07:16:50 +08:00
Karl Seguin
98b3b79e25 Merge pull request #2276 from lightpanda-io/fix-headers-init-leak
http: free curl header list on error
2026-04-28 07:14:14 +08:00
Navid EMAD
46f1646cf0 forms: honor formaction / formmethod / formenctype on submit button
When a form is submitted via a click on a submit button (or
form.requestSubmit(button)), the HTML form-submission algorithm requires
the submitter's formaction / formmethod / formenctype attributes to
override the form's corresponding attributes when present. Frame.submitForm
was reading action / method / enctype only from the form element, so the
button-side overrides were silently ignored. The symmetrical lookup for
formtarget already exists upstream — this commit applies the same pattern
to the other three attributes.

Closes #2278
2026-04-28 00:12:22 +02:00
Navid EMAD
d4479c0383 ci: retrigger after pre-existing demo-scripts flake (lightpanda-io/demo#167) 2026-04-27 23:08:56 +02:00
Navid EMAD
1b9e8ad46c page: drop POST method/body on redirect so reload doesn't replay it
If a POST navigation gets redirected (302/303), the page that actually
loads is fetched with GET — but Frame._navigated_options still carries
the original POST method, body, and Content-Type. Page.reload would
then re-POST the form data to the redirect target, which is both
incorrect and dangerous (re-submission of credentials, charges, etc.).

Reset method/body/header on _navigated_options inside
frameHeaderDoneCallback whenever response.redirectCount() > 0. The full
spec-correct version would distinguish 307/308 (preserve method) from
301/302/303 (convert POST→GET), but resubmitting form data is the more
dangerous failure mode — conservative reset matches Chrome's practical
behavior on reload.

Also collapse the prev_body/prev_header extraction in doReload to a
single tuple-destructured blk: block (no behavior change).

Tests: new cdp.frame: reload after POST→redirect drops the POST drives
POST /redirect_to_echo → 302 → /echo_method, then Page.reload, asserts
the second request is GET. /redirect_to_echo route added to
testing.zig. The existing reload-replays-POST test still passes (no
redirect, POST is still replayed).
2026-04-27 22:47:55 +02:00
Navid EMAD
5b1452f162 Merge remote-tracking branch 'origin/main' into fix-a6-page-reload-replay-post
# Conflicts:
#	src/cdp/domains/page.zig
2026-04-27 22:39:54 +02:00
Navid EMAD
6e65487767 forms: extract Select.effectiveOption to centralize selectedness
Per review: collectForm walked the children once to look for a
non-disabled <option>, then Select.getValue walked them again to
emit the value. Extract the selectedness-candidate algorithm into
Select.effectiveOption() ?*Option so both call sites share it and
each runs a single iteration.

- Select.getValue is now a one-liner over effectiveOption.
- collectForm replaces the candidate-check + getValue pair with
  `select.effectiveOption() orelse continue; break :blk opt.getValue(frame);`.

Behavior is unchanged; the existing form_data.html fixtures
(selectWithoutOptions, selectMultipleWithoutOptions,
selectAllOptionsDisabled) still pass.
2026-04-27 22:25:55 +02:00
Navid EMAD
fecab22ef0 forms: address review feedback on range clamp
- Use std.fmt.allocPrint in formatFloat (per-review suggestion).
- Drop the `r3.value = '1' -> '1'` fractional-bounds assertion, which
  conflicted with WHATWG step matching (default step=1, base=min=0.5,
  nearest valid is '1.5'). Step matching remains out of scope here.
2026-04-27 22:10:00 +02:00
Navid EMAD
a5e5639a14 selector: own attributeValue result in-function, avoid double dupe
Per review feedback. attributeValue now guarantees an arena-owned
return: the quoted path already allocates into the arena via the
ArrayList, so we just dupe in the unquoted path and drop the redundant
arena.dupe at the call site. Pre-size the ArrayList to input.len since
escapes only shrink, skipping grow-from-zero reallocs.
2026-04-27 21:41:13 +02:00
Adrià Arrufat
fdadbaaad5 http: free curl header list on error 2026-04-27 17:32:01 +02:00
Karl Seguin
ef3305a713 Merge pull request #2271 from lightpanda-io/disable_webgl_canvas
return null on getContext('webgl') and getContext('experimental-webgl')
2026-04-27 22:00:05 +08:00
Karl Seguin
65f3f90cab Merge pull request #2270 from lightpanda-io/terminate_guard
Mutex for Isolate.Terminate
2026-04-27 21:49:22 +08:00
Karl Seguin
1e61c76009 Merge pull request #2251 from lightpanda-io/same_url_navigate
Fix same-url navigate
2026-04-27 21:49:08 +08:00
Karl Seguin
f515233b52 Merge pull request #2248 from lightpanda-io/blackhole_storage
Add and default to Blackhole storage
2026-04-27 21:48:54 +08:00
Karl Seguin
36f89f0071 Merge pull request #2272 from lightpanda-io/uievents
Improve WPT uievents/ tests
2026-04-27 21:48:36 +08:00
Halil Durak
968cf5f9e5 Config: log-filter-scopes -> --log-filter-scopes 2026-04-27 16:06:29 +03:00
Halil Durak
f4b220fb5f Config: fix --wait-script-file regression 2026-04-27 16:06:12 +03:00
Halil Durak
604210fd8b cli: introduce variants
Also refactors parsing by moving value parsing functionality to `parseValue` function.
2026-04-27 16:05:40 +03:00
Karl Seguin
fcb9b00e09 Merge pull request #2243 from lightpanda-io/exit_on_cli_error
Propagate CLI parsing errors
2026-04-27 19:39:30 +08:00
Karl Seguin
cb0ff4d07c Merge pull request #2257 from navidemad/fix-a15-location-pathname-search-setters
browser: trigger navigation when Location.pathname or .search is assigned
2026-04-27 18:31:09 +08:00
Karl Seguin
0bcb0e4e6a Merge pull request #2265 from navidemad/fix-a16-redirect-fragment-inherit
http: inherit request URL fragment across fragment-less redirect
2026-04-27 18:15:16 +08:00