Skip to content

[Bug]: Firefox delivers other pages' mouse events to a page that captured the pointer #43631

Open

Description

Version

1.64.1

Steps to reproduce

import { chromium, firefox, webkit } from 'playwright';

const logEvents = `
  window.events = [];
  for (const type of ['pointerdown', 'pointerup', 'click']) {
    document.addEventListener(type, () => window.events.push(type));
  }
`;

for (const browserType of [chromium, firefox, webkit]) {
  const browser = await browserType.launch();
  const capturing = await browser.newPage();
  const clicked = await browser.newPage();

  await capturing.setContent(`
    <div id="handle" style="width: 200px; height: 200px; background: red"></div>
    <script>
      ${logEvents}
      handle.addEventListener('pointerdown', (event) => handle.setPointerCapture(event.pointerId));
    </script>
  `);
  await clicked.setContent(`
    <button style="width: 200px; height: 200px">click me</button>
    <script>${logEvents}</script>
  `);

  // press the mouse on the handle of the first page, which captures the pointer
  await capturing.mouse.move(100, 100);
  await capturing.mouse.down();

  // click the second page, in another context, 3 times
  for (let i = 0; i < 3; i++) {
    await clicked.mouse.click(100, 100);
  }

  await capturing.mouse.up();

  console.log(`${browserType.name()}:`);
  console.log(`  first page:  ${(await capturing.evaluate(() => window.events)).join(', ')}`);
  console.log(`  second page: ${(await clicked.evaluate(() => window.events)).join(', ')}`);

  await browser.close();
}

Expected behavior

Each page receives its own mouse events, as in Chromium and WebKit:

first page:  pointerdown, pointerup, click
second page: pointerdown, pointerup, click, pointerdown, pointerup, click, pointerdown, pointerup, click

Actual behavior

In Firefox, the second page's first click goes to the first page, which ends its pointer capture, and the second page never receives it:

first page:  pointerdown, pointerdown, pointerup, click, pointerup
second page: pointerdown, pointerup, click, pointerdown, pointerup, click

Additional context

Juggler dispatches every page's mouse events with identifier: win.windowUtils.DEFAULT_MOUSE_POINTER_ID (Page.dispatchMouseEvent in PageHandler.js). Firefox's parent process tracks pointer capture per pointer ID across all tabs (sPointerCaptureRemoteTargetTable in PointerEventHandler.cpp), and sends mouse events with a captured pointer ID to the capturing tab (EventStateManager::DispatchCrossProcessEvent). With one physical mouse that's correct, but Playwright's pages and contexts are meant to be independent.

We hit this in Vitest's browser mode, which runs test files in parallel pages of one browser, one context each. While a test drags a resize handle that captures the pointer, tests in other pages lose clicks, and the drag ends early when it receives their pointerups. We currently run those tests one at a time in a separate browser.

Using a distinct pointer ID per page, or per context, would keep their pointer captures apart.

Environment

OS: Windows 11 Pro 10.0.26300
Node.js: 26.12.1
playwright: 1.64.1
Firefox: 157.0

Activity

  1. dgozman commented on Oct 27, 2026

    @dgozman
    Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    browser-firefoxBlocked on upstream/fork firefox issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions