Fixes#98468
## Claude explanation of the fix
`useDrag` listened for `pointermove` and `pointerup`, but never
`pointercancel`.
When a user agent cancels a gesture — a browser or system gesture takes
over, a second finger arrives — it fires `pointercancel` and no
`pointerup`, implicitly releasing pointer capture as it does. So
`cancel()` never ran: the state machine stayed `{ state: 'drag' }` and
`cleanup.current` was never invoked, leaving the
`pointermove`/`pointerup` listeners on `window`.
The next `pointerup` anywhere on the page then hit those orphaned
listeners, reached `cancel()` with the state still `'drag'`, and called
`releasePointerCapture()` on a pointer that no longer existed — the
reported `NotFoundError`. Each cancelled drag also leaked another
listener pair.
This registers `pointercancel` alongside `pointerup` and removes it in
the same cleanup, so the machine unwinds when a gesture is cancelled;
and it releases pointer capture only when `hasPointerCapture()` says it
is still held, which is preferable to `try/catch` swallowing genuine
faults too.
Note `touch-action: none` (#97723) removed the common touch trigger, but
not the defect: on canary a forced `touchCancel` still throws.
https://github.com/user-attachments/assets/45a9524d-9ffa-40b6-b59a-5af9d947f8c7
## Fixed version
https://github.com/user-attachments/assets/b5ada68c-7442-42ef-a256-bacc512fbeca
## Browser checks
- [x] Chrome, as describe by the bug report
- [x] FF works fine pre and post fix (with and without the pointer
simulation)
- [x] iOS safari simulator, before the fix, I can't see an error, but,
the drag gets frozen, with this fix it works correctly
https://github.com/user-attachments/assets/bcfdc786-bada-4462-b530-299523fdca6d🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
### What?
- Restore `touch-action: none` on the draggable devtools indicator.
- Add integration coverage for the draggable computed touch action.
### Why?
PR #86816 made devtools content selectable, but also removed
`touch-action: none` with the selection styles. On touch screens, that
allows the browser to claim the gesture for viewport panning and
suppress the pointer event stream, causing the indicator to stop moving.
### How?
Keep the drag-lifecycle selection handling introduced by #86816 and
restore only `touch-action: none` before the gesture begins.
Fixes#97668
### Testing
- `pnpm build --filter next`
- Targeted Prettier and ESLint checks
- `NEXT_SKIP_ISOLATE=1 pnpm test-dev
test/development/app-dir/devtools-position/default-position.test.ts`
### What?
Migrate remaining direct `next-webdriver` test callers that have a
`NextInstance` to `next.browser()`, and expose the shared `Playwright`
browser type from `e2e-utils`.
### Why?
`NextInstance.browser` should be the supported browser-opening interface
for test fixtures, with `next-webdriver` kept as the private
implementation detail.
### How?
Updated affected development, e2e, and production tests to call
`next.browser()` directly, passing `baseUrl` where tests intentionally
target a manually spawned or proxied server. Shared helpers now receive
browser callbacks from the test context, and browser types import
`Playwright` from `e2e-utils` instead of deriving from `next.browser` or
importing from private paths.
<!-- NEXT_JS_LLM_PR -->