9

I have a create-react-app that reads and writes local files using File System Access API. When run in a browser (Chrome or Edge that support it), both reading and writing files work fine.

When the app is run in Electron, reading works but writing fails due to: Uncaught (in promise) DOMException: The request is not allowed by the user agent or the platform in the current context.

I am using the latest Electron (12.0.1) which uses the same Chromium (89.0.4389.82) as the one in my Chrome browser.

Below is the relevant code. The console log after requestPermission call shows true and granted in the browser and true and denied in Electron.

I tried disabling webSecurity when creating BrowserWindow, disabling sandbox with appendSwitch but nothing helped.

Is there a way to give Chromium in Electron more permissions?

If not, I am willing to handle file writing differently when in Electron. In that case, what to write in place of TODO in the code? Note that because it is a create-react-app, the fs module is not available.

export async function chooseAndReadFile() {
    const fileHandle = await window.showOpenFilePicker().then((handles) => handles[0])
    const file = await fileHandle.getFile()
    const contents = await file.text()
    return contents
}

export async function chooseAndWriteToFile(contents: string) {
    const fileHandle = await window.showSaveFilePicker()

    const descriptor: FileSystemHandlePermissionDescriptor = {
        writable: true,
        mode: "readwrite"
    }
    const permissionState = await fileHandle.requestPermission(descriptor)
    console.log(window.isSecureContext)
    console.log(permissionState)

    const writable = await fileHandle.createWritable()
    await writable.write(contents)
    await writable.close()
}

let isElectron = require("is-electron")
export async function chooseAndWriteToFileUniversal(contents: string) {
    if (isElectron()) {
        // TODO: Do what???
    } else {
        chooseAndWriteToFile(contents)
    }
}
astraujums
  • 709
  • 8
  • 20

1 Answers1

4

Answering my own question, I finally used a solution with HTML download attribute, nicely described here. When this technique is used in Electron, it presents a file save dialog which is exactly what I want. When used in a browser, this technique just downloads the file without a prompt, so I will continue using File System Access API for browser environments.

Here is the code that handles downloading when running in Electron.

function download(filename: string, contents: string) {
    var element = document.createElement('a');
    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(contents));
    element.setAttribute('download', filename);
    element.style.display = 'none';
    document.body.appendChild(element);

    element.click();

    document.body.removeChild(element);
}

let isElectron = require("is-electron");
export async function chooseAndWriteToFileUniversal(contents: string) {
    if (isElectron()) {
        download("data.txt", contents)
    } else {
        chooseAndWriteToFile(contents) // See the original question for implementation of this function
    }
}

Still, would be nice to know why/how is Chromium in Electron more restricted than in a normal Chrome or Edge browser, and if it can be changed.

astraujums
  • 709
  • 8
  • 20
  • 1
    I see there is a open electron issue for this problem: https://github.com/electron/electron/issues/28422 – XYZ Jul 27 '22 at 23:24
  • 1
    I can confirm that calling app.commandLine.appendSwitch("enable-experimental-web-platform-features") at the start of main.js is a work around, but I would still be cautious to use this for security reasons. – XYZ Jul 27 '22 at 23:43