Commit Graph

26 Commits

Author SHA1 Message Date
Muki Kiboigo
46d0b34c54 add RequestParams and SyncRequest 2026-04-28 07:01:39 -07: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
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
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
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
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
Navid EMAD
ea6b228f9d page: replay POST method/body/header on Page.reload
doReload built a NavigateOpts with only url + kind=.reload; method/body/header
defaulted to GET/null/null, so any prior POST navigation regressed to a GET
on reload. The HTML reload navigation re-fetches the document that produced
the current entry, and Chrome replays the same HTTP request that loaded the
page (including method, body, and Content-Type) — Lightpanda dropped all
three.

Retain the prior request body and content-type header in Frame.NavigatedOpts
(duped into the frame arena), and have doReload capture them into the CDP
command's arena before replacePage() frees the old frame. The reload's
frame.navigate call carries the replayed method/body/header so the request
the page was loaded with is the request that runs again.

Closes #2258
2026-04-27 06:28:58 +02:00
Navid EMAD
746921b86d Fix SubmitEvent.submitter for requestSubmit() with no argument
When Form.requestSubmit() is called without a submitter argument, the
HTML spec defaults the internal submitter variable to the form element
itself (step 2). The subsequent "submit a form element" algorithm then
collapses submitter === form back to submitterButton = null before
constructing the SubmitEvent. We were skipping that collapse and using
the form as the SubmitEvent's submitter, which diverges from Chrome /
Firefox / WPT.

Frame.submitForm now coerces submitter_ to null when it equals the
form element, mirroring the spec algorithm. The form.html fixture
that previously asserted the wrong behavior is updated to assert
.submitter === null per spec.

https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-submit

Closes #2252
2026-04-27 03:44:33 +02:00
Karl Seguin
a2f21ff463 Fix same-url navigate
Same-URL navigate should always cause a reload. The code that currently prevents
a navigate on fragment change is too loose and treats identical URLs as being
a fragment change..but for it to be considered a fragment change, the fragment
actually has to change.

This improves some WPT compat where tests do:

<iframe></iframe>  <--- loads "about:blank"

<script>
document.querySelector('iframe').src = "about:blank"; <--- should load it again
</script>
2026-04-27 09:27:30 +08:00
Karl Seguin
e1e9a0d78c Merge pull request #2244 from navidemad/fix/get-element-by-id-from-node-recovery
Fix Frame.getElementByIdFromNode to recover from removed_ids
2026-04-27 08:46:05 +08:00
Karl Seguin
b3257754e0 Merge pull request #2245 from lightpanda-io/navigate_load_arena
Use an arena from the ArenaPool for the main page
2026-04-26 08:25:55 +08:00
Karl Seguin
fc636d4e36 Merge pull request #2247 from lightpanda-io/small_fixes
Various small fixes
2026-04-26 08:25:41 +08:00
Karl Seguin
8509b112b8 Various small fixes
Extracted from https://github.com/lightpanda-io/browser/pull/2242
2026-04-25 13:22:41 +08:00
Karl Seguin
d02674f365 Use an arena from the ArenaPool for the main page
Was previously using page.arena, but there's no reason to hold this beyond the
point where the page is parsed, and the page can live for quite some time after
the initial load.
2026-04-25 10:49:27 +08:00
Karl Seguin
25cec9bd82 Merge pull request #2233 from lightpanda-io/custom_elements
Custom elements
2026-04-25 10:12:09 +08:00
Navid EMAD
3761d2a32f Fix querySelector('#id') vs getElementById('id') disagreement
Frame.getElementByIdFromNode is the selector engine's fast path for
`#id` queries. It only consulted `_elements_by_id`, while
Document.getElementById and ShadowRoot.getElementById both fall back
to `_removed_ids` + TreeWalker to recover a surviving duplicate after
the first has been removed.

The two APIs could disagree after DOM manipulation that removes a
duplicate-ID element (e.g. Turbo Drive's PageRenderer.replaceBody
doing innerHTML + replaceWith):

  document.querySelector('#page-title')  => null
  document.getElementById('page-title')  => <h1>

Dispatch to the existing canonical getElementById on the scope
(ShadowRoot or Document) instead of keeping a third, map-only copy
of the lookup. The two APIs now agree by construction.
2026-04-25 04:04:14 +02:00
Karl Seguin
757c70b6db Add adoptedCallback for CustomElements
Fired when elements are moved from one document to another. I don't think this
is really used much, but it helps pass a number of WPT cases.

This required tweaking insertAdjacentHTML as it was creating a full document
and trigger spurious callbacks in the new code. A DocumentFragment is now used
instead.
2026-04-24 17:26:39 +08:00
Karl Seguin
2c4179f1ad Support direct instantiation of custom element
Support `new MyElement()` syntax to create custom element. The implementation
for this is pretty straightforward, but it depends on:
https://github.com/lightpanda-io/zig-v8-fork/pull/173.

Also modify the `attributeChangedCallback` and pass a missing parameter: the
namespace. Currently, we pass `null`, but this is less likely to cause issues
than not passing anything.
2026-04-24 16:48:24 +08:00
Karl Seguin
b15a40adc4 Merge pull request #2224 from lightpanda-io/console_own_properties
Define v8 functions directly on console instance.
2026-04-24 16:34:46 +08:00
Karl Seguin
f450bddcc3 Define v8 functions directly on console instance.
functions should be defined directly on the window.console (and window.CSS)
instances. This is necessary for being able to iterate their "own" properties.
businessinsider.com has code that proxies console via such an iterator (through
Object.entries(console)). This commit allows types to declare that they own
their properties (as opposed to the prototype).

Also fixed HTMLDocument.location. This was using Document._location, but that
field wasn't always set. The new code removes the _location field and changes
the getter to get window._location. (Also an issue on businessinsider.com)
2026-04-23 20:16:28 +08:00
Karl Seguin
859a41ab4e Adds navigator.userAgentData
This API isn't supported by FireFox (yet), so it isn't a huge priority, but I
did notice that its used across many Google properties. It uses the same value
as Sec-Ch-Ua (https://github.com/lightpanda-io/browser/pull/2100) to provide
consistent data.

Smaller changes:
1 - Allow `OffscreenCanvas` to be used with Worker (noticed this error too)
2 - Don't like JS execution errors at "error" level for "load" and
    "DOMContentLoaded". "error" should be reserved for things we can fix and
    should never be triggered from invalid a bug in JavaScript.
2026-04-23 16:24:42 +08:00
Karl Seguin
550fb58f3f Introduce Page (container)
Follow up to https://github.com/lightpanda-io/browser/pull/2200

This change is actually pretty mundane, but a bunch of files that used to
take a *Session (e.g. every WebAPI releaseRef and deinit) now take a *Page.

This aims to separate the 2 lifetimes currently managed by Session by moving
the "Page" lifetime to a dedicated container: Page. Ultimately, the goal is to
remove the 1-page-per-session limit of the current design. Not to explicitly
support multiple pages per session (though, that's more possible now), but
in order to better emulate Chrome where, during a navigation event, the old and
new page both exist.
2026-04-23 15:48:13 +08:00
Karl Seguin
4ff55a8cff More Worker APIs
Enable URL, AbortController and AbortSignal for Workers. Had a large impact
because Event.initTrusted was changed from taking a *Frame to taking a *Session.

Also, make WGS and Frame have a matching `dispatch` method so that it's easier
to dispatch events against an executor (inline else =>). Similarly, added
dupeString directly to executor to make migrations easier.
2026-04-22 12:47:44 +08:00
Karl Seguin
2275416505 Page -> Frame
This is to pave the way for introducing a new "Page" container, which will take
over the page lifecycle currently burdening Session. The ultimate goal of that
is to allow the Session to have multiple pages (mostly for better transitions
between pages), which is hard to do now since the Session has so much state.

This rename was aggressive, e.g. currentPage() -> currentFrame() so that, when
the new Page container is added, you won't see "currentPage()" and wonder:

  "Does 'currentPage' mean the new Page container, or the Frame (which
  used to be called Page)".
2026-04-22 08:42:18 +08:00