Commit Graph

6008 Commits

Author SHA1 Message Date
Pierre Tachoire
acdddb7ec8 keep the existing page active until the pending one is loaded
During a root navigation, we keep the existing page active until we get
the headers callback from the pending page. Then
Session.commitPendingPage makes the switch.

It delays the deinit of CPD execution context to handle JS execution in
the meantime.

Now session has an array of two pages, _active_idx points to the main
page.

Both active and pending pages share the same frame_id, it must remains
stable. So this PR adds a Request.protect_from_abort to avoid removing
the request form the pending page when deinit the previous active page.
2026-05-04 08:50:26 +02:00
Karl Seguin
0420802f8d Merge pull request #2349 from lightpanda-io/remove-arraylist-unmanaged
RobotsLayer: use ArrayList
2026-05-04 14:19:09 +08:00
Adrià Arrufat
eab9ae0243 RobotsLayer: use managed ArrayList 2026-05-04 08:01:22 +02:00
Karl Seguin
b8144d3ec4 Merge pull request #2346 from navidemad/fix/libidn2-macos-strchrnul
build: provide strchrnul shim for libidn2 on macOS
2026-05-04 11:30:42 +08:00
Karl Seguin
87b7ebfc00 Merge pull request #2340 from lightpanda-io/tick_efficiency
More aggressive popup cleanup, more efficient load tick
2026-05-04 11:29:24 +08:00
Karl Seguin
cf04873d7a Merge pull request #2334 from lightpanda-io/script_mode
Split Script
2026-05-04 11:28:25 +08:00
Karl Seguin
150c401a27 Merge pull request #2313 from lightpanda-io/callback_fixes
Fix more DOM assumptions that callback and document.write can break
2026-05-04 11:28:05 +08:00
Karl Seguin
501bfe65f0 Merge pull request #2333 from webcoyote/display-dynamic-port
Display actual port when binding --port 0
2026-05-04 11:15:58 +08:00
Karl Seguin
ab93cbb56c Merge pull request #2318 from lightpanda-io/atob_btoa
Fix atob/btoa
2026-05-04 08:29:11 +08:00
Karl Seguin
0da32a73b7 Merge pull request #2337 from lightpanda-io/about_blank_navigation_assertion
Fix failed navigation assertion
2026-05-04 08:27:28 +08:00
Karl Seguin
9bea9c9dc7 Merge pull request #2347 from lightpanda-io/fix/summary-click-toggles-details
Fix/summary click toggles details
2026-05-04 08:26:57 +08:00
Karl Seguin
1b90ef58be Summary click opens details
Tweak on https://github.com/lightpanda-io/browser/pull/2342
2026-05-04 08:02:56 +08:00
Navid EMAD
d76b76221f build: colocate libidn2 strchrnul shim with strverscmp block
config.h already has a hand-added _LIBIDN2_LP_DECLS guarded block at
the end declaring extern strverscmp for the same gnulib-overlay reason.
Move the strchrnul prototype into that block (still #ifdef __APPLE__
since glibc Linux already declares it) so all project-added externs
live in one place, the autoconf-generated section stays untouched, and
the precedent for "how this codebase handles missing glibc extensions"
is one consistent pattern instead of two.

Refresh the build.zig and strchrnul.c comments to point at the
relocated declaration.

No functional change — verified across -target x86_64-macos.{13.0,
14.0,15.4} (lookup.c pattern + the three TUs that include both
<config.h> and <string.h>) plus x86_64-linux-gnu, all under
-Wall -Wextra -Werror.
2026-05-02 10:11:41 +02:00
Navid EMAD
7b5e18e23d build: relocate libidn2 strchrnul prototype into config.h
Move the macOS strchrnul declaration from a side-channel header injected
via -include into vendor/libidn2/config.h itself, gated on __APPLE__.
config.h is the canonical place where libidn2 documents its
HAVE_STRCHRNUL detection — co-locating the platform fallback prototype
there mirrors gnulib's own approach (which declares missing symbols in
its substituted <string.h>) and removes the per-platform build-flag
asymmetry plus the dedicated include path.

The Darwin-only strchrnul.c implementation is unchanged; build.zig now
just adds it on Darwin without touching the lib/ flags array.

