Skip to content

Local Cloak browser-run does not forward transient filechooser events #448

Description

@luissantana1

Summary

webcmd browser run cannot receive a filechooser event from the local Cloak runtime, so an in-memory upload cannot be supplied to transient input[type=file] controls such as Flutter Web file_picker.

Reproduction

Environment:

  • Webcmd 0.7.4
  • Node 26.7.0
  • Local Cloak runtime 0.4.5; reproduced again after testing 0.5.9

Create an isolated browser session and run this program:

await page.setContent(`
  <button id="pick">Attach PDF</button><output id="result"></output>
  <script>
    document.querySelector('#pick').addEventListener('click', () => {
      const input = document.createElement('input');
      input.type = 'file';
      input.accept = '.pdf';
      input.style.display = 'none';
      input.addEventListener('change', () => {
        document.querySelector('#result').textContent = input.files[0]?.name ?? '';
      });
      document.body.appendChild(input);
      input.click();
      input.remove();
    });
  </script>
`);

const pending = page.waitForEvent('filechooser', { timeout: 5000 });
await page.getByRole('button', { name: 'Attach PDF' }).click();
const chooser = await pending;
await chooser.setFiles({
  name: 'evidence.pdf',
  mimeType: 'application/pdf',
  buffer: new TextEncoder().encode('%PDF-1.4'),
});
return await page.locator('#result').textContent();

Expected

The program receives the file chooser and reports evidence.pdf. File payloads remain in memory; host filesystem paths are not needed.

Actual

The program times out waiting for filechooser (the overall browser-run limit is reached when no shorter timeout is supplied).

Control

The equivalent regression runs successfully in Webcmd's runBrowserProgram test with playwright-core; the failure occurs only through the real local Cloak runtime bridge. Updating Cloak from 0.4.5 to 0.5.9 did not change the result.

Requested behavior

Forward Page.filechooser through the local Cloak browser-run bridge so callers can use the existing FileChooser.setFiles API with in-memory payloads, including inputs that are removed immediately after click().

Activity

  1. Ayushraj06-bit commented on Sep 13, 2026

    @Ayushraj06-bit
    Contributor

    I tried to reproduce this against the real local Cloak runtime (cloakbrowser 0.4.5, Chromium 146.0.7680.177.5, headed persistent context) and the file chooser turns out to be fine — it's the first line of the program that hangs.

    Running your program unchanged reproduces the timeout. Changing only page.setContent(...) to page.goto('data:text/html,' + encodeURIComponent(html)) and leaving the rest identical:

    A) direct Playwright on Cloak : "evidence.pdf"
    B) through browser run        : ok, "evidence.pdf"
    

    So filechooser is forwarded through the bridge, FileChooser.setFiles works with in-memory payloads, and the input being removed right after click() is not a problem.

    What fails is page.setContent(), which on Cloak applies the markup and then never resolves:

    [stock chromium]     setContent: RESOLVED 11ms | console ev: ["log:main-log","debug:main-debug"]
    [cloak humanize:on]  setContent: HUNG 8005ms   | content landed: "hi" | console ev: []
    [cloak humanize:off] setContent: HUNG 8005ms   | content landed: "hi" | console ev: []
    

    Cloak's Chromium accepts Runtime.enable but never emits Runtime.consoleAPICalled. Playwright resolves Frame.setContent on a console.debug(<tag>) sentinel it writes from the utility world next to document.write, so the promise never settles. Your program spends its whole budget on statement one, which is why it looked like a filechooser timeout — and why the control passed: with playwright-core and a stock Chromium, setContent resolves normally. That also explains why Cloak 0.5.9 made no difference; it is the browser binary, not the wrapper.

    Workaround today: navigate to the markup instead of setting it.

    await page.goto('data:text/html,' + encodeURIComponent(html));

    #528 makes browser run say this instead of Split the task into a smaller run or increase --timeout, which is the part that currently sends you back around the same 30s wall. It does not make setContent work — that needs a fix in Cloak's Chromium.

    One side effect worth noting separately: because those console events never arrive, page.on('console') also never fires on the default runtime, so page-side console.log is silently dropped in browser run. Happy to open that as its own issue if useful.

  2. Ayushraj06-bit commented on Sep 13, 2026

    @Ayushraj06-bit
    Contributor

    Correction to the workaround I posted above, and an upstream link.

    This is a known CloakBrowser bug — CloakHQ/cloakbrowser#360, opened 2026-06-07. Cloak-HQ confirmed and root-caused it to the browser binary's DevTools layer and said a fix was going into the next binary build. It is still open, and I've confirmed it still reproduces on the current binary (146.0.7680.177.5, wrapper 0.4.5).

    They publish a better workaround than the data: URL I suggested — it keeps the page on its current origin instead of navigating away, which matters if you are mid-session on a real site:

    await page.evaluate((h) => { document.open(); document.write(h); document.close(); }, html);
    await page.waitForLoadState('load');

    I verified this end to end through browser run on real Cloak with your exact program: it completes in 21ms, leaves page.url() untouched, and the file chooser then resolves to evidence.pdf.

    #528 has been updated to point at this recovery rather than the data: URL.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions