diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /ipc/glue/test/browser/browser_utility_filepicker_crashed.js | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/glue/test/browser/browser_utility_filepicker_crashed.js')
-rw-r--r-- | ipc/glue/test/browser/browser_utility_filepicker_crashed.js | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/ipc/glue/test/browser/browser_utility_filepicker_crashed.js b/ipc/glue/test/browser/browser_utility_filepicker_crashed.js new file mode 100644 index 0000000000..e8eb83cf30 --- /dev/null +++ b/ipc/glue/test/browser/browser_utility_filepicker_crashed.js @@ -0,0 +1,170 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +SimpleTest.requestCompleteLog(); + +// Wait until the child process with the given PID has indeed been terminated. +// +// Note that `checkUtilityExists`, and other functions deriving from the output +// of `ChromeUtils.requestProcInfo()`, do not suffice for this purpose! It is an +// attested failure mode that the file-dialog utility process has been removed +// from the proc-info list, but is still live with the file-picker dialog still +// displayed. +function untilChildProcessDead(pid) { + return utilityProcessTest().untilChildProcessDead(pid); +} + +async function fileDialogProcessExists() { + return !!(await tryGetUtilityPid("windowsFileDialog")); +} + +// Poll for the creation of a file dialog process. +function untilFileDialogProcessExists(options = { maxTime: 2000 }) { + // milliseconds + const maxTime = options.maxTime ?? 2000, + pollTime = options.pollTime ?? 100; + const count = maxTime / pollTime; + + return TestUtils.waitForCondition( + () => tryGetUtilityPid("windowsFileDialog", { quiet: true }), + "waiting for file dialog process", + pollTime, // interval + count // maxTries + ); +} + +function openFileDialog() { + const process = (async () => { + await untilFileDialogProcessExists(); + let pid = await tryGetUtilityPid("windowsFileDialog"); + ok(pid, `pid should be acquired in openFileDialog::process (got ${pid})`); + // HACK: Wait briefly for the file dialog to open. + // + // If this is not done, we may attempt to crash the process while it's in + // the middle of creating and showing the file dialog window. There _should_ + // be no problem with this, but `::MiniDumpWriteDump()` occasionally fails + // with mysterious errors (`ERROR_BAD_LENGTH`) if we crashed the process + // while that was happening, yielding no minidump and therefore a failing + // test. + // + // Use of an arbitrary timeout could presumably be avoided by setting a + // window hook for the file dialog being shown and `await`ing on that. + // + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(res => setTimeout(res, 1000)); + return pid; + })(); + + const file = new Promise((resolve, reject) => { + info("Opening Windows file dialog"); + let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); + fp.init(window, "Test: browser_utility_filepicker_crashed.js", fp.modeOpen); + fp.open(result => { + ok( + result == fp.returnCancel, + "filepicker should resolve to cancellation" + ); + resolve(); + }); + }); + + return { process, file }; +} + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + // remote, no fallback + ["widget.windows.utility_process_file_picker", 2], + ], + }); +}); + +function makeTask(description, Describe, action) { + let task = async function () { + if (await fileDialogProcessExists()) { + // If this test proceeds, it will probably cause whatever other test has a + // file dialog open to fail. + // + // (We shouldn't be running two such tests in parallel on the same Fx + // instance, but that's not obvious at this level.) + ok(false, "another test has a file dialog open; aborting"); + return; + } + + const { process, file } = openFileDialog(); + const pid = await process; + const untilDead = untilChildProcessDead(pid); + + info(Describe + " the file-dialog utility process"); + await action(); + + // the file-picker's callback should have been promptly cancelled + const _before = Date.now(); + await file; + const _after = Date.now(); + const delta = _after - _before; + info(`file callback resolved after ${description} after ${delta}ms`); + + // depending on the test configuration, this may take some time while + // cleanup occurs + await untilDead; + }; + + // give this task a legible name + Object.defineProperty(task, "name", { + value: "testFileDialogProcess-" + Describe.replace(" ", ""), + }); + + return task; +} + +for (let [description, Describe, action] of [ + ["crash", "Crash", () => crashSomeUtilityActor("windowsFileDialog")], + [ + "being killed", + "Kill", + () => cleanUtilityProcessShutdown("windowsFileDialog", true), + ], + // Unfortunately, a controlled shutdown doesn't actually terminate the utility + // process; the file dialog remains open. (This is expected to be resolved with + // bug 1837008.) + /* [ + "shutdown", + "Shut down", + () => cleanUtilityProcessShutdown("windowsFileDialog"), + ] */ +]) { + add_task(makeTask(description, Describe, action)); + add_task(testCleanup); +} + +async function testCleanup() { + const killFileDialogProcess = async () => { + if (await tryGetUtilityPid("windowsFileDialog", { quiet: true })) { + await cleanUtilityProcessShutdown("windowsFileDialog", true); + return true; + } + return false; + }; + + // If a test failure occurred, the file dialog process may or may not already + // exist... + if (await killFileDialogProcess()) { + console.warn("File dialog process found and killed"); + return; + } + + // ... and if not, may or may not be pending creation. + info("Active file dialog process not found; waiting..."); + try { + await untilFileDialogProcessExists({ maxTime: 1000 }); + } catch (e) { + info("File dialog process not found during cleanup (as expected)"); + return; + } + await killFileDialogProcess(); + console.warn("Delayed file dialog process found and killed"); +} |