1. Introduction
This section is non-normative.
This
document
defines
fundamental
infrastructure
for
file
system
APIs.
In
addition,
it
defines
an
API
that
makes
it
possible
for
websites
to
get
access
to
a
file
system
directory
without
having
to
first
prompt
the
user
for
access.
This
enables
use
cases
where
a
website
wants
to
save
data
to
disk
before
a
user
has
picked
a
location
to
save
to,
without
forcing
the
website
to
use
a
completely
different
storage
mechanism
with
a
different
API
for
such
files.
The
entry
point
for
this
is
the
navigator.storage.getDirectory()
method.
It
also
defines
an
API
to
listen
to
file
system
change
events.
Without
it,
sites
can
recursively
poll
the
file
system
to
find
changes
in
the
files
or
folder
structure.
This
can
be
time
consuming
especially
for
large
directories.
The
FileSystemObserver
provides
an
API
to
listen
for
change
events
so
that
polling
for
them
isn’t
required.
2. Files and Directories
2.1. Concepts
2.1.1. File System
A file system maintains an implementation-defined mapping of file system path s to file system entries .
Each file system has an associated root , an opaque string whose value is implementation-defined .
A file system path is a list of one or more strings . This may be a virtual path that is mapped to real location on disk or in memory, may correspond directly to a path on the local file system, or may not correspond to any file on disk at all. The actual physical location of the corresponding file system entry is implementation-defined .
Let
path
be
the
list
«
"
data
",
"
drafts
",
"
example.txt
"
».
There
is
no
expectation
that
a
file
named
example.txt
exists
anywhere
on
disk.
A file system path a is the same path as a file system path b if a ’s size is the same as b ’s size and for each index of a ’s indices a .\[[ index ]] is b .\[[ index ]].
Getting the path with entry returns path , provided no intermediate file system operations were run.
Locating an entry with path returns entry , provided no intermediate file system operations were run.
A
file
system
event
is
a
struct
consisting
of
a
type
(a
FileSystemChangeType
),
an
entry
type
(a
FileSystemHandleKind
or
null),
a
modified
path
(a
file
system
path
),
and
a
from
path
(a
file
system
path
or
null).
Note: A file system fires these events in response to file operations.
The
from
path
must
be
set
when
type
is
"
moved
"
and
must
be
null
otherwise.
Note:
The
entry
type
is
null
when
the
file
system
cannot
determine
the
FileSystemHandleKind
of
the
modified
path
due
to
the
underlying
file
system
entry
no
longer
being
there.
A file system has an associated set of file system observer registrations observer registrations which is initialized to the empty set.
When a user agent receives a list of file system events events for a file system fileSystem , the user agent MUST notify observer registrations of the fileSystem of events .
Note: The file system events fired by a file system might be unreliable and can be inaccurate, out of order, or missing.
Enqueue the following steps to the file system queue :
For each observerRegistration of the fileSystem ’s observer registrations .
Notify observerRegistration of events from fileSystem .
When a user agent receives an error event for a file system fileSystem and observerRegistrations (a subset of the fileSystem ’s observer registrations ), the user agent MUST send the error to observerRegistrations of fileSystem .
Enqueue the following steps to the file system queue :
Assert observerRegistrations is a subset of fileSystem ’s observer registrations .
For each observerRegistration of observerRegistrations :
Destroy observerRegistration .
Let observer be observerRegistration ’s observer .
Let global be observer ’s relevant global object .
Queue a storage task with global to run these steps:
Let changedHandle be the observerRegistration ’s root handle .
Let record be the result of creating a new
FileSystemChangeRecordfor observerRegistration given changedHandle , "errored", and null.Invoke observer ’s callback with « record » as the first argument and observer as the second argument.
A
file
system
observer
registration
consists
of
a
FileSystemObserver
observer
,
a
FileSystemHandle
root
handle
,
and
a
boolean
recursive
.
FileSystemObserver
observer
on
FileSystemHandle
rootHandle
with
boolean
recursive
:Let observerRegistration be a file system observer registration whose observer is observer , root handle is rootHandle , and recursive is recursive .
Let observerRegistrationLocator be rootHandle ’s locator .
Let observerRegistrationMap be observer ’s observerRegistrations .
Let fileSystem be the observerRegistrationLocator ’s file system .
Append observerRegistration to the fileSystem ’s observer registrations .
set observerRegistrationMap [ observerRegistrationLocator ] to observerRegistration .
Note: These steps have to be run on the file system queue .
Let observer be observerRegistration ’s observer .
Let rootHandle be observerRegistration ’s root handle .
Let observerRegistrationLocator be rootHandle ’s locator .
Let fileSystem be observerRegistrationLocator ’s file system .
Remove observerRegistration from the fileSystem ’s observer registrations .
Remove observerRegistrationLocator from observer ’s observerRegistrations .
Note: These steps have to be run on the file system queue .
Let rootHandle be observerRegistration ’s root handle .
Let observerRegistrationLocator be rootHandle ’s locator .
Assert : observerRegistrationLocator ’s file system is equal to fileSystem .
Let observer be observerRegistration ’s observer .
Let global be observer ’s relevant global object .
Queue a storage task with global to run these steps:
Let records be a list of
FileSystemChangeRecord.For each event of events :
Let eventType be event ’s type .
Assert eventType is not equal to "
errored".Note: "
errored" events are sent in the send an error steps.Let eventEntryType be event ’s entry type .
If eventType is "
modified" and eventEntryType is "directory" or null, continue .Note: We can safely ignore "
modified" events for "directory". These might be sent when one of its children receives an event which the page will be made aware of through that child event. Or they might be sent when some OS specific property changes which the web page cannot observe through web APIs. Similarly when we don’t know theFileSystemHandleKind, we can ignore it since the file system entry at the modified path no longer exists.If eventEntryType is null:
Set eventEntryType to observerRegistrationLocator ’s kind .
Note: When we don’t know the
FileSystemHandleKindof the modified path , we have to arbitrarily choose one to construct thechangedHandle. Setting it to observerRegistrationLocator ’s kind at least means it’ll be right for events on the root handle.Let modifiedPath be event ’s modified path .
Let fromPath be event ’s from path .
Let changedLocator be a file system locator whose kind is eventEntryType , file system is fileSystem , and path is modifiedPath .
Let movedFromLocator be null.
Let modifiedPathInScope be equal to changedLocator is in scope of observerRegistration .
Let fromPathInScope be false.
If eventType is "
moved":Assert : fromPath is not null.
Set movedFromLocator be a file system locator whose kind is eventEntryType , file system is fileSystem , and path is fromPath .
Set fromPathInScope equal to movedFromLocator is in scope of observerRegistration .
If both modifiedPathInScope and fromPathInScope are false, continue .
If eventType is "
moved":If modifiedPathInScope is false:
Set eventType to "
disappeared".Set changedLocator to movedFromLocator .
Set fromPath to null.
If fromPathInScope is false:
Set eventType to "
appeared".Set fromPath to null.
Note: Some file systems convert "
moved" events in and out of scope to "appeared" and "disappeared" respectively before we can. So to maintain consistency, we do it here.Let record be the result of creating a new
FileSystemChangeRecordfor observerRegistration given changedLocator , eventType , and fromPath .Append record to records .
If eventType is equal to "
disappeared" and changedHandle ’s locator is equal to observerRegistrationLocator :Set errorRecord to the result of creating a new
FileSystemChangeRecordfor observerRegistration given changedHandle , "errored", and null.Append errorRecord to records .
Destroy observerRegistration .
break .
If records is not empty :
Invoke observer ’s callback with records as the first argument and observer as the second argument.
Note: These steps have to be run on the file system queue .
Let observerRegistrationLocator be observerRegistration ’s root handle 's locator .
Let observerRegistrationRecursive be observerRegistration ’s recursive .
Let pathRelation be the result of getting the relationship between observerRegistrationLocator and handleLocator .
If pathRelation is "
other" or "ancestor", return false.If pathRelation is "
descendant" and observerRegistrationRecursive is false, return false.Return true.
Note: These steps have to be run on the file system queue .
2.1.2. File System Entry
A file system entry is either a file entry or a directory entry .
Each file system entry has an associated file system file system .
Each
file
system
entry
has
an
associated
query
access
algorithm,
which
takes
"
read
"
or
"
readwrite
"
mode
and
returns
a
file
system
access
result
.
Unless
specified
otherwise
it
returns
a
file
system
access
result
with
a
permission
state
of
"
denied
"
and
with
an
error
name
of
the
empty
string.
Each
file
system
entry
has
an
associated
request
access
algorithm,
which
takes
"
read
"
or
"
readwrite
"
mode
and
returns
a
file
system
access
result
.
Unless
specified
otherwise
it
returns
a
file
system
access
result
with
a
permission
state
of
"
denied
"
and
with
an
error
name
of
the
empty
string.
A file system access result is a struct encapsulating the result of querying or requesting access to the file system. It has the following items :
- permission state
- error name
-
A string which must be the empty string if permission state is "
granted"; otherwise an name listed in theDOMExceptionnames table . It is expected that in most cases when permission state is not "granted", this should be "NotAllowedError".
Dependent specifications may consider this API a powerful feature . However, unlike other powerful features whose permission request algorithm may throw, file system entry 's query access and request access algorithms must run in parallel on the file system queue and are therefore not allowed to throw. Instead, the caller is expected to queue a storage task to reject , as appropriate, should these algorithms return an error name other than the empty string.
Note: Implementations that only implement this specification and not dependent specifications do not need to bother implementing file system entry 's query access and request access .
Make access check algorithms associated with a FileSystemHandle.
Each file system entry has an associated name (a string ).
A valid file name is a string that is not an empty string, is not equal to "." or "..", and does not contain '/' or any other character used as path separator on the underlying platform.
Note: This means that '\' is not allowed in names on Windows, but might be allowed on other operating systems. Additionally underlying file systems might have further restrictions on what names are or aren’t allowed, so a string merely being a valid file name is not a guarantee that creating a file or directory with that name will succeed.
We should consider having further normative restrictions on file names that will never be allowed using this API, rather than leaving it entirely up to underlying file systems.
A
file
entry
additionally
consists
of
binary
data
(a
byte
sequence
),
a
modification
timestamp
(a
number
representing
the
number
of
milliseconds
since
the
Unix
Epoch
),
a
lock
(a
string
that
may
exclusively
be
"
open
",
"
taken-exclusive
"
or
"
taken-shared
")
and
a
shared
lock
count
(a
number
representing
the
number
shared
locks
that
are
taken
at
a
given
point
in
time).
A user agent has an associated file system queue which is the result of starting a new parallel queue . This queue is to be used for all file system operations.
exclusive
"
or
"
shared
"
on
a
given
file
entry
file
:
-
Let lock be the file ’s lock .
-
Let count be the file ’s shared lock count .
-
If value is "
exclusive":-
If lock is "
open":-
Set lock to "
taken-exclusive". -
Return "
success".
-
-
-
If value is "
shared":-
If lock is "
open":-
Set lock to "
taken-shared". -
Set count to 1.
-
Return "
success".
-
-
Otherwise, if lock is "
taken-shared":-
Increase count by 1.
-
Return "
success".
-
-
-
Return "
failure".
Note: These steps have to be run on the file system queue .
-
Let lock be the file ’s associated lock .
-
Let count be the file ’s shared lock count .
-
If lock is "
taken-shared":-
Decrease count by 1.
-
If count is 0, set lock to "
open".
-
-
Otherwise, set lock to "
open".
Note: These steps have to be run on the file system queue .
Note:
Locks
help
prevent
concurrent
modifications
to
a
file.
A
FileSystemWritableFileStream
requires
a
shared
lock,
while
a
FileSystemSyncAccessHandle
requires
an
exclusive
one.
A directory entry additionally consists of a set of children , which are themselves file system entries . Each member is either a file entry or a directory entry .
A file system entry entry should be contained in the children of at most one directory entry , and that directory entry is also known as entry ’s parent . A file system entry 's parent is null if no such directory entry exists.
Note: Two different file system entries can represent the same file or directory on disk, in which case it is possible for both entries to have a different parent, or for one entry to have a parent while the other entry does not have a parent.
File system entries can (but don’t have to) be backed by files on the host operating system’s local file system, so it is possible for the binary data , modification timestamp , and children of entries to be modified by applications outside of this specification. Exactly how external changes are reflected in the data structures defined by this specification, as well as how changes made to the data structures defined here are reflected externally is left up to individual user-agent implementations.
A file system entry a is the same entry as a file system entry b if a is equal to b , or if a and b are backed by the same file or directory on the local file system.
Enqueue
the
following
steps
to
the
file
system
queue
:
If
child
’s
locator
's
root
is
not
root
’s
locator
's
root
,
resolve
result
with
null,
and
abort
these
steps.
Let
childPath
be
child
’s
locator
's
path
.
Let
rootPath
be
root
’s
locator
's
path
.
If
childPath
is
the
same
path
as
rootPath
,
resolve
result
with
«
»,
and
abort
these
steps.
If
rootPath
’s
size
is
greater
than
childPath
’s
size
,
resolve
result
with
null,
and
abort
these
steps.
2.1.3.
For
each
index
of
rootPath
’s
indices
:
If
rootPath
.\[[
index
]]
is
not
childPath
.\[[
index
]],
then
resolve
result
with
null,
and
abort
these
steps.
Let
relativePath
be
«
».
File
System
Locator
For
each
index
of
the
range
from
rootPath
’s
size
to
rootPath
’s
size
,
exclusive,
append
childPath
.\[[
index
]]
to
relativePath
.
Resolve
result
with
relativePath
.
Return
result
.
A file system locator represents a potential location of a file system entry . A file system locator is either a file locator or a directory locator .
Each
file
system
locator
has
an
associated
path
(a
file
system
path
),
a
kind
(a
FileSystemHandleKind
),
and
a
root
file
system
(a
file
system
root
).
Consider giving each locator a storage bucket .
A
file
locator
is
a
file
system
locator
whose
kind
is
"
file
".
A
directory
locator
is
a
file
system
locator
whose
kind
is
"
directory
".
For
a
file
system
locator
locator
whichs
locates
to
a
file
entry
entry
that
conceptually
exists
at
the
path
data/drafts/example.txt
relative
to
the
root
directory
of
a
bucket
file
system
,
locator
’s
kind
has
to
be
"
file
",
locator
’s
path
has
to
be
«
"
data
",
"
drafts
",
"
example.txt
"
»,
and
locator
’s
root
file
system
might
include
relevant
identifying
information
such
as
the
storage
has
to
be
a
bucket
and
the
disk
drive.
file
system
.
A
file
system
locator
a
is
the
same
locator
as
a
file
system
locator
b
if
a
’s
kind
is
b
’s
kind
,
a
’s
root
file
system
is
b
’s
root
file
system
,
and
a
’s
path
is
the
same
path
as
b
’s
path
.
To
resolve
algorithm
given
a
file
system
locator
locator
child
runs
an
implementation-defined
relative
to
a
file
system
locator
series
root
:
Let relationship be the result of
steps adhering to these constraints:getting the relationship between root and child .-
If
locatorrelationship isa file locator , they return a file entryequal to "other" or "ancestor", return null. -
If
locatorrelationship isa directory locator , theyequal to "self", returna directory entry or null.« ». -
If these steps return a non-nullLetentry , then:childPath be child ’s path . -
Getting the locator withLetentryrootPathreturnsbelocator , provided no intermediate file system operations were run.root ’s path . -
Let
entryrelativePath’s namebe « ». For each
isindex of thelast itemrangeoffromlocatorrootPath ’spathsize to childPath ’s size , exclusive, append childPath [ index ] to relativePath .Return relativePath .
Note: These steps have to be run on the file system queue .
-
If
entryself ’s file system isanot related ’s fileentrysystem ,theyreturna file locator ."other". -
IfLetentryselfPathis a directory entry , they return a directory locatorbe self ’s path . -
If these steps returnLetlocator , then:relatedPath be related ’s path . -
Locating an entry withLetlocatorselfPathSizereturnsbeentry , provided no intermediate file system operations were run.selfPath ’s size . -
Let
entryrelatedPathSize’s name is the last item ofbelocatorrelatedPath ’spathsize . -
A file system path is a listFor each index ofone or more strings . This may be a virtual path thatselfPath ’s indices :If index is
mapped to real location on disk or in memory, may correspond directly to a path on the local file system,greater than ormay not correspondequal toany file on disk at all. The actual physical location of the corresponding file system entry is implementation-defined .relatedPathSize , return "ancestor".Let-
If
pathselfPathbe the list «[ index ] is not relatedPath [ index ], return "dataother",".
If selfPathSize equals relatedPathSize , return "
draftsself",".If selfPathSize + 1 equals relatedPathSize , return "
".example.txtdirect childReturn "
». There is no expectation that a file namedexample.txtdescendantexists anywhere".
Note:
These
steps
have
to
be
run
on
disk.
the
file
system
queue
.
-
ALet fileSystem be locator ’s file system . -
Let path be locator ’s path .
-
Let entry be the result of running fileSystem ’s locate an entry given
apath . If entry is null, return null.
Return entry .
Let
afileSystem be entry ’ssize isfile system .Let path be the
same asresult of runningbfileSystem ’ssizeget the pathand for eachgiven entry .Let locator be a file system locator whose path is
indexpathofand whose file system is fileSystem .If entry is a file entry , set locator ’s
indiceskind to "file".If
a .\[[ indexentry]]is a directory entry , setb .\[[ indexlocator]].’s kind to "directory".Return entry .
The contents of a file system locator , including its path , are not expected to be shared in their entirety with the website process. The file system path might contain components which are not known to the website unless the file system locator is later resolved relative to a parent directory locator .
2.2.
The
FileSystemHandle
interface
enum {FileSystemHandleKind ,"file" , }; ["directory" Exposed =(Window ,Worker ),SecureContext ,Serializable ]interface {FileSystemHandle ;readonly attribute FileSystemHandleKind kind ;readonly attribute USVString name ;);Promise <boolean >isSameEntry (FileSystemHandle ); };other
A
FileSystemHandle
object
is
associated
with
a
locator
(a
file
system
locator
).
Note:
Multiple
FileSystemHandle
objects
can
have
the
same
file
system
locator
.
FileSystemHandle
given
a
file
system
locator
locator
in
a
Realm
realm
:Let kind be locator ’s kind .
Let fileSystem be locator ’s file system .
-
Let path be locator ’s path .
-
If kind is "
file":Return the result of creating a new
FileSystemFileHandlegiven fileSystem and path in realm .
Otherwise:
Return the result of creating a new
FileSystemDirectoryHandlegiven fileSystem and path in realm .
A
FileSystemHandle
is
in
a
bucket
file
system
if
the
first
item
of
its
locator
's
path
is
the
empty
string.
Note:
This
is
a
bit
magical,
but
it
works
since
only
the
root
directory
of
a
bucket
file
system
can
have
a
path
which
contains
an
empty
string.
See
getDirectory()
.
All
other
item
s
of
a
path
will
be
a
valid
file
name
.
Consider improving this situation by giving each locator a storage bucket .
FileSystemHandle
objects
are
serializable
objects
.
Their serialization steps , given value , serialized and forStorage are:
-
Set serialized .[[Origin]] to value ’s relevant settings object 's origin .
-
Set serialized .[[Locator]] to value ’s locator .
-
If serialized .[[Origin]] is not same origin with value ’s relevant settings object 's origin , then throw a "
DataCloneError"DOMException. -
Set value ’s locator to serialized .[[Locator]].
-
handle
.
kind -
Returns "
file" if handle is aFileSystemFileHandle, or "directory" if handle is aFileSystemDirectoryHandle.This can be used to distinguish files from directories when iterating over the contents of a directory.
-
handle
.
name -
Returns the last path component of handle ’s locator 's path .
The
kind
getter
steps
are
to
return
this
's
locator
's
kind
.
The
name
getter
steps
are
to
return
the
last
item
(a
string
)
of
this
's
locator
's
path
.
2.2.1.
The
isSameEntry()
method
-
same
=
await
handle1
.
isSameEntry( handle2 ) -
Returns true if handle1 and handle2 represent the same file or directory.
isSameEntry(
other
)
method
steps
are:
-
Let realm be this 's relevant Realm .
-
Let p be a new promise in realm .
-
Return p .
2.3.
The
FileSystemFileHandle
interface
dictionary {FileSystemCreateWritableOptions boolean =keepExistingData false ; }; [Exposed =(Window ,Worker ),SecureContext ,Serializable ]{interface :FileSystemFileHandle FileSystemHandle {Promise <File >getFile ();Promise <FileSystemWritableFileStream >createWritable (optional FileSystemCreateWritableOptions = {}); [options Exposed =DedicatedWorker ]Promise <FileSystemSyncAccessHandle >createSyncAccessHandle (); };
Note:
A
FileSystemFileHandle
's
associated
locator
's
kind
is
"
file
".
FileSystemFileHandle
given
a
directory
locator
parentLocator
and
a
string
name
in
a
Realm
realm
:
-
Let handle be a new
FileSystemFileHandlein realm . -
Let childType be "
file". -
Let
childRootchildFileSystem bea copy ofthe parentLocator ’srootfile system . -
Let childPath be the result of cloning parentLocator ’s path and appending name .
-
Set handle ’s locator to a file system locator whose kind is childType ,
rootfile system ischildRootchildFileSystem , and path is childPath . -
Return handle .
FileSystemFileHandle
given
a
file
system
-
Let handle be a new
FileSystemFileHandlein realm . -
Set handle ’s locator to a file system locator whose kind is "
file",rootfile system isrootfileSystem , and path is path . -
Return handle .
FileSystemFileHandle
objects
are
serializable
objects
.
Their
serialization
steps
and
deserialization
steps
are
the
same
as
those
for
FileSystemHandle
.
2.3.1.
The
getFile()
method
getFile()
method
steps
are:
-
Let result be a new promise .
-
Let global be this 's relevant global object .
-
Enqueue the following steps to the file system queue :
-
Let entry be the result of locating an entry given locator .
-
Let accessResult be the result of running entry ’s query access given "
read". -
Queue a storage task with global to run these steps:
-
If accessResult ’s permission state is not "
granted", reject result with aDOMExceptionof accessResult ’s error name and abort these steps. -
If entry is null, reject result with a "
NotFoundError"DOMExceptionand abort these steps. -
Assert : entry is a file entry .
-
Let f be a new
File. -
Set f ’s snapshot state to the current state of entry .
-
Set f ’s underlying byte sequence to a copy of entry ’s binary data .
-
Set f ’s
lastModifiedto entry ’s modification timestamp . -
Set f ’s
typeto an implementation-defined value, based on for example entry ’s name or its file extension.The reading and snapshotting behavior needs to be better specified in the [FILE-API] spec, for now this is kind of hand-wavy.
-
Resolve result with f .
-
-
-
Return result .
2.3.2.
The
createWritable()
method
-
stream
=
await
fileHandle
.
createWritable()- stream = await fileHandle .
createWritable({keepExistingData: true/false }) - stream = await fileHandle .
-
Returns a
FileSystemWritableFileStreamthat can be used to write to the file. Any changes made through stream won’t be reflected in the file entry locatable by fileHandle ’s locator until the stream has been closed. User agents try to ensure that no partial writes happen, i.e. the file will either contain its old contents or it will contain whatever data was written through stream up until the stream has been closed.This is typically implemented by writing data to a temporary file, and only replacing the file entry locatable by fileHandle ’s locator with the temporary file when the writable filestream is closed.
If
keepExistingDatais false or not specified, the temporary file starts out empty, otherwise the existing file is first copied to this temporary file.Creating a
FileSystemWritableFileStreamtakes a shared lock on the file entry locatable with fileHandle ’s locator . This prevents the creation ofFileSystemSyncAccessHandlesfor the entry, until the stream is closed.
See
WICG/file-system-access
issue
#67
for
discussion
around
and
desire
for
a
"inPlace"
mode
for
createWritable
(where
changes
will
be
written
to
the
actual
underlying
file
as
they
are
written
to
the
writer,
for
example
to
support
in-place
modification
of
large
files
or
things
like
databases).
This
is
not
currently
implemented
in
Chrome.
Implementing
this
is
currently
blocked
on
figuring
out
how
to
combine
the
desire
to
run
malware
checks
with
the
desire
to
let
websites
make
fast
in-place
modifications
to
existing
large
files.
In-place
writes
are
available
for
files
in
a
bucket
file
system
via
the
FileSystemSyncAccessHandle
interface.
createWritable(
options
)
method
steps
are:
-
Let result be a new promise .
-
Let realm be this 's relevant Realm .
-
Let global be this 's relevant global object .
-
Enqueue the following steps to the file system queue :
-
Let entry be the result of locating an entry given locator .
-
Let accessResult be the result of running entry ’s request access given "
readwrite". -
If accessResult ’s permission state is not "
granted", queue a storage task with global to reject result with aDOMExceptionof accessResult ’s error name and abort these steps. -
If entry is
null, queue a storage task with global to reject result with a "NotFoundError"DOMExceptionand abort these steps. -
Assert : entry is a file entry .
-
Let lockResult be the result of taking a lock with "
shared" on entry . -
Queue a storage task with global to run these steps:
-
If lockResult is "
failure", reject result with a "NoModificationAllowedError"DOMExceptionand abort these steps. -
Let stream be the result of creating a new
FileSystemWritableFileStreamfor entry in realm . -
If options ["
keepExistingData"] is true:-
Set stream ’s [[buffer]] to a copy of entry ’s binary data .
-
-
Resolve result with stream .
-
-
-
Return result .
2.3.3.
The
createSyncAccessHandle()
method
-
handle
=
await
fileHandle
.
createSyncAccessHandle() -
Returns a
FileSystemSyncAccessHandlethat can be used to read from/write to the file. Changes made through handle might be immediately reflected in the file entry locatable by fileHandle ’s locator . To ensure the changes are reflected in this file, the handle can be flushed.Creating a
FileSystemSyncAccessHandletakes an exclusive lock on the file entry locatable with fileHandle ’s locator . This prevents the creation of furtherFileSystemSyncAccessHandlesorFileSystemWritableFileStreamsfor the entry, until the access handle is closed.The returned
FileSystemSyncAccessHandleoffers synchronous methods. This allows for higher performance on contexts where asynchronous operations come with high overhead, e.g., WebAssembly.For the time being, this method will only succeed when the fileHandle is in a bucket file system .
createSyncAccessHandle()
method
steps
are:
-
Let result be a new promise .
-
Let realm be this 's relevant Realm .
-
Let global be this 's relevant global object .
-
Let isInABucketFileSystem be true if this is in a bucket file system ; otherwise false.
-
Enqueue the following steps to the file system queue :
-
Let entry be the result of locating an entry given locator .
-
Let accessResult be the result of running entry ’s request access given "
readwrite". -
If accessResult ’s permission state is not "
granted", queue a storage task with global to reject result with aDOMExceptionof accessResult ’s error name and abort these steps. -
If isInABucketFileSystem is false, queue a storage task with global to reject result with an "
InvalidStateError"DOMExceptionand abort these steps. -
If entry is
null, queue a storage task with global to reject result with a "NotFoundError"DOMExceptionand abort these steps. -
Assert : entry is a file entry .
-
Let lockResult be the result of taking a lock with "
exclusive" on entry . -
Queue a storage task with global to run these steps:
-
If lockResult is "
failure", reject result with a "NoModificationAllowedError"DOMExceptionand abort these steps. -
Let handle be the result of creating a new
FileSystemSyncAccessHandlefor entry in realm . -
Resolve result with handle .
-
-
-
Return result .
2.4.
The
FileSystemDirectoryHandle
interface
dictionary {FileSystemGetFileOptions boolean =create false ; };dictionary {FileSystemGetDirectoryOptions boolean =create false ; };dictionary {FileSystemRemoveOptions boolean =recursive false ; }; [Exposed =(Window ,Worker ),SecureContext ,Serializable ]{ >;interface :FileSystemDirectoryHandle FileSystemHandle {async iterable <USVString ,FileSystemHandle >;Promise <FileSystemFileHandle >getFileHandle (USVString ,name optional FileSystemGetFileOptions = {});options Promise <FileSystemDirectoryHandle >getDirectoryHandle (USVString ,name optional FileSystemGetDirectoryOptions = {});options Promise <undefined >removeEntry (USVString ,name optional FileSystemRemoveOptions = {});options );Promise <sequence <USVString >?>resolve (FileSystemHandle ); };possibleDescendant
Note:
A
FileSystemDirectoryHandle
's
associated
locator
's
kind
is
"
directory
".
FileSystemDirectoryHandle
given
a
directory
locator
parentLocator
and
a
string
name
in
a
Realm
realm
:
-
Let handle be a new
FileSystemDirectoryHandlein realm . -
Let childType be "
directory". -
Let
childRootchildFileSystem bea copy ofthe parentLocator ’srootfile system . -
Let childPath be the result of cloning parentLocator ’s path and appending name .
-
Set handle ’s locator to a file system locator whose kind is childType ,
rootfile system ischildRootchildFileSystem , and path is childPath . -
Return handle .
FileSystemDirectoryHandle
given
a
file
system
-
Let handle be a new
FileSystemDirectoryHandlein realm . -
Set handle ’s locator to a file system locator whose kind is "
directory",rootfile system isrootfileSystem , and path is path . -
Return handle .
FileSystemDirectoryHandle
objects
are
serializable
objects
.
Their
serialization
steps
and
deserialization
steps
are
the
same
as
those
for
FileSystemHandle
.
2.4.1. Directory iteration
-
for
await
(let
[
name
,
handle
]
of
directoryHandle
)
{}
- for await (let [ name , handle ] of directoryHandle . entries()) {}
- for await (let handle of directoryHandle . values()) {}
- for await (let name of directoryHandle . keys()) {}
- for await (let [ name , handle ] of directoryHandle . entries()) {}
-
Iterates over all entries whose parent is the directory entry locatable by directoryHandle ’s locator . Entries that are created or deleted while the iteration is in progress might or might not be included. No guarantees are given either way.
In the future we might want to add arguments to the async iterable declaration to support for example recursive iteration.
FileSystemDirectoryHandle
handle
and
its
async
iterator
iterator
are:
-
Set iterator ’s past results to an empty set .
FileSystemDirectoryHandle
handle
and
its
async
iterator
iterator
:
-
Let promise be a new promise .
-
Enqueue the following steps to the file system queue :
-
Let directory be the result of locating an entry given handle ’s locator .
-
Let accessResult be the result of running directory ’s query access given "
read". -
Queue a storage task with handle ’s relevant global object to run these steps:
-
If accessResult ’s permission state is not "
granted", reject promise with aDOMExceptionof accessResult ’s error name and abort these steps.: -
If directory is
null, reject result with a "NotFoundError"DOMExceptionand abort these steps.-
Assert : directory is a directory entry .
-
-
Let child be a file system entry in directory ’s children , such that child ’s name is not contained in iterator ’s past results , or
nullif no such entry exists.Note: This is intentionally very vague about the iteration order. Different platforms and file systems provide different guarantees about iteration order, and we want it to be possible to efficiently implement this on all platforms. As such no guarantees are given about the exact order in which elements are returned.
-
If child is
null, resolve promise withundefinedand abort these steps. -
Append child ’s name to iterator ’s past results .
-
If child is a file entry :
-
Let result be the result of creating a child
FileSystemFileHandlewith handle ’s locator and child ’s name in handle ’s relevant Realm .
-
-
Otherwise:
-
Let result be the result of creating a child
FileSystemDirectoryHandlewith handle ’s locator and child ’s name in handle ’s relevant Realm .
-
-
-
-
Return promise .
2.4.2.
The
getFileHandle()
method
-
fileHandle
=
await
directoryHandle
.
getFileHandle( name )- fileHandle = await directoryHandle .
getFileHandle( name , {create: false }) - fileHandle = await directoryHandle .
-
Returns a handle for a file named name in the directory entry locatable by directoryHandle ’s locator . If no such file exists, this rejects.
-
fileHandle
=
await
directoryHandle
.
getFileHandle( name , {create: true }) -
Returns a handle for a file named name in the directory entry locatable by directoryHandle ’s locator . If no such file exists, this creates a new file. If no file with named name can be created this rejects. Creation can fail because there already is a directory with the same name, because the name uses characters that aren’t supported in file names on the underlying file system, or because the user agent for security reasons decided not to allow creation of the file.
This operation requires write permission, even if the file being returned already exists. If this handle doesn’t already have write permission, this could result in a prompt being shown to the user. To get an existing file without needing write permission, call this method with
{.create: false }
getFileHandle(
name
,
options
)
method
steps
are:
-
Let result be a new promise .
-
Let realm be this 's relevant Realm .
-
Let global be this 's relevant global object .
-
Enqueue the following steps to the file system queue :
-
If name is not a valid file name , queue a storage task with global to reject result with a
TypeErrorand abort these steps. -
Let entry be the result of locating an entry given locator .
-
If options ["
create"] is true:-
Let accessResult be the result of running entry ’s request access given "
readwrite".
-
-
Otherwise:
-
Let accessResult be the result of running entry ’s query access given "
read".
-
-
Queue a storage task with global to run these steps:
-
If accessResult ’s permission state is not "
granted", reject result with aDOMExceptionof accessResult ’s error name and abort these steps. -
If entry is
null, reject result with a "NotFoundError"DOMExceptionand abort these steps. -
Assert : entry is a directory entry .
-
For each child of entry ’s children :
-
If child ’s name equals name :
-
If child is a directory entry :
-
Reject result with a "
TypeMismatchError"DOMExceptionand abort these steps.
-
-
Resolve result with the result of creating a child
FileSystemFileHandlewith locator and child ’s name in realm and abort these steps.
-
-
-
If options ["
create"] is false:-
Reject result with a "
NotFoundError"DOMExceptionand abort these steps.
-
-
Let child be a new file entry whose query access and request access algorithms are those of entry .
-
Set child ’s name to name .
-
Set child ’s binary data to an empty byte sequence .
-
Set child ’s modification timestamp to the current time.
-
If creating child in the underlying file system throws an exception, reject result with that exception and abort these steps.
Better specify what possible exceptions this could throw.
-
Resolve result with the result of creating a child
FileSystemFileHandlewith locator and child ’s name in realm .
-
-
-
Return result .
2.4.3.
The
getDirectoryHandle()
method
-
subdirHandle
=
await
directoryHandle
.
getDirectoryHandle( name )- subdirHandle = await directoryHandle .
getDirectoryHandle( name , {create: false }) - subdirHandle = await directoryHandle .
-
Returns a handle for a directory named name in the directory entry locatable by directoryHandle ’s locator . If no such directory exists, this rejects.
-
subdirHandle
=
await
directoryHandle
.
getDirectoryHandle( name , {create: true }) -
Returns a handle for a directory named name in the directory entry locatable by directoryHandle ’s locator . If no such directory exists, this creates a new directory. If creating the directory failed, this rejects. Creation can fail because there already is a file with the same name, or because the name uses characters that aren’t supported in file names on the underlying file system.
This operation requires write permission, even if the directory being returned already exists. If this handle doesn’t already have write permission, this could result in a prompt being shown to the user. To get an existing directory without needing write permission, call this method with
{.create: false }
getDirectoryHandle(
name
,
options
)
method
steps
are:
-
Let result be a new promise .
-
Let realm be this 's relevant Realm .
-
Let global be this 's relevant global object .
-
Enqueue the following steps to the file system queue :
-
If name is not a valid file name , queue a storage task with global to reject result with a
TypeErrorand abort these steps. -
Let entry be the result of locating an entry given locator .
-
If options ["
create"] is true:-
Let accessResult be the result of running entry ’s request access given "
readwrite".
-
-
Otherwise:
-
Let accessResult be the result of running entry ’s query access given "
read".
-
-
Queue a storage task with global to run these steps:
-
If accessResult ’s permission state is not "
granted", reject result with aDOMExceptionof accessResult ’s error name and abort these steps. -
If entry is
null, reject result with a "NotFoundError"DOMExceptionand abort these steps. -
Assert : entry is a directory entry .
-
For each child of entry ’s children :
-
If child ’s name equals name :
-
If child is a file entry :
-
Reject result with a "
TypeMismatchError"DOMExceptionand abort these steps.
-
-
Resolve result with the result of creating a child
FileSystemDirectoryHandlewith locator and child ’s name in realm and abort these steps.
-
-
-
If options ["
create"] is false:-
Reject result with a "
NotFoundError"DOMExceptionand abort these steps.
-
-
Let child be a new directory entry whose query access and request access algorithms are those of entry .
-
Set child ’s name to name .
-
If creating child in the underlying file system throws an exception, reject result with that exception and abort these steps.
Better specify what possible exceptions this could throw.
-
Resolve result with the result of creating a child
FileSystemDirectoryHandlewith locator and child ’s name in realm .
-
-
-
Return result .
2.4.4.
The
removeEntry()
method
-
await
directoryHandle
.
removeEntry( name )- await directoryHandle .
removeEntry( name , {recursive: false }) - await directoryHandle .
-
If the directory entry locatable by directoryHandle ’s locator contains a file named name , or an empty directory named name , this will attempt to delete that file or directory.
Attempting to delete a file or directory that does not exist is considered success, while attempting to delete a non-empty directory will result in a promise rejection.
-
await
directoryHandle
.
removeEntry( name , {recursive: true }) -
Removes the file system entry named name in the directory entry locatable by directoryHandle ’s locator . If that entry is a directory, its contents will also be deleted recursively.
Attempting to delete a file or directory that does not exist is considered success.
removeEntry(
name
,
options
)
method
steps
are:
-
Let result be a new promise .
-
Let global be this 's relevant global object .
-
Enqueue the following steps to the file system queue :
-
If name is not a valid file name , queue a storage task with global to reject result with a
TypeErrorand abort these steps. -
Let entry be the result of locating an entry given locator .
-
Let accessResult be the result of running entry ’s request access given "
readwrite". -
Queue a storage task with global to run these steps:
-
If accessResult ’s permission state is not "
granted", reject result with aDOMExceptionof accessResult ’s error name and abort these steps. -
If entry is
null, reject result with a "NotFoundError"DOMExceptionand abort these steps. -
Assert : entry is a directory entry .
-
For each child of entry ’s children :
-
If child ’s name equals name :
-
If child is a directory entry :
-
If child ’s children is not empty and options ["
recursive"] is false:-
Reject result with an "
InvalidModificationError"DOMExceptionand abort these steps.
-
-
-
If removing child in the underlying file system throws an exception, reject result with that exception and abort these steps.
Note: If
recursiveis true, the removal can fail non-atomically. Some files or directories might have been removed while other files or directories still exist.Better specify what possible exceptions this could throw.
-
Resolve result with
undefined.
-
-
-
Reject result with a "
NotFoundError"DOMException.
-
-
-
Return result .
2.4.5.
The
resolve()
method
-
path
=
await
directory
.
resolve( child ) -
If child is equal to directory , path will be an empty array.
If child is a direct child of directory , path will be an array containing child ’s name.
If child is a descendant of directory , path will be an array containing the names of all the intermediate directories and child ’s name as last element. For example if directory represents
/home/user/projectand child represents/home/user/project/foo/bar, this will return['foo', 'bar'].Otherwise ( directory and child are not related), path will be null.
// Assume we at some point got a valid directory handle. const dir_ref= current_project_dir; if ( ! dir_ref) return ; // Now get a file reference: const file_ref= await dir_ref. getFileHandle( filename, { create: true }); // Check if file_ref exists inside dir_ref: const relative_path= await dir_ref. resolve( file_ref); if ( relative_path=== null ) { // Not inside dir_ref. } else { // relative_path is an array of names, giving the relative path // from dir_ref to the file that is represented by file_ref: assert relative_path. pop() === file_ref. name; let entry= dir_ref; for ( const nameof relative_path) { entry= await entry. getDirectory( name); } entry= await entry. getFile( file_ref. name); // Now |entry| will represent the same file on disk as |file_ref|. assertawait entry. isSameEntry( file_ref) === true ; }
resolve(
possibleDescendant
)
method
steps
Let result be a new promise .
Let global be this 's relevant global object .
Enqueue the following steps to
returnthe file system queue :Return result .
2.5.
The
FileSystemWritableFileStream
interface
enum {WriteCommandType ,"write" ,"seek" , };"truncate" dictionary {WriteParams required WriteCommandType ;type unsigned long long ?;size unsigned long long ?; (position BufferSource or Blob or USVString )?; };data typedef (BufferSource or Blob or USVString or WriteParams ); [FileSystemWriteChunkType Exposed =(Window ,Worker ),SecureContext ]interface :FileSystemWritableFileStream WritableStream {Promise <undefined >write (FileSystemWriteChunkType );data Promise <undefined >seek (unsigned long long );position Promise <undefined >truncate (unsigned long long ); };size
A
FileSystemWritableFileStream
has
an
associated
[[file]]
(a
file
entry
).
A
FileSystemWritableFileStream
has
an
associated
[[buffer]]
(a
byte
sequence
).
It
is
initially
empty.
Note: This buffer can get arbitrarily large, so it is expected that implementations will not keep this in memory, but instead use a temporary file for this. All access to [[buffer]] is done in promise returning methods and algorithms, so even though operations on it seem sync, implementations can implement them async.
A
FileSystemWritableFileStream
has
an
associated
[[seekOffset]]
(a
number).
It
is
initially
0.
FileSystemWritableFileStream
object
is
a
WritableStream
object
with
additional
convenience
methods,
which
operates
on
a
single
file
on
disk.
Upon creation, an underlying sink will have been created and the stream will be usable. All operations executed on the stream are queuable and producers will be able to respond to backpressure.
The
underlying
sink’s
write
method,
and
therefore
WritableStreamDefaultWriter’s
write()
method,
will
accept
byte-like
data
or
WriteParams
as
input.
The
FileSystemWritableFileStream
has
a
file
position
cursor
initialized
at
byte
offset
0
from
the
top
of
the
file.
When
using
write()
or
by
using
WritableStream
capabilities
through
the
WritableStreamDefaultWriter’s
write()
method,
this
position
will
be
advanced
based
on
the
number
of
bytes
written
through
the
stream
object.
Similarly,
when
piping
a
ReadableStream
into
a
FileSystemWritableFileStream
object,
this
position
is
updated
with
the
number
of
bytes
that
passed
through
the
stream.
getWriter()
returns
an
instance
of
WritableStreamDefaultWriter
.
FileSystemWritableFileStream
given
a
file
entry
file
in
a
Realm
realm
:
-
Let stream be a new
FileSystemWritableFileStreamin realm . -
Set stream ’s [[file]] to file .
-
Let writeAlgorithm be an algorithm which takes a chunk argument and returns the result of running the write a chunk algorithm with stream and chunk .
-
Let closeAlgorithm be these steps:
-
Let closeResult be a new promise .
-
Enqueue the following steps to the file system queue :
-
Let accessResult be the result of running file ’s query access given "
readwrite". -
Queue a storage task with file ’s relevant global object to run these steps:
-
If accessResult ’s permission state is not "
granted", reject closeResult with aDOMExceptionof accessResult ’s error name and abort these steps. -
Run implementation-defined malware scans and safe browsing checks. If these checks fail, reject closeResult with an "
AbortError"DOMExceptionand abort these steps. -
Set stream ’s [[file]] 's binary data to stream ’s [[buffer]] . If that throws an exception, reject closeResult with that exception and abort these steps.
Note: It is expected that this atomically updates the contents of the file on disk being written to.
-
Enqueue the following steps to the file system queue :
-
Release the lock on stream ’s [[file]] .
-
Queue a storage task with file ’s relevant global object to resolve closeResult with
undefined.
-
-
-
-
Return closeResult .
-
-
Let abortAlgorithm be these steps:
-
Enqueue this step to the file system queue :
-
Release the lock on stream ’s [[file]] .
-
-
-
Let highWaterMark be 1.
-
Let sizeAlgorithm be an algorithm that returns
1. -
Set up stream with writeAlgorithm set to writeAlgorithm , closeAlgorithm set to closeAlgorithm , abortAlgorithm set to abortAlgorithm , highWaterMark set to highWaterMark , and sizeAlgorithm set to sizeAlgorithm .
-
Return stream .
FileSystemWritableFileStream
stream
and
chunk
,
runs
these
steps:
-
Let input be the result of converting chunk to a
FileSystemWriteChunkType. If this throws an exception, then return a promise rejected with that exception. -
Let p be a new promise .
-
Enqueue the following steps to the file system queue :
-
Let accessResult be the result of running stream ’s [[file]] 's query access given "
readwrite". -
Queue a storage task with stream ’s relevant global object to run these steps:
-
If accessResult ’s permission state is not "
granted", reject p with aDOMExceptionof accessResult ’s error name and abort these steps. -
Let command be input ["
type"] if input is a dictionary ; otherwise "write". -
If command is "
write":-
If input is
undefinedor input is a dictionary and input ["data"] does not exist , reject p with aTypeErrorand abort these steps. -
Let data be input ["
data"] if input is a dictionary ; otherwise input . -
Let writePosition be stream ’s [[seekOffset]] .
-
If input is a dictionary and input ["
position"] exists , set writePosition to input ["position"]. -
Let oldSize be stream ’s [[buffer]] 's length .
-
If data is a
BufferSource, let dataBytes be a copy of data . -
Otherwise, if data is a
Blob:-
Let dataBytes be the result of performing the read operation on data . If this throws an exception, reject p with that exception and abort these steps.
-
-
Otherwise:
-
Let dataBytes be the result of UTF-8 encoding data .
-
If writePosition is larger than oldSize , append writePosition - oldSize 0x00 (NUL) bytes to the end of stream ’s [[buffer]] .
Note: Implementations are expected to behave as if the skipped over file contents are indeed filled with NUL bytes. That doesn’t mean these bytes have to actually be written to disk and take up disk space. Instead most file systems support so called sparse files, where these NUL bytes don’t take up actual disk space.
-
Let head be a byte sequence containing the first writePosition bytes of stream ’s [[buffer]] .
-
Let tail be an empty byte sequence .
-
If writePosition + data ’s length is smaller than oldSize :
-
Let tail be a byte sequence containing the last oldSize - ( writePosition + data ’s length ) bytes of stream ’s [[buffer]] .
-
-
Set stream ’s [[buffer]] to the concatenation of head , data and tail .
-
If the operations modifying stream ’s [[buffer]] in the previous steps failed due to exceeding the storage quota , reject p with a "
QuotaExceededError"DOMExceptionand abort these steps, leaving stream ’s [[buffer]] unmodified.Note: Storage quota only applies to files stored in a bucket file system . However this operation could still fail for other files, for example if the disk being written to runs out of disk space.
-
Set stream ’s [[seekOffset]] to writePosition + data ’s length .
-
Resolve p .
-
-
Otherwise, if command is "
seek":-
Assert : chunk is a dictionary .
-
If chunk ["
position"] does not exist , reject p with aTypeErrorand abort these steps. -
Set stream ’s [[seekOffset]] to chunk ["
position"]. -
Resolve p .
-
-
Otherwise, if command is "
truncate":-
Assert : chunk is a dictionary .
-
If chunk ["
size"] does not exist , reject p with aTypeErrorand abort these steps. -
Let newSize be chunk ["
size"]. -
Let oldSize be stream ’s [[buffer]] 's length .
-
If newSize is larger than oldSize :
-
Set stream ’s [[buffer]] to a byte sequence formed by concating stream ’s [[buffer]] with a byte sequence containing newSize - oldSize
0x00bytes. -
If the operation in the previous step failed due to exceeding the storage quota , reject p with a "
QuotaExceededError"DOMExceptionand abort these steps, leaving stream ’s [[buffer]] unmodified.Note: Storage quota only applies to files stored in a bucket file system . However this operation could still fail for other files, for example if the disk being written to runs out of disk space.
-
-
Otherwise, if newSize is smaller than oldSize :
-
Set stream ’s [[buffer]] to a byte sequence containing the first newSize bytes in stream ’s [[buffer]] .
-
-
If stream ’s [[seekOffset]] is bigger than newSize , set stream ’s [[seekOffset]] to newSize .
-
Resolve p .
-
-
-
-
Return p .
2.5.1.
The
write()
method
-
await
stream
.
write( data )- await stream .
write({type: "write",data: data }) - await stream .
-
Writes the content of data into the file associated with stream at the current file cursor offset.
No changes are written to the actual file on disk until the stream has been closed. Changes are typically written to a temporary file instead.
-
await
stream
.
write({type: "write",position: position ,data: data }) -
Writes the content of data into the file associated with stream at position bytes from the top of the file. Also updates the current file cursor offset to the end of the written data.
No changes are written to the actual file on disk until the stream has been closed. Changes are typically written to a temporary file instead.
-
await
stream
.
write({type: "seek",position: position }) -
Updates the current file cursor offset the position bytes from the top of the file.
-
await
stream
.
write({type: "truncate",size: size }) -
Resizes the file associated with stream to be size bytes long. If size is larger than the current file size this pads the file with null bytes, otherwise it truncates the file.
The file cursor is updated when
truncateis called. If the cursor is smaller than size , it remains unchanged. If the cursor is larger than size , it is set to size to ensure that subsequent writes do not error.No changes are written to the actual file until on disk until the stream has been closed. Changes are typically written to a temporary file instead.
write(
data
)
method
steps
are:
-
Let writer be the result of getting a writer for this .
-
Let result be the result of writing a chunk to writer given data .
-
Release writer .
-
Return result .
2.5.2.
The
seek()
method
-
await
stream
.
seek( position ) -
Updates the current file cursor offset the position bytes from the top of the file.
seek(
position
)
method
steps
are:
-
Let writer be the result of getting a writer for this .
-
Let result be the result of writing a chunk to writer given «[ "
type" → "seek", "position" → position ]». -
Release writer .
-
Return result .
2.5.3.
The
truncate()
method
-
await
stream
.
truncate( size ) -
Resizes the file associated with stream to be size bytes long. If size is larger than the current file size this pads the file with null bytes, otherwise it truncates the file.
The file cursor is updated when
truncateis called. If the cursor is smaller than size , it remains unchanged. If the cursor is larger than size , it is set to size to ensure that subsequent writes do not error.No changes are written to the actual file until on disk until the stream has been closed. Changes are typically written to a temporary file instead.
truncate(
size
)
method
steps
are:
-
Let writer be the result of getting a writer for this .
-
Let result be the result of writing a chunk to writer given «[ "
type" → "truncate", "size" → size ]». -
Release writer .
-
Return result .
2.6.
The
FileSystemSyncAccessHandle
interface
dictionary { [FileSystemReadWriteOptions EnforceRange ]unsigned long long ; }; [at Exposed =DedicatedWorker ,SecureContext ]interface {FileSystemSyncAccessHandle unsigned long long (read AllowSharedBufferSource ,buffer optional FileSystemReadWriteOptions = {});options unsigned long long (write AllowSharedBufferSource ,buffer optional FileSystemReadWriteOptions = {});options undefined truncate ([EnforceRange ]unsigned long long );newSize unsigned long long getSize ();undefined flush ();undefined close (); };
A
FileSystemSyncAccessHandle
has
an
associated
[[file]]
(a
file
entry
).
A
FileSystemSyncAccessHandle
has
an
associated
[[state]]
,
a
string
that
may
exclusively
be
"
open
"
or
"
closed
".
A
FileSystemSyncAccessHandle
is
an
object
that
is
capable
of
reading
from/writing
to,
as
well
as
obtaining
and
changing
the
size
of,
a
single
file.
A
FileSystemSyncAccessHandle
offers
synchronous
methods.
This
allows
for
higher
performance
on
contexts
where
asynchronous
operations
come
with
high
overhead,
e.g.,
WebAssembly.
A
FileSystemSyncAccessHandle
has
a
file
position
cursor
initialized
at
byte
offset
0
from
the
top
of
the
file.
FileSystemSyncAccessHandle
given
a
file
entry
file
in
a
Realm
realm
:
-
Let handle be a new
FileSystemSyncAccessHandlein realm . -
Set handle ’s [[file]] to file .
-
Set handle ’s [[state]] to "
open". -
Return handle .
2.6.1.
The
read()
method
Specify how Access Handles should react when reading from a file that has been modified externally.
read(
buffer
,
FileSystemReadWriteOptions
:
options
)
method
steps
are:
-
If this 's [[state]] is "
closed", throw an "InvalidStateError"DOMException. -
Let bufferSize be buffer ’s byte length .
-
Let fileContents be this 's [[file]] 's binary data .
-
Let fileSize be fileContents ’s length .
-
Let readStart be options ["
at"] if options ["at"] exists ; otherwise this 's file position cursor . -
If the underlying file system does not support reading from a file offset of readStart , throw a
TypeError. -
If readStart is larger than fileSize :
-
Set this 's file position cursor to fileSize .
-
Return 0.
-
-
Let readEnd be readStart + ( bufferSize − 1).
-
If readEnd is larger than fileSize , set readEnd to fileSize .
-
Let bytes be a byte sequence containing the bytes from readStart to readEnd of fileContents .
-
Let result be bytes ’s length .
-
If the operations reading from fileContents in the previous steps failed:
-
If there were partial reads and the number of bytes that were read into bytes is known, set result to the number of read bytes.
-
Otherwise set result to 0.
-
-
Let arrayBuffer be buffer ’s underlying buffer .
-
Write bytes into arrayBuffer .
-
Set this 's file position cursor to readStart + result .
-
Return result .
2.6.2.
The
write()
method
-
handle
.
write( buffer )- handle .
write( buffer , {at}) - handle .
-
Writes the content of buffer into the file associated with handle , optionally at a given offset, and returns the number of written bytes. Checking the returned number of written bytes allows callers to detect and handle errors and partial writes.
The file cursor is updated when
writeis called to point to the byte after the last byte written.
write(
buffer
,
FileSystemReadWriteOptions
:
options
)
method
steps
are:
-
If this 's [[state]] is "
closed", throw an "InvalidStateError"DOMException. -
Let writePosition be options ["
at"] if options ["at"] exists ; otherwise this 's file position cursor . -
If the underlying file system does not support writing to a file offset of writePosition , throw a
TypeError. -
Let fileContents be a copy of this 's [[file]] 's binary data .
-
Let oldSize be fileContents ’s length .
-
Let bufferSize be buffer ’s byte length .
-
If writePosition is larger than oldSize , append writePosition − oldSize 0x00 (NUL) bytes to the end of fileContents .
Note: Implementations are expected to behave as if the skipped over file contents are indeed filled with NUL bytes. That doesn’t mean these bytes have to actually be written to disk and take up disk space. Instead most file systems support so called sparse files, where these NUL bytes don’t take up actual disk space.
-
Let head be a byte sequence containing the first writePosition bytes of fileContents .
-
Let tail be an empty byte sequence .
-
If writePosition + bufferSize is smaller than oldSize :
-
Set tail to a byte sequence containing the last oldSize − ( writePosition + bufferSize ) bytes of fileContents .
-
-
Let newSize be head ’s length + bufferSize + tail ’s length .
-
If newSize − oldSize exceeds the available storage quota , throw a "
QuotaExceededError"DOMException. -
Set this 's [[file]] 's binary data to the concatenation of head , the contents of buffer and tail .
Note: The mechanism used to access buffer’s contents is left purposely vague. It is likely that implementations will choose to focus on performance by issuing direct write calls to the host operating system (instead of creating a copy of buffer), which prevents a detailed specification of the write order and the results of partial writes.
-
If the operations modifying the this 's [[file]] 's binary data in the previous steps failed:
-
If there were partial writes and the number of bytes that were written from buffer is known:
-
Let bytesWritten be the number of bytes that were written from buffer .
-
Set this 's file position cursor to writePosition + bytesWritten .
-
Return bytesWritten .
-
-
Otherwise throw an "
InvalidStateError"DOMException.
-
-
Set this 's file position cursor to writePosition + bufferSize .
-
Return bufferSize .
2.6.3.
The
truncate()
method
-
handle
.
truncate( newSize ) -
Resizes the file associated with handle to be newSize bytes long. If newSize is larger than the current file size this pads the file with null bytes; otherwise it truncates the file.
The file cursor is updated when
truncateis called. If the cursor is smaller than newSize , it remains unchanged. If the cursor is larger than newSize , it is set to newSize .
truncate(
newSize
)
method
steps
are:
-
If this 's [[state]] is "
closed", throw an "InvalidStateError"DOMException. -
Let fileContents be a copy of this 's [[file]] 's binary data .
-
Let oldSize be the length of this 's [[file]] 's binary data .
-
If the underlying file system does not support setting a file’s size to newSize , throw a
TypeError. -
If newSize is larger than oldSize :
-
If newSize − oldSize exceeds the available storage quota , throw a "
QuotaExceededError"DOMException. -
Set this 's [[file]] 's to a byte sequence formed by concatenating fileContents with a byte sequence containing newSize − oldSize 0x00 bytes.
-
If the operations modifying the this 's [[file]] 's binary data in the previous steps failed, throw an "
InvalidStateError"DOMException.
-
-
Otherwise, if newSize is smaller than oldSize :
-
Set this 's [[file]] 's to a byte sequence containing the first newSize bytes in fileContents .
-
If the operations modifying the this 's [[file]] 's binary data in the previous steps failed, throw an "
InvalidStateError"DOMException.
-
-
If this 's file position cursor is greater than newSize , then set file position cursor to newSize .
2.6.4.
The
getSize()
method
-
handle
.
getSize() -
Returns the size of the file associated with handle in bytes.
getSize()
method
steps
are:
-
If this 's [[state]] is "
closed", throw an "InvalidStateError"DOMException. -
Return this 's [[file]] 's binary data 's length .
2.6.5.
The
flush()
method
flush()
method
steps
are:
-
If this 's [[state]] is "
closed", throw an "InvalidStateError"DOMException. -
Attempt to transfer all cached modifications of the file’s content to the file system’s underlying storage device.
Note: This is also known as flushing. This
maymight be a no-op on some file systems, such as in-memory file systems, which do not have a "disk" to flush to.
2.6.6.
The
close()
method
-
handle
.
close() -
Closes the access handle or no-ops if the access handle is already closed. This disables any further operations on it and releases the lock on the [[file]] associated with handle .
close()
method
steps
are:
-
Set lockReleased to false.
-
Enqueue the following steps to the file system queue :
-
Release the lock on file .
-
Set lockReleased to true.
-
-
Pause until lockReleased is true.
Note:
This
method
does
not
guarantee
that
all
file
modifications
will
be
immediately
reflected
in
the
underlying
storage
device.
Call
the
flush()
method
first
if
you
require
this
guarantee.
3. Accessing the Bucket File System
The
bucket
file
system
is
a
storage
endpoint
whose
identifier
is
"fileSystem"
,
types
are
«
"local"
»
,
and
quota
is
null.
The bucket file system implements file system with root as an implementation-defined opaque string .
Note: bucket file system 's root might include relevant identifying information such as the storage bucket .
Storage endpoints should be defined in [storage] itself, rather than being defined here. So merge this into the table there.
Note: While user agents will typically implement this by persisting the contents of a bucket file system to disk, it is not intended that the contents are easily user accessible. Similarly there is no expectation that files or directories with names matching the names of children of a bucket file system exist.
[SecureContext ]partial interface StorageManager {Promise <FileSystemDirectoryHandle >getDirectory (); };
-
directoryHandle
=
await
navigator
.
storage
.
getDirectory() -
Returns the root directory of the bucket file system .
getDirectory()
method
steps
are:
-
Let environment be the current settings object .
-
Let map be the result of running obtain a local storage bottle map with environment and
"fileSystem". If this returns failure, return a promise rejected with a "SecurityError"DOMException. -
If map ["root"] does not exist :
-
Let dir be a new directory entry whose query access and request access algorithms always return a file system access result with a permission state of "
granted" and with an error name of the empty string. -
Set dir ’s name to the empty string.
-
Set map ["root"] to dir .
-
-
Let
rootfileSystem bean implementation-definedthe relevant bucket file systemopaque string's root . -
Let path be « the empty string ».
-
Let handle be the result of creating a new
FileSystemDirectoryHandle. givenrootfileSystem and path in the current realm .Note: root might include relevant identifying information such as the storage bucket . -
Assert: locating an entry given handle ’s locator returns a directory entry that is the same entry as map ["root"].
-
Return a promise resolved with handle .
4. Observing file system change events
4.1.
The
FileSystemObserver
interface
dictionary {FileSystemObserverObserveOptions boolean =recursive false ; };callback =FileSystemObserverCallback undefined (sequence <FileSystemChangeRecord >,records FileSystemObserver ); [observer Exposed =(DedicatedWorker ,SharedWorker ,Window ),SecureContext ]interface {FileSystemObserver constructor (FileSystemObserverCallback );callback Promise <undefined >observe (FileSystemHandle ,handle optional FileSystemObserverObserveOptions = {});options undefined unobserve (FileSystemHandle );handle undefined disconnect (); };
FileSystemObserver
interface
can
be
used
to
observe
file
system
events
.
The
FileSystemObserver
has
an
asociated
FileSystemObserverCallback
callback
.
The
FileSystemObserver
has
an
associated
observerRegistrations
(a
map
of
file
system
locators
to
file
system
observer
registrations
).
4.1.1.
The
FileSystemObserver
constructor
new
FileSystemObserver(FileSystemObserverCallback
callback
)
steps
are:
Let this be a new
FileSystemObserver.Set this 's observerRegistrations be the empty map.
4.1.2.
The
observe()
method
The
observe(FileSystemHandle
handle
,
FileSystemObserverObserveOptions
options
)
steps
are:
Let result be a new promise .
Let recursive be options ["
recursive"].Let observerRegistrationMap be this 's observerRegistrations .
Let locator be handle ’s locator .
Let global be this 's relevant global object .
Enqueue the following steps to the file system queue :
If observerRegistrationMap [ locator ] exists , abort these steps.
Let entry be the result of locating an entry given locator .
Let accessResult be the result of running entry ’s query access given "
read".Queue a storage task with global to run these steps:
If accessResult ’s permission state is not "
granted", reject result with aDOMExceptionof accessResult ’s error name and abort these steps.If entry is null, reject result with a "
NotFoundError"DOMExceptionand abort these steps.Enqueue the following steps to the file system queue :
Create an observer registration for this on handle with recursive .
Resolve result with null.
4.1.3.
The
unobserve()
method
unobserve(FileSystemHandle
handle
)
steps
are:
Let locator be handle ’s locator .
Let observerRegistrationMap be this 's observerRegistrations .
4.1.4.
The
disconnect()
method
disconnect()
steps
are:
Let observerRegistrationMap be this 's observerRegistrations .
Enqueue the following steps to the file system queue :
Let observerRegistrations be the result of getting the values of observerRegistrationMap .
For each observerRegistration in observerRegistrations :
Destroy observerRegistration .
4.2.
The
FileSystemChangeRecord
interface
enum {FileSystemChangeType ,"appeared" ,"disappeared" ,"errored" ,"modified" ,"moved" , };"unknown" dictionary {FileSystemChangeRecord required FileSystemHandle ;root FileSystemHandle ?;changedHandle required sequence <DOMString >;relativePathComponents required FileSystemChangeType ;type sequence <DOMString >?; };relativePathMovedFrom
- root
The handle that was passed to FileSystemObserver.observe().
- changedHandle
The handle that the change occurred on. This is not set for "disappeared", "errored", and "unknown" events. It’s not given for "disappeared" since the handle would point to a non-existent folder or file. It’s not given for "errored" and "unknown" since it is meaningless in these contexts as these events are sent for the observation as a whole and not any one particular file or folder.
- relativePathComponents
The path of
changedHandlerelative toroot.- type
The type of change.
- relativePathMovedFrom
If
typeis "moved", this corresponds to the former path ofchangedHandlerelative toroot, if the former path is known; otherwise null.
FileSystemChangeType
indicate
the
following:
- "appeared"
The
changedHandlewas created or moved into the scope of theroot.- "disappeared"
The
changedHandlewas deleted or moved out of the scope of theroot.- "errored"
An error has occurred for this file system observer registration and it will no longer receive change events. For example, this might be sent for a Linux implementation using the inotify API when it runs out of watches.
- "modified"
The
changedHandlehas been modified.- "moved"
The
changedHandlehas been moved within the watch scope fromrelativePathMovedFromtorelativePathComponents.- "unknown"
Zero or more events might have been missed. For example, this might be sent for a Windows implementation using the ReadDirectoryChangesW API when it has a buffer overflow. A developer might want to poll the watched directory or file for changes they missed.
FileSystemChangeRecord
for
a
file
system
observer
registration
observerRegistration
given
a
file
system
locator
changedLocator
,
a
FileSystemChangeType
type
,
and
an
optional
file
system
locator
movedFromLocator
:Let root be observerRegistration ’s root handle .
Let rootLocator be root ’s locator .
Let relativePathComponents be the result of resolving changedLocator relative to rootLocator .
Let relativePathMovedFrom be null.
If movedFromLocator was given, set relativePathMovedFrom to the result of resolving movedFromLocator relative to rootLocator .
Let realm be root ’s relevant realm .
Let record be a new
FileSystemChangeRecordin realm .Set record ’s
rootto root .Set record ’s
changedHandleto null.Set record ’s
relativePathComponentsto relativePathComponents .Set record ’s
typeto type .Set record ’s
relativePathMovedFromto relativePathMovedFrom .If type is not equal to "disappeared", "errored", or "unknown":
Set record ’s
changedHandleto the result of creating a newFileSystemHandlegiven changedLocator in realm .
Return record .
Acknowledgments
Many thanks to Alex Danilo, Anne van Kesteren, Anoesj Sadraee, Austin Sullivan, Chase Phillips, Daseul Lee, Dru Knox, Edgar Chen, Emanuel Krivoy, Hazim Mohamed, Ingvar Stepanyan, Jari Jalkanen, Joshua Bell, Kagami Sascha Rosylight, Marcos Cáceres, Martin Thomson, Olivier Yiptong, Philip Jägenstedt, Randell Jesup, Richard Stotz, Ruth John, Sid Vishnoi, Sihui Liu, Stefan Sauer, Thomas Steiner, Victor Costan, and Youenn Fablet for being awesome!
This standard is written by Marijn Kruisselbrink ( Google , mek@chromium.org ).
Intellectual property rights
Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a Creative Commons Attribution 4.0 International License . To the extent portions of it are incorporated into source code, such portions in the source code are licensed under the BSD 3-Clause License instead.
This is the Living Standard. Those interested in the patent-review version should view the Living Standard Review Draft .