Skip to content

LibWeb: Skip user-select:none in selection highlight and copy#9709

Merged
AtkinsSJ merged 2 commits into
LadybirdBrowser:masterfrom
sideshowbarker:sideshowbarker/fix-9695-user-select-none-select-all
May 28, 2026
Merged

LibWeb: Skip user-select:none in selection highlight and copy#9709
AtkinsSJ merged 2 commits into
LadybirdBrowser:masterfrom
sideshowbarker:sideshowbarker/fix-9695-user-select-none-select-all

Conversation

@sideshowbarker

@sideshowbarker sideshowbarker commented May 28, 2026

Copy link
Copy Markdown
Member

Problem: Select All (Ctrl+A or the context menu) doesn’t honor user-select:none — breaking compat with Chrome and Firefox. Two visible symptoms:

  • The selection highlight is drawn across user-select:none content.
  • Ctrl+C after Select All copies that content to the clipboard.

Mouse double-click already skips user-select:none correctly; only the keyboard / context-menu path was broken.

Cause: Both Ctrl+A and right-click “Select All” flow through Selection::select_all_children(body), which sets a single Range from (body, 0) to (body, childCount) — including any user-select:none subtrees. Two downstream functions then fail to filter:

  • ViewportPaintable::recompute_selection_states walks the range and assigns SelectionState::Start, ::Full, or ::End to each layout node within. The walk filters out is_inert() nodes — but not user-select:none nodes.
  • Navigable::selected_text() walks the range via visible_text_in_range() and concatenates each text node’s data. Filters out nodes without a layout — but not user-select:none.

Fix: Add a user-select:none check at each walk point in both functions. The Selection range itself is left unchanged — Selection.toString() still returns the full text per spec — but:

  • The paint path leaves user-select:none nodes at SelectionState::None from the initial reset, so the highlight skips them.
  • The clipboard-extraction path excludes user-select:none subtrees.

This implements https://drafts.csswg.org/css-ui/#valdef-user-select-none (“The content of the element must be excluded from selection by [...] the selection methods of the Selection API and the like”) at the highlight and clipboard boundaries — matching Chrome and Firefox. Fixes #9695.

Problem: Select All (Ctrl+A or the context menu) followed by Ctrl+C
unexpectedly copies text from elements with user-select:none — breaking
compat with Chrome and Firefox, which both exclude user-select:none
content from the clipboard.

Cause: Navigable::selected_text() walks the selection range via
visible_text_in_range() and concatenates each text node’s data. The walk
filters out nodes without a layout, but not nodes whose used value of
user-select is ‘none’.

Fix: Add a user-select check at each visible_text_in_range() walk point.
The Selection range itself is left unchanged. Selection.toString() still
returns the full text per spec — but the clipboard-extraction path now
excludes user-select: none subtrees, per spec
Problem: Select All draws the selection highlight across
user-select:none content — breaking compat with Chrome and Firefox,
which leave such content unhighlighted.

Cause: ViewportPaintable::recompute_selection_states walks the Range and
assigns SelectionState::Start|::Full|::End to each layout node within.
The walk filters out is_inert() nodes — but not user-select: none nodes.

Fix: Extend the existing inert-only guards into a helper that also
rejects nodes whose used value of user-select is ‘none’. Such nodes stay
at SelectionState::None from the initial reset — so the selection
highlight skips them.

Fixes LadybirdBrowser#9695
@AtkinsSJ
AtkinsSJ enabled auto-merge (rebase) May 28, 2026 11:03
@AtkinsSJ
AtkinsSJ merged commit aec2439 into LadybirdBrowser:master May 28, 2026
12 of 13 checks passed
@sideshowbarker
sideshowbarker deleted the sideshowbarker/fix-9695-user-select-none-select-all branch May 28, 2026 13:04
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.

user-select: none is ignored by Select All (Ctrl+A and context menu)

2 participants