Verified that on both `-target x86_64-macos.14.0` (no libc strchrnul)
and `-target x86_64-macos.15.4` (libc has it) a TU mimicking lookup.c
(`#include "config.h"` then call strchrnul) compiles cleanly with no
implicit-declaration error and no redeclaration warning.
2026-05-02 10:00:42 +02:00
Navid EMAD
40900a358c build: provide strchrnul shim for libidn2 on macOS
libidn2's lib/lookup.c calls strchrnul() unconditionally — a glibc
extension that macOS libc neither defines nor declares. Upstream's
portable build relies on gnulib substituting <string.h> with a
declaration and linking gl/strchrnul.c, but that overlay is not wired
up here. The result was an "implicit function declaration" hard error
on every macOS nightly build.

Add a Darwin-only shim under vendor/libidn2/darwin/ and inject its
prototype into the libidn2 lib/ sources via -include. The shim mirrors
strchrnul's semantics without falling through to rawmemchr (also
glibc-only) since libidn2 only ever searches for '.'.
2026-05-02 09:52:05 +02:00
Karl Seguin
e69bcfa4da Merge pull request #2345 from lightpanda-io/fix/cookie-samesite-json-case-guyua9
Fix/cookie samesite json case guyua9
2026-05-02 13:30:25 +08:00
Karl Seguin
3ec507773c change sameSite to string to deal with case-insensitive values 2026-05-02 12:10:57 +08:00
Karl Seguin
4704185980 Merge pull request #2341 from lightpanda-io/script_manager_encode
Encode script urls before making request
2026-05-02 09:39:17 +08:00
swaroski
55b5c134dc dom: toggle parent <details>.open on <summary> click
Per HTML §4.11.1.2, a <summary>'s activation behavior runs the
toggle-details-state algorithm on its parent <details>, but only when
the summary is the first summary child of that details.

Frame.handleClick previously had no arm for .generic, so summary clicks
fell into `else => {}` and details.open never changed. Add a .generic
arm gated on `_tag == .summary` that walks the parent's element
children to confirm first-summary-child, then flips details.open via
the existing Details.setOpen.

Out of scope (tracked separately): firing ToggleEvent on the parent
(no ToggleEvent type yet); activating when a descendant of <summary>
is clicked (same gap exists for <a>/<label>; needs a broader walk-up
refactor of handleClick).

Tests: src/browser/tests/element/html/summary_click.html covers
closed→open, open→closed, non-first summary no-op, and orphan summary
no-op.
2026-05-01 18:31:01 -04:00
Pierre Tachoire
47ea77f7b2 Merge pull request #2286 from navidemad/fix-b6-validity-api 2026-05-01 20:53:53 +02:00
Pierre Tachoire
76deb2552a fix Input.sameFormOwner regression 2026-05-01 18:40:49 +02:00
Karl Seguin
9fdd13a626 Encode script urls before making request
This comes from a WPT crash in /content-security-policy/blob/self-doesnt-match-blob.sub.html

The core issue is double-free on the request header list. This is a fundamental
issue about ambiguous ownership. On error, who's responsible for freeing the
request? The caller or the HttpClient. Answer: it depends when/where the error
happens. That just doesnt' work, and needs to be fixed. BUT....

There's also the issue that this specific test is failing because we aren't
properly encoding script URLs. This is a real and separate issue that also needs
to be fixed. That's what this commit does. In some ways, it's a bit superficial
because there's a real double-free issue, but this is a real issue to fix.
2026-05-01 13:00:12 +08:00
Karl Seguin
560b15c9b2 update v8 dep 2026-05-01 09:11:16 +08:00
Karl Seguin
2b95b1e743 More aggressive popup cleanup, more efficient load tick
Follow up to https://github.com/lightpanda-io/browser/pull/2335 which hooks
into the tick's GC hint to close any closed window (rather than having to wait
until page unload).

Also, use schedule task for dispatchLoad which removes an check in every tick.
2026-05-01 09:06:47 +08:00
Navid EMAD
604f812827 forms: address review feedback on constraint validation API
- Cache `ValidityState` per element so repeated `el.validity` access
  returns the same instance (also avoids allocating a struct per
  per-keystroke call).
- Use `Event.initTrusted` for the `invalid` event — browser-generated.
- Use `frame.dupeString(message)` (interns short strings) instead of
  `frame.arena.dupe(u8, message)` for `setCustomValidity` storage.
- Cascade `*const Input` / `*const Select` through `getValidationMessage`,
  `suffersValueMissing`, and Select.effectiveOption.
- Factor radio-group walking into a shared `radioGroupIterator`; add the
  same-form check to `radioGroupHasChecked` (was only in
  `uncheckRadioGroup`). The form comparison uses a frame-less helper so
  it can run from the const validation path.
