Skip to content

Fix select, nested scroll, and Shadow DOM interactions - #17

Open
2bTwist wants to merge 1 commit into
SunkenInTime:mainfrom
2bTwist:codex/firefox-interaction-fixes
Open

Fix select, nested scroll, and Shadow DOM interactions#17
2bTwist wants to merge 1 commit into
SunkenInTime:mainfrom
2bTwist:codex/firefox-interaction-fixes

Conversation

@2bTwist

@2bTwist 2bTwist commented Aug 23, 2026

Copy link
Copy Markdown

Summary

  • make focused single-select controls respond to ArrowUp and ArrowDown while dispatching input and change events
  • route wheel and synthesized scroll gestures to the innermost scrollable ancestor before falling back to the page
  • recursively hit-test accessible open shadow roots for pointer actions
  • tighten the strict-CSP helper ownership and supported-selector behavior
  • add regressions for each corrected interaction path

Verification

  • npm test passes on the clean v1.4.10 branch
  • node tests/test-firefox-compat.mjs passes
  • git diff --check passes
  • the same compatibility-layer patch was exercised in Firefox 154.0 on macOS arm64 before the clean rebase, producing Firefox Bridge Live|Gamma|true, frame-bridge-live, shadow-clicked, and Inner scroll target reached

Scope

This PR makes no general browser-parity claim. It covers the tested enabled single-select, nested vertical scrolling, open Shadow DOM hit-testing, nested-frame, and direct strict-CSP DOM-domain paths. Closed Shadow DOM, broader selector semantics, and first-attempt latency remain outside this change.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d2fc86d5-f822-4cfe-a697-301868e504e0


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Greptile Summary

This change improves Firefox computer-use support for strict-CSP pages, open Shadow DOM pointer targeting, nested scrolling, and keyboard selection. Executed checks confirmed that pointer actions reach the innermost accessible open-shadow element, nested scroll gestures use an available container before the page, disabled select options are skipped, and a page-defined helper is replaced. Two input regressions remain in extension/firefox-compat.js: canceled wheel events still scroll, and Arrow navigation removes values from multi-select controls.

Confidence Score: 5/5

Do not merge until the wheel-cancellation and multi-select selection regressions in extension/firefox-compat.js are corrected.

Every investigated behavior was exercised through the Firefox compatibility bridge with captured before/after evidence. Both remaining failures were reproduced directly, and the shadow targeting, nested scrolling, disabled-option, and strict-CSP helper behaviors were verified by execution.

Files Needing Attention: extension/firefox-compat.js needs changes around wheel dispatch at lines 1013-1014 and select keyboard handling at lines 1097-1106. Add focused regressions in tests/test-firefox-compat.mjs for both cases.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced proofs for posted P1 findings and prepared validation context for each finding.
  • T-Rex exercised pointer press and release through two nested accessible open Shadow DOM roots, and confirmed the innermost button received the pointerdown and click while hosts did not receive the target event.
  • T-Rex exercised a synthesized scroll gesture over a nested scroll container and verified the nested container scrolled to scrollTop: 340 with no page-scroll call.
  • T-Rex exercised ArrowDown through the CDP keyboard path with disabled options, observed that the all-disabled-next case preserved the selection and emitted no events, and that a later enabled option was selected and notified.
  • T-Rex validated strict-CSP helper installation in the presence of a hostile page-defined helper, observed replacement on reinstall and successful resolution of CSS and label selectors.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (2)

  1. General comment

    P1 Canceled wheel events still trigger computer-use scrolling

    • Bug
      • At extension/firefox-compat.js:1013-1014, a wheel listener can successfully call preventDefault() on the cancelable synthetic event, but the target scroll container still moves by the requested delta because scrolling is performed immediately afterward. Observable result: scrollTop is 120 both without a canceling listener and with a listener that sets defaultPrevented: true.
    • Cause
      • The return value from target.dispatchEvent(...) and/or WheelEvent.defaultPrevented is discarded before the unconditional scrollAtPoint(x, y, deltaX, deltaY) call.
    • Fix
      • Store the dispatched wheel event (or use dispatchEvent's boolean return) and call scrollAtPoint only when the event was not canceled, e.g. const wheel = new WheelEvent(...); if (target.dispatchEvent(wheel)) scrollAtPoint(...).

    T-Rex Ran code and verified through T-Rex

  2. General comment

    P1 ArrowDown collapses a multiple select and reports a change

    • Bug
      • A <select multiple> initially selecting Alpha and Beta loses Alpha after a dispatched ArrowDown; only Beta remains selected. The path also emits one input and one change event for that destructive selection change.
    • Cause
      • extension/firefox-compat.js:1097-1106 applies the single-select selectedIndex navigation behavior to every HTMLSelectElement. At line 1104, assigning target.selectedIndex = nextIndex clears the other selected options in the multiple-select reproduction; lines 1105-1106 explicitly emit notification events.
    • Fix
      • Do not apply this synthetic single-selection ArrowUp/ArrowDown behavior to target.multiple selects, or implement multi-select keyboard semantics that preserve the selected-option set according to the intended modifier behavior.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "Fix Firefox interaction routing edge cas..." | Re-trigger Greptile

Comment on lines +1013 to +1014
target.dispatchEvent(new WheelEvent("wheel", { ...common, deltaX, deltaY, deltaMode: WheelEvent.DOM_DELTA_PIXEL }));
scrollAtPoint(x, y, deltaX, deltaY);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Canceled wheel events still scroll

The bridge dispatches a cancelable wheel event but ignores whether a page listener canceled it before unconditionally calling scrollAtPoint. A listener that calls preventDefault() observes defaultPrevented: true, yet the nested container still scrolls by the requested delta. Honor dispatchEvent()'s boolean return, or event.defaultPrevented, before applying the synthetic scroll.

T-Rex Ran code and verified through T-Rex

Comment on lines +1097 to +1106
} else if (target instanceof HTMLSelectElement && (key === "ArrowDown" || key === "ArrowUp")) {
const direction = key === "ArrowDown" ? 1 : -1;
let nextIndex = target.selectedIndex;
do {
nextIndex += direction;
} while (nextIndex >= 0 && nextIndex < target.options.length && target.options[nextIndex].disabled);
if (nextIndex >= 0 && nextIndex < target.options.length && nextIndex !== target.selectedIndex) {
target.selectedIndex = nextIndex;
target.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
target.dispatchEvent(new Event("change", { bubbles: true }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Arrow navigation destroys multi-select values

This single-select navigation path also runs for <select multiple>. Assigning selectedIndex clears the existing selection set: an exercised control initially selecting Alpha and Beta retained only Beta after one ArrowDown, then emitted input and change. Exclude target.multiple controls unless modifier-aware multi-select behavior is implemented.

T-Rex Ran code and verified through T-Rex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant