Firefox OS/Cloud Storage: Difference between revisions

update the document
(Update Planning Phases)
(update the document)
Line 1: Line 1:
= Overview =
= Overview =
This project aims at to support a framework for web apps to interact with cloud storage.<br />
FirefoxOS cloud storage support is a project that supports a framework to integrate user's cloud storage into their FirefoxOS device.<br/>
[[File:Cloud Storage Support Overview.png|550px]]<br />
= Current Cloud Storage User Scenario =
Cloud Storage framework wants to support following features
In android or iOS, viewing a media file on cloud, it has following possible ways.<br/>
* '''Universal Storage API'''
1. Using one viewing app and one cloud app. It needs following steps.<br/>
Universal Storage API is a general API for web apps to access all kinds of cloud storage.
# Using the cloud app to download the media file to device.
* '''Virtual Storage Interface'''
# Using the viewing app to open the downloaded media file.
Virtual Storage Interface is plugin system for cloud storage framework to extend cloud storage option.
:In this way, user must download the whole media file first. Download brings following drawbacks.<br/>
 
:* File duplication.
= Planning Phases =
:File occupies the local storage. If device local storage is almost full, user might need to cleanup the local storage first.<br/>
[[File:PlanningPhases.png|thumbnail|Planning Phases|500px]]<br/>
:* Need to download the whole file before viewing.
* '''Phase 1'''
:Some files might be not suitable to be downloaded, for example 4k video files.<br/>
** We will implement a read-only storage for the ready-made app to read the data via Device Storage API. For example, Video app can play a video in Dropbox even the file size is larger than internal storage without any app modification.
<br/>
* '''Phase 2'''
2. Using a viewing app which support the specified cloud accessing. It needs following steps.<br/>
** Each user might have more than one storage account, so multi-storage support is an important feature for user to manipulate their storage more flexible.
# Using the viewing app to open the media file on cloud.
** Second, offline Support is for using storages even there is no network available (, and we can leverage Cache mechanism to implement this feature.)
:In this way, user can view the media in stream way. However, the viewing app must to support the user preferred cloud. Otherwise, user might select another app or cloud they don't want.<br/>
** From the perspective of storage provider, Flexible Storage Module is an easy way to install or update the storage plugin for 3rd party storage provider like Dropbox, Box.net, etc.
<br/>
** Multi Storage support will be handled by Account management. 3rd-party Storage plugin and Virtual Storage Interface are the key components to implement Flexible Storage Module.
In this project, we want to support user
* '''Phase 3'''
* <b>Choose their preferred apps and cloud in free.</b>
** Read-Write support, just like its literal meaning, files can be read or written. Read-Write feature is our last mile to achieve our final destination, and Sync Mechanism should handle all possible conflict when any file is changed from different clients.
* <b>No need to save the whole file in local.</b>
 
= Cloud Storage Support Framework =
= Cloud Storage Framework =
The basic idea of this framework is mounting cloud storage as a FirefoxOS fake volume in FirefoxOS Volume System. By mounting cloud as a fake volume, FirefoxOS becomes a proxy for apps to access user's cloud, such that apps needn't know how to access cloud data in special way and maintain cloud by themselves. On the other hand, FirefoxOS can support streaming access to avoid downloading the whole file into local storage.<br/>
== Overall Architecture ==
Instead of accessing cloud directly in Gecko, we design FileSystemProvider API to provide apps/addons can create a virtual file system in Gaia, and accessing cloud in these apps/addons. According to this design, we can gain following benefits<br/>
[[File:OverallArchitecture.png|thumbnail|Overall Architecture|500px]]<br/>
* No need to maintain third party cloud APIs in Gecko.
Based on the planning phases, this is an overall architecture of Cloud Storage includes Gecko part and Gaia part.<br/>
:It means FirefoxOS no need to FOTA to support the third party cloud APIs upgrade. It only needs to update the corresponding Cloud Storage Addons.<br/>
* Cloud Storage Configuration UI is for user to enable/disable cloud storage via storage management API. It could be in Settings app or any possible ways.<br/>
* Easy to reuse the framework on other kinds of storage.
* Gaia apps can use Device Storage API to access the files in Cloud Storage, just like Video app, Music app and Gallery app did. Eventually, we have to extend Device Storage API for Cloud Storage, like getting the synchronisation status of a file.<br/>
:Not only cloud storage, but also NFS, personal NAS can reuse this framework to plug into FirefoxOS Volume System, such that apps can also interact with these storage directly.<br/>
* 3rd party storage provider can implement a new plugin for users to manipulate their storage via Virtual Storage Interface. The interface is for storage provider to define how a file is accessed including getting meta data, file content reading and writing.<br/>
== FirefoxOS Volume System ==
* Cache mechanism, sync mechanism, and account management are the modules in gecko to be hidden, so app developers don’t have to worry about the issues like how to resolve the conflict issue, data caching for performance, etc.
FirefoxOS Volume System is a middle layer between FirefoxOS and low-level OS kernel. It supports basic volume operations, such as mounting, unmounting, and formatting, to manage volumes in the device. In addition to the basic volume operations, the Volume System also provides fake volume creation and deletion mechanism.<br/>
 
In this framework, we creates fake volumes for each connected cloud, therefore these connected cloud can be recognized, identified and accessed by apps through DeviceStorage API and File API.<br/>
== Universal Storage API ==
== DeviceStorage API and File API ==
Universal Storage API is a general API for web apps to access all kinds of storage.<br />
DeviceStorage API and File API are interface used by apps to access FirefoxOS file systems. Apps can use these APIs to perform file read, write, creation, and deletion. Here are more detail about [https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/API/Device_Storage_API DeviceStorage API] and [https://developer.mozilla.org/en-US/docs/Web/API/File File API]<br/>
According to different purposes, Universal Storage API can be separated into following two categories
In this framework, we let apps still use these APIs to access cloud data. It means apps can access user's cloud without any modification which just like they access the device local storage.<br/>
* '''Document based API'''
== FileSystemProvider API ==
Document based API is used to help web apps save/operate their application data, such as indexedDB, on the cloud storage.
FileSystemProvider API provides apps/addons capability to create virtual file systems in Gaia. It provides<br/>
* '''Filesystem based API'''
* Methods to request Volume System to create/delete a fake volume.
Filesystem based API supports web apps to access cloud storage with filesystem operations, such as file create, delete, open, read, write, close, etc.<br />
* Events and callbacks to request data from virtual file systems created by apps/addons.
Reference for Filesystem based API: [http://www.w3.org/TR/file-system-api/ W3C FileAPI]
=== Types ===
[https://github.com/filerjs/filer#providers filer]
:<b>OpenFileMode</b><br/>
[[WebAPI/DeviceStorageAPI|DeviceStorageAPI]]
::Mode for open a file.<br/>
== Virtual Storage Interface ==
::{| class="wikitable" style="width:100%"
Virtual Storage Interface is plugin system for cloud storage framework to cooperate with different cloud storage.
| style="background:#abc2e0" | <b>Enum</b>
 
== Cache Architecture ==
=== Background ===
* '''Improve performance'''
** The response time of each file operation should be as short as possible to provide better user experience when users want to view a photo, listen an audio, or watch a video.
* '''Offline Support'''
** Cache store is a subset of cloud storage, so Cache system still can provide cached file content from cache store even no network connection available.
=== Architecture ===
[[File:CacheArchitecture.png|thumbnail|Cache Architecture|500px]]
* '''Cache Manager'''
** Block-based data management
** Dispatch low-level file operation from FUSE to Data/Meta Cache first
** Get data from Web Storage Manager if there is no data existed in Cache.
* '''Meta Cache'''
** Metadata for each file (mdate, cdate, size, etc.) should be cached in memory or Indexed DB.
* '''Data Cache'''
** Data Cache should store a file content requested by user for each file in order to avoid extra network transaction.
* '''Web Storage Manager'''
** Provide an interface for each web storage provider (Dropbox, Box.net, etc) to communicate with Cache Manager.
** 3rd party plugin could be in the form of add-on for developer to support new web storage easily.
* '''Cache Policy'''
** Cache store entry can be flexible to adjust for different requirement, i.e. storage size. Any new request can replace one existed entry if the maximum entry is exceeded. ‘existed entry’ has not been well-defined yet, and it depends on user story.
** Cache policy is a set of method to prefetch file content or meta aggressively, so the response time for each request can be improved.
 
=== Data Cache Implement Proposal ===  
* File-based Cache
** minimum data chunk - File
* Block-based Cache
** minimum data chunk - Block (with fixed-size)
** File System level operation can be mapped to cloud storage.
{| class="wikitable"
|-
|-
! !! File-based !! Block-based
| <b>"READ"</b> or <b>"WRITE"</b>
|}
:<b>MountOptions</b><br/>
::Options for the mount method.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b>  
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system. It must be unique for each file system.
|-
| DOMString || displayName || The fake volume name.
|-
| boolean || writable || Writable file system.
|-
| unsigned long || openedFilesLimit<br/>(optional) || The maximum number of opened files.
|}
:<b>UnmountOptions</b><br/>
::Options for the unmount method.<br/>
::{| class="wikitable" style="width:100%"
| colspan=3 style="background:#abc2e0" | <b>Properties</b>
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|}
:<b>OpenedFile</b><br/>
::Represents an opened file.<br/>
::{| class="wikitable" style="width:100%"
| colspan=3 style="background:#abc2e0" | <b>Properties</b>
|-
| width=15% | unsigned long || width=15% | openRequestId || width=70% | The open request ID.
|-
| DOMString || filePath || The opened file path.
|-
| OpenFileMode || mode || The mode of open file.
|}
:<b>FileSystemInfo</b><br/>
::Represents a mounted file system.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| DOMString || displayName || The fake volume name.
|-
| boolean || writable || Writable file system.
|-
| unsigned long || openedFilesLimit || The maximum number of opened files.
|-
| sequence<br/><OpenedFile> || openedFiles || The sequence of opened files.
|}
:<b>FileSystemProviderError</b><br/>
::FileSystemProvider error types.<br/>
::{| class="wikitable" style="width:100%"
| style="background:#abc2e0" | <b>Enum</b>
|-
| <b>"Failed"</b>, <b>"InUse"</b>, <b>"Exists"</b>, <b>"NotFound"</b>, <b>"AccessDenied"</b>, <b>"TooManyOpened"</b>, <b>"NoMemory"</b>, <b>"NoSpace"</b>, <b>"NotADirectory"</b>, <b>"InvalidOperation"</b>, <b>"Security"</b>, <b>"Abort"</b>, <b>"NotAFile"</b>, <b>"NotEmpty"</b>, or <b>"InvalidURL"</b>
|}
:<b>EntryMetadata</b><br/>
::Represents metadata of a file or a directory.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | boolean || width=15% | isDirectory || width=70% | True if it is a directory.
|-
| DOMString || name || Name of this entry (not full path name). Must not contain '/'. For root it must be empty.
|-
| unsigned long long || size || File size in bytes.
|-
| DOMTimeStamp || modificationTime || The last modified time of this entry.
|-
| DOMString || mimeType<br/>(Optional) || Mime type for the entry.
|}
:<b>UnmountRequestedOptions</b><br/>
::Options for unmountrequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|}
:<b>AbortRequestedOptions</b><br/>
::Options for abortrequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| unsigned long || operationRequestId || The unsigned integer identifier of the aborting request.
|}
:<b>GetMetadataRequestedOptions</b><br/>
:: Options for getmetadatarequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || entryPath || The path of the entry to fetch metadata about.
|}
:<b>ReadDirectoryRequestedOptions</b><br/>
::Options for readdirectoryrequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || directoryPath || The path of the directory which contents are requested.
|}
:<b>OpenFileRequestedOptions</b><br/>
::Options for openfilerequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || filePath || The path of the file to be opened.
|-
| OpenFileMode || mode || Whether the file will be used for reading or writing.
|}
:<b>CloseFileRequestedOptions</b><br/>
::Options for closefilerequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| unsigned long || openRequestId || The unsigned integer identifier of the corresponding open request.
|}
:<b>ReadFileRequestedOptions</b><br/>
::Options for readfilerequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| unsigned long || openRequestId || The unsigned integer identifier of the corresponding open file request.
|-
| unsigned long long || offset || Position in the file (in bytes) to start reading from.
|-
| unsigned long long || size || Number of bytes to be returned.
|}
:<b>CreateDirectoryRequestedOptions</b><br/>
::Options for createdirectoryrequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || directoryPath || The path of the directory to be created.
|-
| boolean || recursive || Whether the operation is recursive (for directories only).
|}
:<b>DeleteEntryRequestedOptions</b><br/>
::Options for deleteentryrequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || entryPath || The path of the entry to be deleted.
|-
| boolean || recursive || Whether the operation is recursive (for directories only).
|}
:<b>CopyEntryRequestedOptions</b><br/>
::Options for copyentryrequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || sourcePath || The source path of the entry to be copied.
|-
| DOMString || targetPath || The destination path for the copy operation.
|}
:<b>MoveEntryRequestedOptions</b><br/>
::Options for moveentryrequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || sourcePath || The source path of the entry to be copied.
|-
| DOMString || targetPath || The destination path for the copy operation.
|}
:<b>CreateFileRequestedOptions</b><br/>
::Options for createfilerequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| DOMString || filePath || The path of the file to be created.
|}
:<b>WriteFileRequestedOptions</b><br/>
::Options for writefilerequested event.<br/>
::{| class="wikitable" style="width:100%"
| colspan="3" style="background:#abc2e0" | <b>Properties</b> 
|-
| width=15% | DOMString || width=15% | fileSystemId || width=70% | The string identifier of the file system.
|-
| unsigned long || requestId || The unsigned integer identifier of this request.
|-
| unsigned long || openRequestId || The unsigned integer identifier of the corresponding open file request.
|-
| unsigned long long || offset || Position in the file (in bytes) to start writing the bytes from.
|-
| ArrayBuffer || data || Buffer of bytes to be written to the file.
|}
=== Methods ===
:<b>mount</b><br/>
::navigator.fileSystemProvider.mount(MountOptions options)<br/>
::Mount a file system with the given MountOptions.<br/>
::{| class="wikitable" style="width:100%"
| colspan="2" style="background:#abc2e0" | <b>Parameters</b>
|-
| width=20% | MountOptions || width=80% | The mount options for mount a file system.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | Promise<void>
|}
:<b>unmount</b><br/>
::navigator.fileSystemProvider.unmount(UnmountOptions options)<br/>
::Unmount a file system by the given UnmountOptions.<br/>
::{| class="wikitable" style="width:100%"
| colspan="2" style="background:#abc2e0" | <b>Parameters</b>
|-
| width=20% | UnmountOptions || width=80% | The unmount options for unmount a file system.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | Promise<void>
|}
:<b>get</b><br/>
::navigator.fileSystemProvider.get(DOMString fileSystemId)<br/>
::Returns information about a file system with the passed fileSystemId.<br/>
::{| class="wikitable" style="width:100%"
| colspan="2" style="background:#abc2e0" | <b>Parameters</b>
|-
| width=20% | DOMStrig || width=80% | The file system ID.
|-
| colspan="2" style="background:#abc2e0" | <b>Return</b>
|-
| colspan="2" | FileSystemInfo
|}
:<b>getAll</b><br/>
::navigator.fileSystemProvider.getAll()<br/>
::Returns all file system information mounted by FileSystemProvider API
::{| class="wikitable" style="width:100%"
| style="background:#abc2e0" | <b>Return value</b>
|-
| sequence<FileSystemInfo>
|}
=== Events ===
:<b>unmountrequested</b><br/>
::Raised when unmounting for the file system with the fileSystemId identifier is requested.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("unmountrequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | UnmountRequestedOptions || colspan="2" width=30% | options || width=50% | The requested unmount options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for unmountrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for unmountrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>abortrequsted</b><br/>
::Raised when aborting an operation with operationRequestId is requested. The operation executed with operationRequestId must be immediately stopped and successCallback of this abort request executed. If aborting fails, then errorCallback must be called. Note, that callbacks of the aborted operation must not be called, as they will be ignored. Despite calling errorCallback, the request may be forcibly aborted.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("abortrequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | AbortRequestedOptions || colspan="2" width=30% | options || width=50% | The requested abort options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for abortrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for abortrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>getmetadatarequested</b><br/>
::Raised when metadata of a file or a directory at entryPath is requested. The metadata must be returned with the successCallback call. In case of an error, errorCallback must be called.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("getmetadatarequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | GetMetadataRequestedOptions || colspan="2" width=30% | options || width=50% | The requested getmetadata options.
|-
| rowspan="5" | function || colspan="2" | successCallback || rowspan="5" valign="top" | Success callback for getmetadatarequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| EntryMetadata || metadata
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for getmetadatarequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>readdirectoryrequested</b><br/>
::Raised when contents of a directory at directoryPath are requested. The results must be returned in chunks by calling the successCallback several times. In case of an error, errorCallback must be called.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("readdirectorytrequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | ReadDirectoryRequestedOptions || colspan="2" width=30% | options || width=50% | The requested readdirectory options.
|-
| rowspan="6" | function || colspan="2" | successCallback || rowspan="6" valign="top" | Success callback for readdirectoryrequested event.<br/>If more entries will be returned, then hasMore must be true, and it has to be called again with additional entries. If no more entries are available, then hasMore must be set to false.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| Sequence<EntryMetadata> || entries
|-
| boolean || hasMore
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for readdirectoryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>openfilerequested</b><br/>
::Raised when opening a file at filePath is requested. If the file does not exist, then the operation must fail.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("openfilerequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | OpenFileRequestedOptions || colspan="2" width=30% | options || width=50% | The requested openfile options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for openfilerequested.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for openfilerequested.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>closefilerequested</b><br/>
::Raised when opening a file previously opened with openRequestId is requested to be closed.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("closefilerequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | CloseFileRequestedOptions || colspan="2" width=30% | options || width=50% | The requested closefile options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for closefilerequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for closefilerequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>readfilerequested</b><br/>
::Raised when reading contents of a file opened previously with openRequestId is requested. The results must be returned in chunks by calling successCallback several times. In case of an error, errorCallback must be called.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("readfilerequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | ReadFileRequestedOptions || colspan="2" width=30% | options || width=50% | The requested readfile options.
|-
| rowspan="6" | function || colspan="2" | successCallback || rowspan="6" valign="top" | Success callback for the readfilerequested event.<br/>If more data will be returned, then hasMore must be true, and it has to be called again with additional entries. If no more data is available, then hasMore must be set to false.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| ArrayBuffer || data
|-
| boolean || hasMore
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for the readfilerequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>createdirectoryrequested</b><br/>
::Raised when creating a directory is requested. The operation must fail with the EXISTS error if the target directory already exists. If recursive is true, then all of the missing directories on the directory path must be created.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("createdirectoryrequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | CreateDirectoryRequestedOptions || colspan="2" width=30% | options || width=50% | The requested createdirectory options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for createdirectoryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for createdirectoryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>deleteentryrequested</b><br/>
::Raised when deleting an entry is requested. If recursive is true, and the entry is a directory, then all of the entries inside must be recursively deleted as well.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("deleteentryrequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | DeleteEntryRequestedOptions || colspan="2" width=30% | options || width=50% | The requested deleteentry options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for deleteentryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for deleteentryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>copyentryrequested</b><br/>
::Raised when copying an entry (recursively if a directory) is requested. If an error occurs, then errorCallback must be called.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("copyentryrequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | CopyEntryRequestedOptions || colspan="2" width=30% | options || width=50% | The requested copyentry options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for copyentryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for copyentryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>moveentryrequested</b><br/>
::Raised when moving an entry (recursively if a directory) is requested. If an error occurs, then errorCallback must be called.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("moveentryrequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | MoveEntryRequestedOptions || colspan="2" width=30% | options || width=50% | The requested moveentry options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for moveentryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for moveentryrequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>createfilerequested</b><br/>
::Raised when creating a file is requested. If the file already exists, then errorCallback must be called with the "EXISTS" error code.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("createfilerequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | CreateFileRequestedOptions || colspan="2" width=30% | options || width=50% | The requested createfile options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for createfilerequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for createfilerequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
| FileSystemProviderError || errorCode
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|}
:<b>writefilerequested</b><br/>
::Raised when writing contents to a file opened previously with openRequestId is requested.<br/>
::<b>addListener</b><br/>
:::navigator.fileSystemProvider.addListener("writefilerequested", function handler)<br/>
::{| class="wikitable" style="width:100%"
| colspan="4" style="background:#abc2e0" | <b>Properties</b>
|-
| width=20% | WriteFileRequestedOptions || colspan="2" width=30% | options || width=50% | The requested writefile options.
|-
| rowspan="3" | function || colspan="2" | successCallback || rowspan="3" valign="top" | Success callback for writefilerequested event.
|-
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
| colspan="2" | void
|-
|-
| Cross Platform/Browser || Yes || TBD...
| rowspan="5" | function || colspan="2" | errorCallback || rowspan="5" valign="top" | Error callback for writefilerequested event.
|-
|-
| Response Time for large-size file<br />(ex: video) || poor || good
| colspan="2" style="background:#abc2e0" | <b>Parameter</b>
|-
|-
| Response Time for small-size file<br />(ex: photo, audio) || good || good<br />(depends on block-size)
| FileSystemProviderError || errorCode
|-
|-
| Implementation Idea || Gaia: JS Library (Filer) || Gaia: TBD...<br />Gecko: FUSE
| colspan="2" style="background:#abc2e0" | <b>Return value</b>
|-
|-
| Related Web API || Blob, MSE(?), ... || Blob, Device Storage, ...
| colspan="2" | void
|}
|}
 
== Cloud Storage Addons ==
Reference
Cloud Storage Addons are in charge of the communication with the target cloud. It should provides at least<br/>
* [https://docs.google.com/presentation/d/1gB41Z6uQQO9C51Ns2aAm2A7vJox0AYt5tz2vyDC0xuo/edit#slide=id.p Cache Architecture study]
* User authentication and authorization mechanism
 
* Cloud access operations through the public APIs from cloud providers
== Proposed Solution(s) ==
Moreover, Cloud Storage Addons might provide more features, such as<br/>
=== FUSE based solution ===
* Data catch
Mounting Cloud Storage to device filesystem through the FUSE mechanism.<br />
* Data Synchronization
[[File:Cloud Storage FUSE solution.png|550px]]<br />
* Account management
==== Benefits ====
= Usage Flow =
* DeviceStorageAPI provides well defined filesystem based API to access files/directories on internal storage and SD card. Once FUSE based solution supported, Web app can used DeviceStorageAPI directly to access cloud storage without any modification.
== Mounting a Cloud on FirefoxOS ==
* FUSE interface is a good reference for Virtual Storage Interface. By propagating FUSE interface to FirefoxOS, FirefoxOS can easy connect to many kinds of storage, such as cloud storage, NAS, etc. [http://en.wikipedia.org/wiki/ExpanDrive Here is an commercial example].
To mount a cloud on FirefoxOS, FileSystemProvider API providers methods and events for Cloud Storage Addons.<br/>
==== Drawbacks ====
The picture shows the flow that Cloud Storage Addon mounts a cloud "MyCloud" on FirefoxOS through FileSystemProvider API.<br/>
* DeviceStorageAPI is not a standard API for all browser.
# Cloud Storage addon registers the event listener on FileSystemProviderEvents.
* FUSE is not supported in all platforms.
# Call FileSystemProvider.mount() method with parameters to request Volume System to create a fake volume.
==== Prototype ====
Following is an example code to mount a cloud "MyCloud"<br/>
* [https://github.com/MozCloudStorage MozCloudStorage github repo]
  // Code in Cloud Storage Addons
* CloudStorageServiceAPI (this is only for prototype)
  var fileSystemProvider = navigator.fileSystemProvider;
CloudStorageServiceAPI is a WebAPI for enabling/disabling cloud storage. <br />
  // Event handler definition
enum CloudStorageType {
  function GetMetadataHandler(event) {
   "Dummy",
    var path = event.options.entryPath;
   "Dropbox",
    // get metadata through cloud APIs with entry path
}; <br />
    if (metadata) {
Promise<boolean> enable(DOMString cloudName, CloudStorageType cloudType, DOMString accessToken)<br />
      event.successCallback(metadata);
Promise<void> disable(DOMString cloudName)<br />
    } else {
=== JS library solution ===
      event.errorCallback('NotFound');
==== Benefits ====
    }
==== Drawbacks ====
  }
== Framework Issues ==
    ...
* '''Cloud Storage Account management and Authentication'''
   // Register event listener on FileSystemProviderEvents
* '''Security issues'''
  fileSystemProvider.addEventListener("getmetadatarequested", GetMetadataHandler);
=== Issue Study ===
   fileSystemProvider.addEventListener("openfilerequested", OpenFileHandler);
* '''Video Support with MSE'''
  fileSystemProvider.addEventListener("closefilerequested", CloseFileHandler);
** [https://w3c.github.io/media-source/ Media Source Extensions]
  fileSystemProvider.addEventListener("readdirectoryrequested", ReadDirectoryHandler);
** The challenge with HTML5 audio and video is still the fragmented support for audio and video formats.
  fileSystemProvider.addEventListener("readfilerequested", ReadFileHandler);
** In Firefox browser, the maximum size of each fragment is 75MB. Getting QuotaExceededError if the fragment size is over 75MB.
    ...
** Fragmented Video Format - the video MUST be in fragmented format, and this video can be played well.
  // Call mount method to create fake volume "MyCloud" in FirefoxOS
** Non-fragmented Video - this more general format can be played in MSE way. Browser treats a non-fragment video as a one big fragment file because the maximum size of a non-fragment video is 75MB.
  fileSystemProvider.mount({fileSystemId: 'MyCloud',
 
                            displayName: 'MyCloud',
= Related Use cases =
                            writable: true,
== Play media files saved on cloud ==
                            openedFilesLimit: 256})
John saves his media files on dropbox or any other cloud storage and wants to play these media online on his mobile device.<br />
                            .then(
In traditional, John needs to do with following steps<br />
                            function() { dump('mount successfully\n');}
#Open the cloud storage app, such as dropbox and google drive, and download the media files to the mobile device.<br />
                            function(aError) { dump('mount failed ['+aError+']\n');}
#Open the player app, such as Gallery and Music, and play the media files locally.<br />
                            );
On FirefoxOS device, the steps can be reduced to<br />
== Accessing Cloud through the Framework ==
#Open the player app and play the media files on cloud storage directly.
To access a mounted cloud, app can reuse DeviceStorage and File APIs, just like they access local storage.<br/>
 
The picture shows the flow how Gallery app read the "test.jpg" on cloud "MyCloud".<br/>
== Create and share files on cloud ==
# Gallery app asks "MyCloud/test.jpg" data through the File API.
Bob wants to take a picture and share it with friend on flickr.<br />
# Volume System get the request and send FileSystemProvderReadFileEvent to ask corresponding Cloud Storage Addons to get the data from cloud.
In traditional, Bob needs to do with following steps<br />
# Cloud Storage Addon receives the FileSystemProviderReadFileEvent and call ReadFileEventListener to get "test.jpg" data from cloud through the cloud access API.
#Open the camera app to take a picture and save the file on the device.<br />
# Once read success or fail, call the event.successCallback with data or event.errorCallback with error code.
#(Optional) Open a third party app to edit the picture on the device.<br />
# Volume System receives the callback data and send it back to app through the File API.
#Open the flickr app to upload the file and set it as shared.<br />
Following is an example to access cloud 'MyCloud' through DeviceStorage and File APIs.<br/>
On FirefoxOS device, the steps can be reduced to<br />
  // Get file through DeviceStorage API
#Open the camera app to take a picture and save and share the file on cloud storage directly.<br />
  var myStorage = navigator.getDeviceStorage('MyCloud');
#(Optional) Open a third party app to edit the picture on cloud storage directly.<br />
  if (myStorage) {
 
    // Request to send GetMetadataEvent to get the information of "myFile.txt"
== File management between clouds ==
    var request = myStorage.get('myFile.txt');
Amy wants to copy a finished work document form her personal dropbox space to the company google drive space<br />
    request.onsuccess = function() {
In traditional, Amy needs to do with following steps<br />
      var file = this.result;
#Open the dropbox app and download the document file to device.
      dump(file.name+'\n');
#Open the google drive app and upload the document file.<br />
      var fileReader = new FileReader();
On FirefoxOS device, the steps can be reduced to<br />
      fileRead.onloadend = function() {dump('load finished\n');}
#Open a file browser app and copy the document from dropbox to google drive directly.
      // Request to send OpenFileEvent, ReadFileEvents and CloseFileEvent
 
      fileReader.readAsArrayBuffer(file);
= Related links =
    }
* [[Firefox_Cloud]]<br />
  }
* [[Webmaker/MakeDrive|MakeDrive]]<br />
* [http://fuse.sourceforge.net/ FUSE(filesystem in userspace)]<br />
= Previous works =
* https://github.com/weilonge/unidisk
* http://weilonge.bitbucket.org/unidisk/UD.pdf
= Related Bugzilla =
* [https://bugzilla.mozilla.org/show_bug.cgi?id=1035053 Bug 1035053 - [Device Storage<nowiki>]</nowiki> To Support Variant Cloud Storage]
* [https://bugzilla.mozilla.org/show_bug.cgi?id=1164750 Bug 1164750 - [Meta<nowiki>]</nowiki> Cloud storage support in FirefoxOS FUSE based prototyping]
33

edits