Commit Graph

63 Commits

Author SHA1 Message Date
Adrià Arrufat
70cc8ff5f9 Merge branch 'main' into agent 2026-06-08 17:01:18 +02:00
Matt Van Horn
e7cce250d3 fix: map password inputs to textbox accessibility role 2026-06-07 23:45:20 -07:00
Adrià Arrufat
a3559a4f60 Merge branch 'main' into agent 2026-05-26 19:48:18 +02:00
Karl Seguin
8472d21c59 Accessibility: use content for name for specific role
For typically name-less elements, check the "role" attribute to see if we should
fallback to the content.

Improves a handful of WPT tests, e.g.: /accname/name/comp_text_node.html

(It seems impossible to get 100% on these tests without knowing what is and
isn't a block-level element which would require more knowledge in the
StyleManager).
2026-05-25 12:46:20 +08:00
Adrià Arrufat
525c3e467c input: centralize password value redaction
Introduces `Input.getRedactedValue` to mask password values in
LLM-facing dumps (semantic tree, forms, AXNode) instead of
exposing raw values or using ad-hoc checks.
2026-05-24 08:36:23 +02:00
Navid EMAD
814ca8ab3f accessibility: unify query+tree writers, route objectId via dom.getNode
Fold QueryWriter into Writer behind an Opts.filter. Tree mode is unchanged
(filter=null); query mode walks the full subtree (including AX-ignored
nodes per the queryAXTree spec) and emits the flat-match shape. Shared
resolveRole helper handles label-promotion for both paths so the two
can't drift.

Drop the "objectId not yet supported" carve-out: queryAXTree now reuses
dom.getNode, which already resolves nodeId/backendNodeId/objectId.
2026-05-19 20:43:54 +02:00
Navid EMAD
940e9e9240 accessibility: promote hidden-input labels in queryAXTree
Match Writer.writeNode (line 488-499): when a <label> targets a CSS-hidden
checkbox/radio (toggle-switch / CSS-only radio pattern), emit the label
with the input's role so clients searching by role land on the label's
clickable backendDOMNodeId rather than the hidden input.

Without this, queryAXTree(role="checkbox", name=...) on a toggle-switch
returns the display:none input — a non-actionable target.
2026-05-15 22:18:55 +02:00
Navid EMAD
5f2d897f16 accessibility: implement queryAXTree CDP method
Adds Accessibility.queryAXTree for finding AX nodes by role and/or
accessible name without serializing the full tree. Motivated by
agentic / MCP automation workloads where getFullAXTree round-trips
multi-MB JSON on complex pages.

QueryWriter walks the DOM subtree rooted at the requested node,
reuses the existing role + name resolution + ignore logic from
AXNode, and emits a flat array of matches. Reuses the same
VisibilityCache + LabelByForIndex + temp arena pattern as
axnodeWriter, so no extra retained state.

MVP limitations, each a small follow-up PR:
- objectId param returns a specific not-yet-supported error
- matches emit empty properties/childIds and no parentId
- frameId not parsed; queries the current frame
2026-05-15 21:38:17 +02: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
Pierre Tachoire
f63b8ae004 cdp: AXNodeId is a string per spec
AXNodeId is defined as string in CDP spec.
This this a difference with DOM.NodeId which is an int.

https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/#type-AXNodeId
https://chromedevtools.github.io/devtools-protocol/tot/DOM/#type-NodeId
2026-04-24 11:24:16 +02: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
Adrià Arrufat
e9c93a49f0 cdp: promote <label> to checkbox/radio for CSS-hidden inputs
Chromium's accessibility tree prunes elements hidden via `display:none`,
`visibility:hidden`, the `hidden` attribute, `inert`, or `aria-hidden="true"`
and we match this behavior. It's correct for most elements, but it breaks
a common form pattern: CSS-only toggle switches and custom radio groups
hide the real `<input>` and style the `<label>` as the interactive surface.

Before, an agent walking the AX tree for a toggle switch saw:

    {role: "none", name: "Enable feature", ignored: false}

a generic element with no indication it's interactive and no exposure of
the underlying `checked` state. The input was pruned and the label had no
intrinsic role.