- Narrow `numericRangeBreach` to `.number, .range` — date/time/month/
  week/datetime-local need type-specific conversion before comparison
  (was silently returning false). TODO comment left in place.
- Add `getMinLength`/`setMinLength` getters to Input and TextArea, plus
  `getMaxLength`/`setMaxLength` to TextArea (Input already had them).
  Used internally by `suffersTooLong`/`suffersTooShort` and exposed via
  IDL.
- TODO prefix on the file-input value-missing comment so the limitation
  is greppable.

Tests cover the new IDL surface (`minLength`/`maxLength` on Input and
TextArea), the validity-state caching guarantee
(`el.validity === el.validity`), and the trusted-event flag on the
`invalid` event for Input, Select, TextArea, and Button.
2026-04-30 22:35:44 +02:00
Karl Seguin
b254671fa1 Fix failed navigation assertion
When the root frame navigates to about:blank, we still need to create an
navigation entry, else navigation's expectation (via assertions) fail.
2026-04-30 22:54:22 +08:00
Pierre Tachoire
9a9e79ebc1 Merge pull request #2328 from lightpanda-io/ci-cdp-log
ci: save cdp logs by using cdpproxy
2026-04-30 14:55:35 +02:00
Karl Seguin
216cec5e42 Merge pull request #2335 from lightpanda-io/window_close
Defer window.close()
2026-04-30 19:58:21 +08:00
Pierre Tachoire
ba28324a9e ci: save cdp logs by using cdpproxy 2026-04-30 12:35:30 +02:00
Karl Seguin
f921869fb6 Defer window.close()
Cannot Frame.deinit in window.close() as that's happening inside JS runtime.
Instead, defer on Page.deinit. This is MUCH later than necessary, but I'd like
to address the timing separately. This commit, as-is, prevents real crashes.
2026-04-30 18:04:56 +08:00
Karl Seguin
fe77694437 Merge pull request #2310 from navidemad/fix-a22-iscontenteditable
dom: implement HTMLElement.isContentEditable IDL attribute
2026-04-30 16:09:56 +08:00
Karl Seguin
a591956dcc Split Script
This is a follow up to https://github.com/lightpanda-io/browser/pull/2290. That
PR spit ScriptManager into ScriptManager and ScriptManagerBase, with the
goal of letting a Worker have a ScriptManagerBase that works without needing
a frame.

