Bug 250495

Summary: Data written via FileSystemSyncAccessHandle disappears after creating new FileSystemFileHandle
Product: WebKit Reporter: Alex Titarenko <03_placid_daft>
Component: Website StorageAssignee: Sihui Liu <sihui_liu>
Status: RESOLVED FIXED    
Severity: Normal CC: karlcow, sihui_liu, webkit-bug-importer
Priority: P2 Keywords: BrowserCompat, InRadar
Version: Safari 16   
Hardware: All   
OS: Unspecified   

Description Alex Titarenko 2023-01-11 19:57:04 PST
With many attempts, I could not find a way to persist written data to FileSystemSyncAccessHandle.
The example from https://webkit.org/blog/12257/the-file-system-access-api-with-origin-private-file-system/ article works only if you try to getFile/read data from the same instance of FileSystemFileHandle, when you get a new instance of FileHandle all data disappears.

This can be easily reproduced with a slight modification to the existing LayoutTest at: https://github.com/WebKit/WebKit/blob/3729059e6a040c8c168679975c9d04dab48e5a4d/LayoutTests/storage/filesystemaccess/resources/file-handle-getfile.js#L44

If before `fileObject = await fileHandle.getFile();` line you place `fileHandle = await rootHandle.getFileHandle("file-handle-getfile.txt", { "create" : false });` you will not get any actual data in return, even if you add accessHandle.flush() call before accessHandle.close();

This works perfectly fine in Chrome but not in WebKit.

FileSystemSyncAccessHandle is the only available way to write data in WebKit but it's not working because it does not persist data.
Comment 1 Alex Titarenko 2023-01-11 20:22:13 PST
More clear information on how to reproduce:

In file:
https://github.com/WebKit/WebKit/blob/main/LayoutTests/storage/filesystemaccess/resources/file-handle-getfile.js

replace test function with:

```
async function test() 
{
    try {
        var rootHandle = await navigator.storage.getDirectory();
        // Create a new file for this test.
        await rootHandle.removeEntry("file-handle-getfile.txt").then(() => { }, () => { });
        fileHandle = await rootHandle.getFileHandle("file-handle-getfile.txt", { "create" : true });
        fileContent = "";
    
        // Write file content in worker.
        if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
            console.log('hello from WebWorker')
            accessHandle = await fileHandle.createSyncAccessHandle();
            const encoder = new TextEncoder();
            fileContent = "This is a test.";
            writeBuffer = encoder.encode(fileContent);
            writeSize = accessHandle.write(writeBuffer, { "at" : 0 });
            shouldBe("writeSize", "writeBuffer.byteLength");
            accessHandle.flush(); // This is a new line
            accessHandle.close();
        }

        fileHandle = await rootHandle.getFileHandle("file-handle-getfile.txt"); // This is a new line
        fileObject = await fileHandle.getFile();
        readText = await read(fileObject);
        shouldBe("readText", "fileContent");

        finishTest();
    } catch (error) {
        finishTest(error.toString());
    }
}
```

Then run LayoutTest:
https://github.com/WebKit/WebKit/blob/main/LayoutTests/storage/filesystemaccess/file-handle-getfile-worker.html
Comment 2 Sihui Liu 2023-01-12 11:26:36 PST Comment hidden (obsolete)
Comment 3 Sihui Liu 2023-01-12 11:28:12 PST
(In reply to Alex Titarenko from comment #1)
> More clear information on how to reproduce:
> 
> In file:
> https://github.com/WebKit/WebKit/blob/main/LayoutTests/storage/
> filesystemaccess/resources/file-handle-getfile.js
> 
> replace test function with:
> 
> ```
> async function test() 
> {
>     try {
>         var rootHandle = await navigator.storage.getDirectory();
>         // Create a new file for this test.
>         await rootHandle.removeEntry("file-handle-getfile.txt").then(() => {
> }, () => { });
>         fileHandle = await
> rootHandle.getFileHandle("file-handle-getfile.txt", { "create" : true });
>         fileContent = "";
>     
>         // Write file content in worker.
>         if (typeof WorkerGlobalScope !== 'undefined' && self instanceof
> WorkerGlobalScope) {
>             console.log('hello from WebWorker')
>             accessHandle = await fileHandle.createSyncAccessHandle();
>             const encoder = new TextEncoder();
>             fileContent = "This is a test.";
>             writeBuffer = encoder.encode(fileContent);
>             writeSize = accessHandle.write(writeBuffer, { "at" : 0 });
>             shouldBe("writeSize", "writeBuffer.byteLength");
>             accessHandle.flush(); // This is a new line
>             accessHandle.close();
>         }
> 
>         fileHandle = await
> rootHandle.getFileHandle("file-handle-getfile.txt"); // This is a new line
>         fileObject = await fileHandle.getFile();
>         readText = await read(fileObject);
>         shouldBe("readText", "fileContent");
> 
>         finishTest();
>     } catch (error) {
>         finishTest(error.toString());
>     }
> }
> ```
> 
> Then run LayoutTest:
> https://github.com/WebKit/WebKit/blob/main/LayoutTests/storage/
> filesystemaccess/file-handle-getfile-worker.html

Thanks for the report. I think it's caused by file being truncated unexpectedly on FileSystemHandle creation. Will make a fix.
Comment 4 Radar WebKit Bug Importer 2023-01-12 11:29:25 PST
<rdar://problem/104187327>
Comment 5 Sihui Liu 2023-01-12 11:32:28 PST
Pull request: https://github.com/WebKit/WebKit/pull/8586
Comment 6 EWS 2023-01-13 09:29:48 PST
Committed 258876@main (60a6090b4106): <https://commits.webkit.org/258876@main>

Reviewed commits have been landed. Closing PR #8586 and removing active labels.