microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
65.69k stars 3.58k forks source link

[BUG] Extension chrome.downloads.onDeterminingFilename won't be called on file download #19885

Closed avishail closed 1 month ago

avishail commented 1 year ago

Context:

Code Snippet

path_to_extension = 'set here the path to the extension directory'
browser_with_extension = playwright.chromium.launch_persistent_context(
    tempfile.TemporaryDirectory().name,  # pylint: disable=consider-using-with
    headless=False,
    #channel="msedge / chrome",
    args=[
        f"--disable-extensions-except={path_to_extension}",
        f"--load-extension={path_to_extension}",
    ],
)

# go to https://speed.hetzner.de/ and click on the first link which should start a file download.

page = browser_with_extension.new_page()
page.goto(
    "https://speed.hetzner.de/",
    wait_until="networkidle",
)
page.wait_for_selector("a")
page.wait_for_timeout(5000)
with page.expect_download(timeout=5000) as download_info:
            page.click("a")

download = download_info.value
path = download.path()

Describe the bug

Our extension register to chrome.downloads.onDeterminingFilename. When downloading a file, this listener should be called and indeed we see it when running on typical Chrome browser. But, when running on playwrite, although the download itself is working the listener won't be called.

Add any other details about the problem here.

I've tried to use Chromium, Chrome and Edge and the behavior was consistent. The file was downloaded but the listener wasn't called.

This is a test extension: extension.zip

mxschmitt commented 1 year ago

That's most likely a bug inside Chromium, I recommend filing a bug on https://crbug.com with a Puppeteer reproducible, I can help there if needed.

avishail commented 1 year ago

@mxschmitt thanks for your comment.

I'm pretty sure this isn't a chromium issue because:

  1. it is working on Selenium which runs chromium
  2. it is not working on Playwright when launching with channel=chrome (and not Chromium)
  3. it is working on Chromium based browsers that are not Chrome
aslushnikov commented 1 year ago

@avishail I indeed can repro using js:

// a.mjs
import { chromium } from '@playwright/test';

const extensionPath = './extension';
const context = await chromium.launchPersistentContext('./foo', {
  headless: false,
  args: [
    `--disable-extensions-except=${extensionPath}`,
    `--load-extension=${extensionPath}`,
  ],
});

const page = await context.newPage();
await page.goto('https://speed.hetzner.de/');

const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.getByText('100Mb').click(),
]);
console.log(await download.path());
// await context.close();

The issue is due to the Browser.setDownloadBehavior CDP call:

https://github.com/microsoft/playwright/blob/e674ea217f5facffc668bf7aaea890dab20d68ca/packages/playwright-core/src/server/chromium/crBrowser.ts#L339-L344

So technically you can work-around it using the browserContext.newCDPSession(page) call, like this:

// a.mjs
import { chromium } from '@playwright/test';

const extensionPath = './extension';
const context = await chromium.launchPersistentContext('./foo', {
  headless: false,
  args: [
    `--disable-extensions-except=${extensionPath}`,
    `--load-extension=${extensionPath}`,
  ],
});

const page = await context.newPage();
await page.goto('https://speed.hetzner.de/');

// >>>>>>>>>>>>>> WORKAROUND TO MAKE chrome.downloads.onDeterminingFilename WORK
const session = await context.newCDPSession(page);
await session.send('Browser.setDownloadBehavior', {
  behavior: 'default',
  downloadPath: './foo',
  eventsEnabled: true,
});
// <<<<<<<<<<<<<<

const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.getByText('100Mb').click(),
]);
console.log(await download.path());
// await context.close();

I'll mark this as P3 to collect upvotes; we might consider fixing this if it hits more people!

avishail commented 1 year ago

@aslushnikov many thanks for the workaround suggestion. I'll try it first thing in the morning.

avishail commented 1 year ago

@aslushnikov I verified and this solution indeed works on my env! Thank you so much :)

dvTB commented 1 year ago

Don't know if this is the same issue, but as download-event does not report in headless mode, it seems like the same. It works if I disable "chromiumSandbox" in den launch-Options of the browser.

For the last few months, this was not a problem but for a few weeks it seems to be a problem, that waitForEvent('download') no longer reports back.

Edit: that was too early. It worked for a few runs an now it won't work. Than it worked again and a few runs later it no longer does. But I always see that the file is downloaded into the configured directory. Really strange

borfig commented 1 year ago

The browser fires the event only in default behavior. However, the download files appear in $HOME/Downloads and are not usable via download.saveAs()

pavelfeldman commented 1 month ago

Why was this issue closed?

Thank you for your contribution to our project. This issue has been closed due to its limited upvotes and recent activity, and insufficient feedback for us to effectively act upon. Our priority is to focus on bugs that reflect higher user engagement and have actionable feedback, to ensure our bug database stays manageable.

Should you feel this closure was in error, please create a new issue and reference this one. We're open to revisiting it given increased support or additional clarity. Your understanding and cooperation are greatly appreciated.