microsoft / microsoft/vscode

Touch long-press context menus flash-close on Android Chromium: four interacting hide paths with no Android exemption (iOS only)

Open
#336,557 2 comments 0 reactions 1 assignee Claimed by @sbatten View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

## Environment

- VS Code / code-server **1.132.0** served over HTTPS, accessed from an Android tablet (Edge desktop-mode UA, Android Chrome/152). Chromium engine.
- **Firefox on the same tablet does NOT reproduce** (Gecko uses the textarea input path, no native EditContext, no system Selection ActionMode) — which we believe isolates the Chromium-specific pipelines below.
- Desktop browsers and headless Chrome/CDP also do not reproduce (no virtual keyboard, no system selection toolbar).

## Symptom

Long-pressing a file-tree item or editor region opens the context menu, which then closes within ~10–50 ms. With instrumented builds we captured all four hide paths firing, often cascading from a single long-press.

## Four hide paths (all verified with call-stack telemetry against 1.132.0)

### 1. Virtual keyboard dismissal → viewport resize → `ContextViewHandler.layout()` → hide

Opening the menu steals focus from the editor's native `EditContext`; the Android virtual keyboard collapses; the viewport resizes; the workbench relayout triggers `ContextViewHandler.layout()`:

`src/vs/base/browser/ui/contextview/contextview.ts` **L318** (1.132.0; now L329 on main):

```ts
if (this.delegate!.canRelayout === false && !(platform.isIOS && BrowserFeatures.pointerEvents)) {
this.hide();
return;
}
```

Context menu delegates set `canRelayout === false`, so any resize while the menu is open hides it. **The exemption exists only for iOS**; Android Chromium falls into the gap.

### 2. Native text-selection toolbar → window `blur` → hide

Long-press with an active selection makes Android Chromium show the system Copy/Share (ActionMode) toolbar, which blurs the page. `src/vs/platform/contextview/browser/contextMenuHandler.ts` **L100**:

```ts
menuDisposables.add(addDisposableListener(targetWindow, EventType.BLUR, () => this.contextViewService.hideContextView(true)));
```

### 3. Duplicate `showContextView` — the second show disposes the first menu

`List.onContextMenu` is built with `Event.any(fromKeyDown, fromKeyUp, fromMouse)` (`src/vs/base/browser/ui/list/listWidget.ts` **L1455–1483**), and `fromMouse` receives both the synthesized `contextmenu` event and Monaco's own gesture-based `-monaco-gesturecontextmenu` DOM event. One long-press can therefore reach `showContextView` twice; since `ContextViewHandler.show` is single-slot, the second call disposes the first menu (~7 ms lifetime observed).

Related known issues: #332619, #331838.

### 4. Post-open focus steal → menu action container `blur` → hide

After the long-press gesture, the editor's native-edit-context focus restoration (`qEe`/`Zue.focus` in 1.132 minified) runs ~1–40 ms after the menu opens, blurring the menu's action container; the ActionBar's `onDidBlur` hides the menu.

## Why we could not fix this from outside

The four paths cascade from a single gesture, and blocking them externally conflicts: intercepting blur breaks click-outside/keyboard cancel; intercepting resize breaks focus tracking and destroys the cursor state (we observed "menu alive but editor dead" states). We rolled back all 20 injected patch attempts and run unpatched 1.132.

## Suggested fix direction

The clean fix lives upstream, mirroring the existing iOS exemption for Android touch:

- In `contextview.ts` `layout()`: extend the `platform.isIOS` condition to Android with coarse pointer (`platform.isAndroid && BrowserFeatures.pointerEvents === 'coarse'`-style check) so a keyboard-driven resize does not hide the menu.
- In `contextMenuHandler.ts`: on Android touch, ignore transient window `blur` (or re-check menu hover/focus state) so the system selection toolbar does not close the menu.
- Debounce/dedupe the dual context-menu synthesis for touch in `List.onContextMenu` (related to #332619/#331838).

Happy to provide the full telemetry dataset (600+ instrumented reports with stack traces per hide path) if useful.

- VS Code version: 1.132.0 (code-server 4.132.0); paths confirmed still present on `main` (contextview.ts L329, contextMenuHandler.ts L100)
- OS: Android 15 tablet, Chrome/152 (Edge desktop-mode UA); control: Firefox on same device does not reproduce

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.