CSS :active state retained after long press (touch-screen)

#358393574 Filed 2024-08-08 P3 / S3 Blink > Input

Summary

On a touch screen, long-press (~1s) a button and lift the finger. The :active pseudo-class styles are retained after pointerup / touchend instead of being released. Another tap or a mouse move is needed to clear them. With a mouse the state is correctly cleared on mouseup. Firefox clears the state on release for touch as well.

Root cause: a long-press ends with a GestureLongTap (not a GestureTap), and Blink treated its hit test as read-only, so the active state set by GestureShowPress was never released. On platforms that show the context menu on release (Windows), the context-menu hit test additionally re-applied the active state.

Test (touch screen required)

Steps

1. Long-press (~1 second) button 1 below, then lift your finger.

2. Watch the button: the red :active styling must disappear when the finger lifts.

3. Repeat with button 2 (the reporter's virtual-keyboard scenario).

Both buttons call preventDefault() on contextmenu so the native menu does not cover the page. The check below samples button.matches(':active') shortly after the touch ends.

Event log

waiting for input...

Expected vs actual

Expected

:active is released when the finger lifts (pointerup/touchend), regardless of how long the press was -- matching mouse behavior and Firefox.

Actual (unpatched Chrome)

:active stays applied after the long-press release; only a further tap or mouse move clears it.

Fix

Blink: treat GestureLongTap as a release in GestureManager::GetHitTypeForGestureType(), and make the context-menu hit tests read-only so the deferred show-menu-on-mouse-up path does not re-apply the state (third_party/blink/renderer/core/input/gesture_manager.cc, event_handler.cc).