Skip to content

Fix Firefox selection crash in Monaco: handle shadow DOM + null hit result in _doHitTestWithCaretPositionFromPoint#313960

Open
aesraethr wants to merge 1 commit intomicrosoft:mainfrom
aesraethr:fix/firefox-caretpositionfrompoint-shadow-dom
Open

Fix Firefox selection crash in Monaco: handle shadow DOM + null hit result in _doHitTestWithCaretPositionFromPoint#313960
aesraethr wants to merge 1 commit intomicrosoft:mainfrom
aesraethr:fix/firefox-caretpositionfrompoint-shadow-dom

Conversation

@aesraethr
Copy link
Copy Markdown

@aesraethr aesraethr commented May 3, 2026

Refs

What

Two related defensive changes in _doHitTestWithCaretPositionFromPoint (src/vs/editor/browser/controller/mouseTarget.ts):

  1. Pass the host's shadow root to caretPositionFromPoint so Firefox can resolve hits inside a shadow tree (parallels the _doHitTestWithCaretRangeFromPoint sibling at line ~990, which already calls dom.getShadowRoot).
  2. Null-check the hit result before dereferencing hitResult.offsetNode — the API legitimately returns null for points that don't fall on an addressable text node (e.g. dragging a multi-line selection past the end of the editor).

Why

Firefox 131 changed document.caretPositionFromPoint so that, when the page uses shadow DOM, the third options argument must include {shadowRoots: [...]} to return the actual hit position. Without it, Firefox returns the shadow host (or null). This crashes the editor's selection logic with:

Uncaught Error: can't access property "offsetNode", i is null
  _doHitTestWithCaretPositionFromPoint@editor.api…
  doHitTest@editor.api…

The error is fired through Monaco's unexpectedErrorHandler, surfaces in the console, and pollutes any error-monitoring tool (Sentry, Datadog RUM) listening on window.onerror. The selection still works — it's a hit-test miss after the fact — but every multi-line drag triggers it.

The bug is not just a shadow-DOM concern: contributors have reported the same offsetNode is null crash on the Monaco playground in Firefox 145+ by simply selecting all text. That suggests Firefox's caretPositionFromPoint can return null even without shadow DOM in some drag scenarios, hence the explicit if (!hitResult) return new UnknownHitTestResult(); guard.

Reproduction

silverwind in microsoft/monaco-editor#4679 (Nov 2025):

text selection causes a JS error `can't access property "offsetNode", hitResult is null` to be thrown in Firefox (currently on v145), and this issue seems to be the root cause. I can reproduce on https://microsoft.github.io/monaco-editor/playground.html?source=v0.55.1#example-creating-the-editor-hello-world by just selecting all text in Monaco

erosman in microsoft/monaco-editor#4679 (Dec 2025):

Uncaught Error: can't access property "offsetNode", i is null — happens when selecting text with mouse in the editor and the mouse moves out of the `<div class="monaco-scrollable-element editor-scrollable vs">`

Fix attribution

The shadow-root passthrough was originally proposed in the issue thread by @nate-bow-db (Oct 2024). The community has been shipping it as a postinstall monkey-patch for over a year. This PR lands the same change in the source plus adds the explicit null-guard for the non-shadow-DOM crash path that surfaced more recently.

Risk / scope

  • Scoped to one private static method in mouseTarget.ts.
  • The shadow-root branch only fires when dom.getShadowRoot(ctx.viewDomNode) returns non-null — so existing non-shadow-DOM consumers (the standard Monaco bundle) hit the same code path as before.
  • The null-guard returns new UnknownHitTestResult(), which is the same fallback the function already returns on its other failure path (line ~1080 today). No new return shape.
  • dom.getShadowRoot is already imported and used in the sibling _doHitTestWithCaretRangeFromPoint method, so no new import is needed.

Copilot AI review requested due to automatic review settings May 3, 2026 15:57
@aesraethr
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens Monaco editor mouse hit-testing for Firefox in mouseTarget.ts, specifically the Gecko-specific caretPositionFromPoint path used during selection. It aligns the Firefox code path with the existing shadow-DOM-aware caret-range logic and prevents a null dereference when Firefox cannot resolve a caret position.

Changes:

  • Pass the containing shadow root to document.caretPositionFromPoint(...) when the editor is hosted inside shadow DOM.
  • Return UnknownHitTestResult when caretPositionFromPoint(...) returns null instead of dereferencing offsetNode.
  • Add inline comments documenting the Firefox/shadow-DOM behavior motivating the defensive handling.

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.

4 participants