Now, when a `<label>` is associated (via `for=` or by wrapping) with a
hidden checkbox or radio input, the label is promoted to the input's role
and state:

    {role: "checkbox", name: "Enable feature",
     properties: [..., {checked: "true"}, {focusable: true}, ...]}

Native browsers already forward label clicks to the associated input, so
the label's `backendDOMNodeId` remains a valid click target — no action-
side changes needed.

Scope is intentionally narrow:

  - Only checkbox and radio inputs. Other labelable controls (textarea,
    select, etc.) don't have the "visible label as interactive surface"
    idiom.
  - Skipped when the label has an explicit `role=` attribute, to respect
    the page's declared semantics.
  - Skipped when the input is visible: normal AX tree flow already
    surfaces it, and promoting would double-count.

The fixture `src/browser/tests/cdp/ax_tree.html` covers:

  - `display:none` checkbox, checked, referenced by `<label for=id>`
  - `display:none` radio, checked, referenced by `<label for=id>`
  - `visibility:hidden` radio, unchecked, referenced by `<label for=id>`
  - Wrapping `<label>` containing a `display:none` checkbox
2026-04-22 16:04:29 +02: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
Karl Seguin
c159be503a Merge pull request #2194 from lightpanda-io/import_log
Change all @import("...../log.zig") to const log = lp.log;
2026-04-20 15:07:15 +08:00
Adrià Arrufat
983e592b43 cdp: use page arena pool for AXNode writer 2026-04-20 07:42:08 +02:00
Karl Seguin
2d20e57f80 Change all @import("...../log.zig") to const log = lp.log;
@import("lightpanda") where needed.

Would also like to do this for String, Page, Session and js which all stand out
as types that are use across the codebase.

I know that a few devs are doing this in new work and I haven't heard anyone
voice an objection.
2026-04-20 12:40:04 +08:00
Adrià Arrufat
3e49b3feae cdp: centralize scratch arena reset in AXNode 2026-04-17 15:22:45 +02:00
Adrià Arrufat
b42251d750 ax: route AXNode.Writer scratch allocations through a dedicated arena 2026-04-17 12:53:43 +02:00
Adrià Arrufat
36c1218486 ax: add lazy label index for name resolution 2026-04-17 12:20:10 +02:00
Adrià Arrufat
40b22f72a9 cdp: use in_aria_hidden instead of literal value 2026-04-17 11:54:32 +02:00
Adrià Arrufat
cee72cabb9 cdp: improve AX tree visibility and label resolution
Prunes hidden subtrees from the accessibility tree and implements
accessible name resolution via labels. Adds the `labels` property
to labellable HTML elements.
2026-04-17 08:33:13 +02:00
Karl Seguin
8eaeafe16c Fix a lot of typos.
I used https://github.com/crate-ci/typos, it worked well.

Also, make sure cdp-initiated KeyboardEvent is freed when no element is in focus
2026-04-10 06:51:10 +08:00
Karl Seguin
ab6c63b24b Add --wait-selector, --wait-script and --wait-script-file options to fetch
These new optional parameter run AFTER --wait-until, allowing the (imo) useful
combination of `--wait-until load --wait-script "report.complete === true"`.
However, if `--wait-until` IS NOT specified but `--wait-selector/script` IS,
then there is no default wait and it'll just check the selector/script. If
neither `--wait-selector` or `--wait-script/--wait-script-file` are specified
 then  `--wait-until` continues to default to `done`.

These waiters were added to the Runner, and the existing Action.waitForSelector
now uses the runner's version. Selector querying has been split into distinct
parse and query functions, so that we can parse once, and query on every tick.

