Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Puppeteer crashes on waitForDevicePrompt() #11072

Closed
2 tasks
outofambit opened this issue Oct 3, 2023 · 12 comments · Fixed by #11095 or #11159
Closed
2 tasks

[Bug]: Puppeteer crashes on waitForDevicePrompt() #11072

outofambit opened this issue Oct 3, 2023 · 12 comments · Fixed by #11095 or #11159
Assignees

Comments

@outofambit
Copy link

outofambit commented Oct 3, 2023

Minimal, reproducible example

import puppeteer from "puppeteer";

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
  });
  const mainPage = await browser.newPage();
  await mainPage.goto("http://google.com");
  await mainPage.waitForDevicePrompt();
})();

Error string

no error

Bug behavior

  • Flaky
  • PDF

Background

I've been working on automating running the manual web bluetooth tests in https://github.com/WebBluetoothCG/manual-tests. (see WebBluetoothCG/manual-tests#38 for more details). I ran into this issue while trying to use puppeteer's API to connect to a bluetooth device.

I've verified that:

  • running navigator.bluetooth.getDevices in the console of a Chrome for Testing opened by puppeteer doesn't crash
  • manually sending a message over CDP to enable DeviceAccess on a Chrome for Testing instance opened by puppeteer also doesn't crash

These are equivalent to what should happen with a call to waitForDevicePrompt(), leading me to believe this is a bug in puppeteer itself. (I've also reproduced this crash with a headless setup, but I'm not sure this can work in a headless environment anyway).

From looking through this repo's git history, it seems that this feature itself has been minimally changed since it was added in #9299, so I'm wondering if perhaps something changed elsewhere in the codebase to break this behavior?

Please let me know if any more information would be helpful!


Note: The minimal script to achieve my goal is below, but the minimal script I provided above is enough to trigger the crash.

import puppeteer from "puppeteer";

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
  });
  const mainPage = await browser.newPage();
  await mainPage.goto(
    "http://googlechrome.github.io/samples/web-bluetooth/get-devices-async-await.html"
  );
  await mainPage.waitForNetworkIdle();
  const [devicePrompt] = await Promise.all([
    mainPage.waitForDevicePrompt(),
    mainPage.locator("#requestBluetoothDevice").click(),
  ]);
  console.log(devicePrompt.devices);
  await browser.close();
})();

Expectation

In the above script, I would expect puppeteer to timeout while waiting for the bluetooth menu to appear. In my actual use case, I would add code

Reality

Puppeteer crashes immediately on calling waitForDevicePrompt() with the following stack trace:

/Users/user-name/src/bluetooth-puppeteer/node_modules/puppeteer-core/src/util/assert.ts:29
    throw new Error(message);
          ^
Error
    at assert (/Users/user-name/src/bluetooth-puppeteer/node_modules/puppeteer-core/src/util/assert.ts:29:11)
    at CdpFrame.#deviceRequestPromptManager (/Users/user-name/src/bluetooth-puppeteer/node_modules/puppeteer-core/src/cdp/Frame.ts:299:11)
    at CdpFrame.waitForDevicePrompt (/Users/user-name/src/bluetooth-puppeteer/node_modules/puppeteer-core/src/cdp/Frame.ts:307:50)
    at CdpFrame.<anonymous> (/Users/user-name/src/bluetooth-puppeteer/node_modules/puppeteer-core/src/util/decorators.ts:71:21)
    at CdpPage.waitForDevicePrompt (/Users/user-name/src/bluetooth-puppeteer/node_modules/puppeteer-core/src/cdp/Page.ts:1215:35)
    at run (/Users/user-name/src/bluetooth-puppeteer/index.ts:9:14)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

Puppeteer configuration file (if used)

No response

Puppeteer version

21.3.6

Node version

18.17.1

Package manager

npm

Package manager version

9.6.7

Operating system

macOS

@github-actions
Copy link

github-actions bot commented Oct 3, 2023

The issue has been labeled as confirmed by the automatic analyser.
Someone from the Puppeteer team will take a look soon!


Analyzer run

@outofambit
Copy link
Author

I'm a little confused by the parsing used for errors by the issue analyzer Action above but hopefully the stack trace I included in the rest of the form makes the expected output from my example clear!

@OrKoN OrKoN self-assigned this Oct 4, 2023
@OrKoN
Copy link
Collaborator

OrKoN commented Oct 4, 2023

@mzgoddard do you have thoughts? It looks like we actually missed the coverage for this case and I think #deviceRequestPromptManager might be not working as expected.

@outofambit
Copy link
Author

@mzgoddard and i chatted offline about this and we looked at these lines, where the assert fails and the crash starts:

const parentFrame = this.parentFrame();
assert(parentFrame !== null);

The best guess we had there was that perhaps something has changed with Frames or the Frame lifecycle in puppeteer in the last ~6 months that would cause this to behave differently? I don't see any obvious things from the git history.

@thiagowfx let us know if you have any ideas! 🙏

@OrKoN
Copy link
Collaborator

OrKoN commented Oct 6, 2023

@outofambit I am not sure anything has changed either but perhaps the assertion was not correct? I believe the main frame always has no parent frame.

@thiagowfx
Copy link
Contributor

@outofambit No ideas, but I will take a look this week

@thiagowfx
Copy link
Contributor

I can reproduce it.

thiagowfx added a commit that referenced this issue Oct 13, 2023
Only call `deviceRequestPromptManager()` if the parent frame is not
null.

Bug: #11072
Follow-up-of: #9299
thiagowfx added a commit that referenced this issue Oct 13, 2023
Only call `deviceRequestPromptManager()` if the parent frame is not
null.

Bug: #11072
Follow-up-of: #9299
thiagowfx added a commit that referenced this issue Oct 13, 2023
Only call `deviceRequestPromptManager()` if the parent frame is not
null.

Bug: #11072
Follow-up-of: #9299
thiagowfx added a commit that referenced this issue Oct 13, 2023
Only call `deviceRequestPromptManager()` if the parent frame is not
null.

Bug: #11072
Follow-up-of: #9299
thiagowfx added a commit that referenced this issue Oct 13, 2023
Only call `deviceRequestPromptManager()` if the parent frame is not
null.

Bug: #11072
Follow-up-of: #9299
thiagowfx added a commit that referenced this issue Oct 15, 2023
Only call `deviceRequestPromptManager()` if the parent frame is not
null.

Bug: #11072
Follow-up-of: #9299
@thiagowfx
Copy link
Contributor

@outofambit Please try again. I believe this should be fixed now.

@thiagowfx
Copy link
Contributor

We also added a unit test here to ensure it is indeed not crashing anymore: #11159

The E2E test is just a smoke test, it does not test any specific behaviors, just that it does not crash.

@outofambit let me know if you have any ideas on how we could add more E2E tests. Maybe one of the examples you're playing with could become a test :D

@outofambit
Copy link
Author

@thiagowfx thanks so much for looking at this! 🙏🏽 i'll give it a try today and report back. i'll also take a look at the e2e tests and see if i have something i can propose.

@Lightning00Blade
Copy link
Collaborator

Note: there is no new release for this it will be part of v21.4.0, when #11095 gets merged.

@outofambit
Copy link
Author

@thiagowfx i was able to run this in my application with a locally built version of puppeteer and it worked great! 🎉 thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants