Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update FileSystemObserver explainer #163

Merged
merged 5 commits into from
Jun 28, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 24 additions & 21 deletions proposals/FileSystemObserver.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,17 +189,17 @@ async function markDirty(record) {
// Decide how to mark the file dirty according to the
// `FileSystemChangeType` included in each file system change record.
switch (record.type) {
case 'created':
case 'appeared':
// `record.root` is the handle passed to `observe()`. Note that
// the File System specification does not expose the concept of an
// absolute path, so understanding a file system change is
// inherently relative to some directory.
markCreated(record.root, record.relativePathComponents);
markAppeared(record.root, record.relativePathComponents);
break;
case 'deleted':
case 'disappeared':
// The relative path of the changed handle may be more useful than
// the handle itself, since the file no longer exists.
markDeleted(record.root, record.relativePathComponents);
markDisappeared(record.root, record.relativePathComponents);
break;
case 'modified':
// A handle to the changed path may be more useful than its
Expand All @@ -219,8 +219,8 @@ async function markDirty(record) {
markMoved(record.root, record.relativePathMovedFrom,
record.relativePathComponents);
break;
case 'unsupported':
// Change types may not be supported on all platforms.
case 'unknown':
// Unknown change event(s) may have been missed.
if (await checkIfChanged(record.changedHandle)) {
markChanged(record.root, record.relativePathComponents);
}
Expand Down Expand Up @@ -264,7 +264,7 @@ async function handleChanges(records) {
const changedHandle = record.changedHandle;

// Take advantage of file-level notifications, if available.
if (changedHandle.kind === 'file' && record.type === 'created') {
if (changedHandle.kind === 'file' && record.type === 'appeared') {
sawFileCreatedRecord = true;
readNewFile(changedHandle);
}
Expand Down Expand Up @@ -304,6 +304,10 @@ Likewise, changes which occur before an observer is created should not be report

A `FileSystemObserver` is not [serializable](https://html.spec.whatwg.org/multipage/structured-data.html#serializable) and therefore cannot be persisted across browsing sessions. Websites which wish to watch the same files on each session may store serializable `FileSystemHandle` and `FileSystemObserverObserveOptions` objects in IndexedDB, then create a `FileSystemObserver` and configure it from these objects on page reload.

### Interactions with Back/forward Cache

If changes occurred while the page was not fully active, and the page becomes active again (i.e. back/forward cache), then user agents may use the `"unknown"` `FileSystemChangeType` to indicate that _changes_ have occurred. Specific types and ordering of changes should not be exposed but indicating that some changes have occurred could be useful to the website to perform any special handling.

### Signaling Changes Made via a `FileSystemSyncAccessHandle`

It is assumed that a user agent’s implementation of the `FileSystemObserver` interface will involve coordinating with a centralized browser process. However, unlike most web storage APIs, reading and writing files with a `FileSystemSyncAccessHandle` is commonly implemented largely without coordinating with a centralized browser process. This is critical to the exceptional performance characteristics of this interface. `write()` or `truncate()` operations on a `FileSystemSyncAccessHandle` should trigger a file system change record, but requiring round-trip IPC to complete before synchronously returning would be detrimental to performance.
Expand Down Expand Up @@ -343,8 +347,8 @@ However, given the cross-platform differences, this proposal does not attempt to
```javascript
const callback = (records, observer) => {
// What change record will be triggered when the file is created?
// -> 1: { type: "create", relativePathComponents: ["file.txt"], ... }
// 2: { type: "create", relativePathComponents: [], ... }
// -> 1: { type: "appeared", relativePathComponents: ["file.txt"], ... }
// 2: { type: "appeared", relativePathComponents: [], ... }
// 3: { type: "modified", relativePathComponents: [], ... }
}

Expand All @@ -355,15 +359,15 @@ await directoryHandle.getFileHandle('file.txt', { create: true });

User agents should attempt to include the most precise information as it can reasonably obtain in the file system change record. In this example, the change record is most useful if it details that a specific file has been added (i.e. option 1) as opposed to mentioning just that the parent directory’s contents were modified - which would require the website to iterate through the directory to figure out which file has changed, and how.

All changes to a Bucket File System should deterministically map to a precise file system change record. In this example, the `getFileHandle()` call should result in a change record with a `”create”` change type and describe the change as occurring on the created file.
All changes to a Bucket File System should deterministically map to a precise file system change record. In this example, the `getFileHandle()` call should result in a change record with a `”appeared”` change type and describe the change as occurring on the created file.

However, this level of detail is not realistic on all platforms for local file system changes. For example, Linux has no native support for recursive watches. As such, the details of a file system change record for a given change to the local file system should be regarded as best-effort. In the example below, the user agent may report either a `”create”` change type describing the created file, a `”create”` change type describing a creation within the observed directory, or a `”modified”` change type describing that the directory contents were modified.
However, this level of detail is not realistic on all platforms for local file system changes. For example, Linux has no native support for recursive watches. As such, the details of a file system change record for a given change to the local file system should be regarded as best-effort. In the example below, the user agent may report either a `”appeared”` change type describing the created file, a `”appeared”` change type describing a creation within the observed directory, or a `”modified”` change type describing that the directory contents were modified.

```javascript
const callback = (records, observer) => {
// What change record will be triggered when the file is created?
// ?? 1: { type: "create", relativePathComponents: ["file.txt"], ... }
// ?? 2: { type: "create", relativePathComponents: [], ... }
// ?? 1: { type: "appeared", relativePathComponents: ["file.txt"], ... }
// ?? 2: { type: "appeared", relativePathComponents: [], ... }
// ?? 3: { type: "modified", relativePathComponents: [], ... }
}

Expand All @@ -374,8 +378,6 @@ await observer.observe(directoryHandle, { recursive: true });
// corresponding to `directoryHandle`, then `touch file.txt`)
```

User agents may also use the `"unsupported"` `FileSystemChangeType` to explicitly indicate that change types are not supported.

#### When to Signal Local File System Writes

Writing to a file on the local file system generally looks like the following:
Expand Down Expand Up @@ -632,7 +634,8 @@ This method could be useful to see which files and directories are being watched
* https://github.com/WICG/file-system-access/issues/72
* https://github.com/whatwg/fs/issues/123
* https://github.com/w3c/IndexedDB/issues/51
* Gecko: No signals
* Gecko: Positive
* https://github.com/mozilla/standards-positions/issues/942#issuecomment-2113526096
* WebKit: No signals

## Appendix
Expand All @@ -654,11 +657,11 @@ callback FileSystemObserverCallback = void (
);

enum FileSystemChangeType {
"created",
"deleted",
"appeared",
"disappeared",
"modified",
"moved",
"unsupported", // Change types are not supported on this platform
"unknown", // Change types are not known
"errored" // This observation is no longer valid
};

Expand All @@ -676,6 +679,6 @@ interface FileSystemChangeRecord {
// The type of change
readonly attribute FileSystemChangeType type;
// Former location of a moved handle. Used only when type === "moved"
readonly attribute FileSystemHandle? relativePathMovedFrom;
readonly attribute FrozenArray<USVString>? relativePathMovedFrom;
};
```
```
Loading