We could potentially optimize --wait-script to compile the script once and call
it on each tick, but we'd have to detect page navigation to recompile the script
in the new context. Something I'd rather optimize separately.
2026-03-31 12:30:46 +08:00
Adrià Arrufat
567cd97312 webapi.Element: centralize disabled state logic 2026-03-24 13:13:53 +09:00
Adrià Arrufat
21fc6d1cf6 cdp: explain buffer size for int serialization 2026-03-16 09:41:28 +09:00
Matt Van Horn
5bc00045c7 fix: serialize AXValue integer as string per CDP spec
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
2026-03-14 14:09:49 -07:00
Adrià Arrufat
37735b1caa SemanticTree: use StaticStringMap for structural role check
Improves performance and readability of isStructuralRole. Also includes minor syntax cleanup in AXNode.
2026-03-11 16:37:24 +09:00
Adrià Arrufat
feccc9f5ce AXNode: remove unused mock JSON lifecycle methods
Simplifies TextCaptureWriter by removing unused methods, ensuring future changes to writeName will fail at build time if new methods are required.
2026-03-11 16:25:34 +09:00
Adrià Arrufat
ca931a11be AXNode: add spacing between concatenated text nodes
When calculating accessible names for elements without explicit labels, multiple descendant text nodes were previously concatenated directly together. This adds a space between distinct text node contents to prevent words from sticking together.
2026-03-11 10:45:07 +09:00
Adrià Arrufat
064e7b404b SemanticTree: unify interactivity detection logic 2026-03-10 19:02:55 +09:00
Adrià Arrufat
a318c6263d SemanticTree: improve visibility, AX roles and xpath generation
- Use `checkVisibility` for more accurate element visibility detection.
- Add support for color, date, file, and month AX roles.
- Optimize XPath generation by tracking sibling indices during the walk.
- Refine interactivity detection for form elements.
2026-03-10 09:23:06 +09:00
Adrià Arrufat
85ebbe8759 SemanticTree: improve accessibility tree and name calculation
- Add more structural roles (banner, navigation, main, list, etc.).
- Implement fallback for accessible names (SVG titles, image alt text).
- Skip children for leaf-like semantic nodes to reduce redundancy.
- Disable pruning in the default semantic tree view.
2026-03-09 21:04:47 +09:00
Adrià Arrufat
c3a53752e7 CDP: simplify AXNode name extraction logic 2026-03-09 15:34:59 +09:00
Adrià Arrufat
b8139a6e83 CDP/MCP: improve Stagehand compatibility for semantic tree 2026-03-08 15:48:44 +09:00
Adrià Arrufat
e0f0b9f210 SemanticTree: use AXRole enum for interactive role check 2026-03-06 16:26:08 +09:00
Adrià Arrufat
0f46277b1f CDP: implement LP.getSemanticTree for native semantic DOM extraction 2026-03-06 15:29:32 +09:00
Karl Seguin
a19a125aec Remove unused import
And a few unused functions
2026-01-29 19:44:10 +08:00
Karl Seguin
a3d2dd8366 Convert most Attribute related calls from []const u8 -> String 2026-01-26 07:52:27 +08:00
Karl Seguin
16ef487871 Make "Safe" variants of Attribute work on String 2026-01-26 07:52:27 +08:00
Pierre Tachoire
4325b80d64 axnode: small fixes 2026-01-16 17:30:43 +01:00
Pierre Tachoire
2074c0149f axnode: add aria-labelledby support 2026-01-16 09:01:39 +01:00
Pierre Tachoire
61ed97dd45 axnode: use writeString for content's name 2026-01-16 09:00:57 +01:00
Pierre Tachoire
a358c46b9f axnode: ignore script and style children 2026-01-16 08:28:16 +01:00
Pierre Tachoire
50c1e2472b axnode: encode json string into stripWhitespaces 2026-01-16 08:27:43 +01:00
Pierre Tachoire
d50e056114 axnode: ignore non-html tags 2026-01-15 16:42:40 +01:00
Pierre Tachoire
d7d956d966 axnode: fix invalid enum 2026-01-15 15:40:52 +01:00
Pierre Tachoire
bd3966bf8d axnode: add focus on webroot 2026-01-15 15:37:49 +01:00
Pierre Tachoire
74578ba274 axnode: implement list marker 2026-01-15 15:37:49 +01:00
Pierre Tachoire
cb89742d2f axnode: add li level 2026-01-15 15:37:48 +01:00
Pierre Tachoire
6d0f991c17 axnode: add hr properties 2026-01-15 15:37:48 +01:00