- From: Eric Uhrhane <ericu@google.com>
- Date: Fri, 29 Jan 2010 13:32:11 -0800
- To: public-device-apis@w3.org
As mentioned in my previous email [1], here's my proposal for an API covering directories and read-write access to a local, sandboxed, per-origin filesystem. As with that API, if people like the general gist of this one, I'll write it up nicely, get it hosted somewhere official, and start rolling in feedback. We have a lot of app developers asking for this sort of a capability, and are quite interested in discussing it further and getting to the point where we can experiment with implementations. Thanks, Eric Uhrhane ericu@google.com How to get a FileSystem in the device world: [Supplemental, NoInterfaceObject] interface DeviceFS { void fileSystems(FileSystemsCB successCallback, [Optional] Callback errorCallback); void resolveURI(DOMString uri, EntryCB successCallback, [Optional] Callback errorCallback); } [NoInterfaceObject, Callback=FunctionOnly] interface FileSystemsCB { void onSuccess(FileSystem[] fileSystems); }; This is pretty much the same as [2], but I moved resolveURI() up to the top level so that you don't need to know which filesystem a URI is in before trying to look it up. How to get a FileSystem in the browser world: [Supplemental, NoInterfaceObject] interface WindowLocalFS { void requestTemporaryFilesystem(FileSystemCB successCallback, [Optional] errorCallback); void requestPersistentFilesystem(long long requestedQuota, FileSystemCB successCallback, [Optional] errorCallback); void resolveURI(DOMString uri, EntryCB successCallback, [Optional] Callback errorCallback); } [NoInterfaceObject, Callback=FunctionOnly] interface FileSystemCB { void onSuccess(FileSystem fileSystem); }; An application can request temporary or persistent storage space. Temporary storage may be easier to get at the UA's discretion [looser quota restrictions, available without prompting the user], but the data stored there may be deleted at the UA's convenience. Once persistent storage has been granted, data stored there by the application must not be deleted by the UA without user intervention. The application may of course delete it itself. The UA should require permission from the user before granting persistent storage space to the application. As with any other client-side storage, filesystem access allows for cookie-resurrection attacks. UAs should present the option of clearing it when the user clears any other origin-specific storage, blocking access to it when cookies are blocked, etc. This is especially important if temporary storage space is permitted without explicit user permission. [NoInterfaceObject] interface FileSystem { readonly attribute DOMString name; readonly attribute DirectoryEntry root; readonly attribute long long quota; }; [NoInterfaceObject, Callback=FunctionOnly] interface EntryCB { void onSuccess(Entry entry); }; Cross-platform compatibility: I've taken out the filesystem attributes for case-sensitivity and -preservation. We don't want to pass the buck on those attributes to the application developer. We just want the same code to work everywhere. What the app sees should be a case-insensitive, case-preserving filesystem. That's easy to implement on top of the most-commonly-used filesystems, and not hard to emulate on case-sensitive systems. Problems due to interactions with applications running outside the browser should be extremely rare and minor. The directory separator is '/'. No file or directory may be named any of [CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9], nor may a file or directory's name begin with any of those strings followed by a period. Filenames may not end in period or space. Paths can't contain any character in the set [<>:"/\|?*] or whose representation in UTF-8 is in [0-31]. "./", as a path segment, refers to the currently-specified directory. If it's not at the beginning of the supplied path, it's therefore a noop. "../" as a path segment will refer to the parent of the currently-specified directory; if it's not the beginning of the supplied path, it therefore effectively deletes the previously-supplied path segment from the path being computed. The parent directory of the root directory of a filesystem is the root directory of the filesystem. [NoInterfaceObject] interface Entry { const unsigned short DIRECTORY = 1; const unsigned short FILE = 2; readonly attribute unsigned short entryType; readonly attribute DOMString name; readonly attribute DOMString fullPath; readonly attribute FileSystem fileSystem; void moveTo(DirectoryEntry parent, optional DOMString newName, [Optional] EntryCB successCallback, [Optional] Callback errorCallback); void copyTo(DirectoryEntry parent, optional DOMString newName, [Optional] EntryCB successCallback, [Optional] Callback errorCallback); void getParent(EntryCB successCallback, [Optional] Callback errorCallback); DOMString toURI(); // Valid only in this domain. // Note that this does not recurse; if this is a DirectoryEntrySync and the // directory it represents isn't empty, this will fail. If it turns out to be // important, we can add it, but there's nothing to stop a JavaScript library // from implementing it using this API. void remove([Optional] Callback successCallback, [Optional] Callback errorCallback); }; [NoInterfaceObject] interface DirectoryEntry : Entry { DirectoryReader createReader(); const unsigned short CREATE = 1; // If CREATE is specified and the Entry already exists, fail. // If CREATE is specified and the Entry doesn't exist, create it as a // zero-length file. // If CREATE is not specified and the Entry doesn't exist, fail. // If CREATE is not specified and the path exists, but is a directory, fail. void getFile(DOMString path, unsigned short options, [Optional] EntryCB successCallback, [Optional] Callback errorCallback); void makeDir(DOMString name, [Optional] EntryCB successCallback, [Optional] Callback errorCallback); }; [Constructor] interface DirectoryReader { // Holds state; subsequent calls return later Entries. // Returns success and zero entries when all Entries have been returned. void readEntries(EntriesCB successCallback, [Optional] Callback errorCallback); }; [NoInterfaceObject, Callback=FunctionOnly] interface EntriesCB { void onSuccess(Entry[] entries); }; [NoInterfaceObject] interface FileEntry : Entry, File { // If APPEND is not specified, the file is truncated to length 0 before its // first write. const unsigned short APPEND = 1; FileWriter createWriter([Optional] unsigned short options); }; I've made FileEntry derive from File in [3], but there are a couple of small issues to iron out: 1) An Entry's URI is persistent across page reloads, although of course the file or directory to which it refers may be deleted or moved. A File's URN is valid only until the page goes away. That's not necessarily a problem, but it seems odd to be able to ask for both from the same object. 2) File's "type" [formerly mediaType] might confuse folks looking for the entryType. That's not a show-stopper, but it might lead to some coding errors. As all operations will generally map to a single underlying platform-specific call, I've given them callbacks, rather than using an EventTarget. There's not really a way to get progress events, and it seems cumbersome to use an EventTarget without getting any extra benefit from it. This means we've got two different styles of async calls [between FileReader/Writer and this proposal], but I think the simplification is well worth the contrast. I took out the zip operation for now, as I'm not sure what the use cases are. Zip and unzip can be written in JavaScript [e.g. http://jszip.stuartk.co.uk/]. If speed will be an issue, please respond with use cases. Synchronous APIs for Web Workers, directly modeled on those above. [Supplemental, NoInterfaceObject] interface WorkerLocalFS { FileSystemSync requestTemporaryFilesystem(); FileSystemSync requestPersistentFilesystem(long long requestedQuota); EntrySync resolveURI(DOMString uri); } [NoInterfaceObject] interface FileSystemSync { readonly attribute DOMString name; readonly attribute DirectoryEntrySync root; readonly attribute long long quota; }; [NoInterfaceObject] interface EntrySync { const unsigned short DIRECTORY = 1; const unsigned short FILE = 2; readonly attribute unsigned short type; readonly attribute DOMString name; readonly attribute DOMString fullPath; readonly attribute FileSystem fileSystem; void moveTo(DirectoryEntrySync parent, optional DOMString newName); void copyTo(DirectoryEntrySync parent, optional DOMString newName); DirectoryEntrySync getParent(); DOMString toURI(); // Valid only in this domain. Same issues as fullPath. // Note that this does not recurse; if this is a DirectoryEntrySync and the // directory it represents isn't empty, this will fail. If it turns out to be // important, we can add it, but there's nothing to stop a JavaScript library // from implementing it using this API. void remove(); }; [NoInterfaceObject] interface DirectoryEntrySync : EntrySync { DirectoryReaderSync createReader(); const unsigned short CREATE = 1; // If CREATE is specified and the Entry already exists, fail. // If CREATE is specified and the Entry doesn't exist, create it as a // zero-length file. // If CREATE is not specified and the Entry doesn't exist, fail. // If CREATE is not specified and the path exists, but is a directory, fail. EntrySync getFile(DOMString path, unsigned short options); DirectoryEntrySync makeDir(DOMString name); }; [Constructor] interface DirectoryReaderSync { // Holds state; subsequent calls return later Entries. // Returns null when all Entries have been returned. EntrySync[] readEntries(); }; [NoInterfaceObject] interface FileEntrySync : EntrySync, File { // If APPEND is not specified, the file is truncated to length 0 as it's // opened. const unsigned short APPEND = 1; FileWriterSync createWriter([Optional] unsigned short options); }; // TODO: Some way to query quota info. Enforcing eventual and approximate quota // correctness is a lot easier to do efficiently than doing it per-call, so I // plan to leave some wiggle room there. [1] http://lists.w3.org/Archives/Public/public-device-apis/2010Jan/0228.html [2] http://dev.w3.org/2009/dap/file-system/file-dir-sys.html [3] http://dev.w3.org/2006/webapi/FileAPI/
Received on Friday, 29 January 2010 21:33:03 UTC