But the Script object was still a large object meant to work with any type of
script invocation, e.g. it has a nullable element. This commit swaps out these
fields for a better-typed "extra" union field. So that Element only exists in
the extra.frame tag. In the end, it just makes it more explicit about what
fields are valid under specific conditions.
2026-04-30 15:30:37 +08:00
Navid EMAD
2af95af678 dom: return false from isContentEditable, log when spec says true
Per maintainer review (PR #2310), Lightpanda has no caret/keyboard editing
pipeline, so honoring the spec-correct value risks routing downstream CDP
clients (notably Puppeteer's dispatchKeyEvent path) into an input pipeline
that silently no-ops. Switch to always returning false and emit
log.info(.not_implemented, "IsContentEditable", .{}) when the spec walk
would have produced true, so the unsupported case surfaces in telemetry
rather than masquerading as a working state.

The HTML §7.7.5.2 walk is preserved (nearest ancestor with `contenteditable`
wins, "false" disables) but only used to gate the log emission. The fixture
is reduced to assert the always-false return across the same shape of
inputs, with a comment pointing back at the rationale.
2026-04-30 07:28:43 +02:00
Patrick Wyatt
47d96ab8ad Display actual port when binding --port 0
This change causes lightpanda to display the actual port number (instead of 0)
when binding a dynamic port (--port 0), which makes automating based on
scraping lightpanda output simple.
2026-04-29 21:44:41 -07:00
Karl Seguin
e42acc5335 Merge pull request #2322 from navidemad/fix-a27-prompt-default-text
cdp: fall back to dialog defaultText when LP.handleJavaScriptDialog promptText is null
2026-04-30 12:12:33 +08:00
Karl Seguin
2c52e9b34c Merge pull request #2324 from navidemad/fix-a28-label-click-activation
dom: run label activation behavior on click
2026-04-30 11:56:59 +08:00
Karl Seguin
dd4a46dc16 Merge pull request #2331 from sunguru98/bugfix/element-getelements-by-tagname
browser: change node filter from lower to tag_name
2026-04-30 11:56:29 +08:00
Karl Seguin
64fbd55746 Merge pull request #2327 from lightpanda-io/nikneym/httponly-cookie
`Cookie`: don't allow JS context to mutate HttpOnly cookies
2026-04-30 11:52:44 +08:00
Sundeep Charan Ramkumar
91c0edddad browser: change node filter from lower to tag_name 2026-04-30 08:28:18 +05:30
Karl Seguin
1f40c30901 Merge pull request #2290 from lightpanda-io/worker-script-manager-split
Split ScriptManager to support worker module imports
2026-04-30 10:27:54 +08:00
Karl Seguin
896adc14d0 Merge pull request #2299 from lightpanda-io/idn
Initial idn support
2026-04-30 09:30:38 +08:00
Karl Seguin
c2a1b6fd64 Split ScriptManager to support worker module imports
Extract module-loading plumbing into ScriptManagerBase so workers can
use it. Previously Context.script_manager was null for worker contexts,
which crashed on dynamic import() via the unwrap in dynamicModuleCallback.

ScriptManagerBase owns the HTTP fetch + V8 module resolution path
(preloadImport, waitForImport, getAsyncImport, resolveSpecifier, the
Script struct) and reaches per-owner fields through an Owner union.
ScriptManager wraps it for Frame and keeps the parser/DOM-bound surface
(addFromElement, parseImportmap, Frame-specific evaluate tail via
tail_hook). WorkerGlobalScope gets its own ScriptManagerBase directly.

This is similar to the work done in https://github.com/lightpanda-io/browser/pull/2093
which split EventManager into EventManager + EventManagerBase (for the same
reason).

add tests
2026-04-30 09:05:51 +08:00
Karl Seguin
39d9ac9b4c Merge pull request #2285 from lightpanda-io/form_submitter
Form Submitter should only override when it's a submit input
2026-04-30 08:47:20 +08:00
Karl Seguin
ce2f6d9bdb Form Submitter should only override when it's a submit input 2026-04-30 08:19:21 +08:00
Karl Seguin
20e1aeaacb Merge pull request #2282 from lightpanda-io/input_file
Input file foundation
2026-04-30 08:09:23 +08:00
Karl Seguin
3f11e6148e Merge pull request #2296 from lightpanda-io/crypto_generateKey_errors
Improve correctness of generateKey error
2026-04-30 08:08:52 +08:00
Halil Durak
0c3d5573f0 Cookie: don't allow JS context to mutate HttpOnly cookies
Changes function signature for `Jar.add` in order to do this, not sure if we should have separate functions for that or comptime-if sufficient.
2026-04-29 17:40:48 +03:00
Navid EMAD
3b3e4e4129 Merge remote-tracking branch 'origin/main' into fix-b6-validity-api 2026-04-29 15:49:37 +02:00
Navid EMAD
31e0e7c81a dom: run label activation behavior on click
`Frame.handleClick` had no `.label` arm, so clicking an `HTMLLabelElement`
fired the click event but never dispatched the synthetic click activation
on the labeled control. Per HTML §4.10.4 "The label element", a label's
activation behavior is to run the synthetic click activation steps on the
labeled control; without it `cb.checked` stays unchanged when the click
target is a `<label>`.

Resolve the labeled control via the existing `Label.getControl(frame)`
(handles both `for=` and the wrapping-descendant case) and call `.click()`
on it, mirroring Chrome's `HTMLLabelElement::DefaultEventHandler`.

Closes #2323
2026-04-29 15:24:42 +02:00
Navid EMAD
71af170658 cdp: fall back to dialog defaultText when LP.handleJavaScriptDialog promptText is null
When a CDP client pre-arms `LP.handleJavaScriptDialog {accept: true}` with
no `promptText` and the page subsequently calls `window.prompt(message,
defaultText)`, Lightpanda discarded the dialog's `defaultText` argument
and returned `""`. Chrome's behavior is to surface `defaultText` as the
prompt's return value — that's the natural "user accepted without typing
anything" outcome per the HTML simple-dialog algorithm.

The fix is one line in `Window.zig`'s `prompt` JS binding: keep the
second argument named (`default_text` instead of `_`) and use it as the
fallback. Pre-armed `promptText` still wins when the client supplies it;
`accept = false` still returns null regardless. When neither
`promptText` nor `defaultText` is provided, the binding still returns
`""` per the CDP spec.

Adds four new assertions to the existing `cdp.lp` integration test
covering the matrix: defaultText fallback (no promptText), promptText
overrides defaultText, accept=false ignores defaultText, and the
existing no-defaultText case continues to return `""`.
2026-04-29 15:01:18 +02:00