Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 'use strict'; | |
| 6 | |
| 7 // For file examples with metadata inside, see example1.fake and example2.fake. | |
|
mtomasz
2014/06/30 08:30:30
... -> Metadata is stored in files as serialized t
cmihail
2014/07/01 00:07:05
Done.
| |
| 8 | |
| 9 // Multiple volumes can be opened at the same time. The key is the | |
| 10 // fileSystemId, which is the same as the file entry.name. The value | |
|
mtomasz
2014/06/30 08:30:31
file -> file's
cmihail
2014/07/01 00:07:05
Done.
| |
| 11 // is a Volume object. | |
| 12 var volumes = {}; | |
| 13 | |
| 14 // Defines a volume object that contains information about mounted devices. | |
| 15 // openedFiles parameter is optional and can be undefined. | |
| 16 function Volume(entry, mount, metadata, openedFiles) { | |
|
mtomasz
2014/06/30 08:30:31
openedFiles -> opt_openedFiles
cmihail
2014/07/01 00:07:05
Done.
| |
| 17 // Used for restoring file entry. | |
|
mtomasz
2014/06/30 08:30:30
... -> Used for restoring the opened file entry af
cmihail
2014/07/01 00:07:05
Done.
| |
| 18 this.entry_ = entry; | |
| 19 | |
| 20 // The volume metadata. | |
|
mtomasz
2014/06/30 08:30:31
Let's add a comment:
Date object is serialized in
cmihail
2014/07/01 00:07:05
Done.
| |
| 21 for (var item in metadata) { | |
|
mtomasz
2014/06/30 08:30:31
I think it is better not to modify input arguments
cmihail
2014/07/01 00:07:05
Done.
| |
| 22 metadata[item].modificationTime = | |
| 23 new Date(metadata[item].modificationTime); | |
| 24 } | |
| 25 this.metadata_ = metadata; | |
| 26 | |
| 27 // A map with currently opened files. As key it has requestId of | |
| 28 // openFileRequested and as a value the file path. | |
| 29 this.openedFiles_ = openedFiles ? openedFiles : {}; | |
| 30 | |
| 31 // Mount device and save device information on local storage in order to be | |
|
mtomasz
2014/06/30 08:30:31
Mount device -> Mount the volume and save its info
cmihail
2014/07/01 00:07:05
Done.
| |
| 32 // able to recover metadata in case of restarts, system crashes, etc. | |
| 33 if (mount) { | |
| 34 chrome.fileSystemProvider.mount( | |
| 35 {fileSystemId: entry.name, displayName: entry.name}, | |
| 36 function() { saveState(); }, | |
| 37 function() { console.error('Failed to mount.'); }); | |
| 38 | |
| 39 } | |
| 40 }; | |
| 41 | |
| 42 function onUnmountRequested(options, onSuccess, onError) { | |
| 43 if (Object.keys(volumes[options.fileSystemId].openedFiles_).length != 0) { | |
| 44 onError('IN_USE'); | |
| 45 return; | |
| 46 } | |
| 47 | |
| 48 chrome.fileSystemProvider.unmount( | |
| 49 {fileSystemId: options.fileSystemId}, | |
| 50 function() { | |
| 51 delete volumes[options.fileSystemId]; | |
| 52 saveState(); // Remove volume from local storage state. | |
| 53 onSuccess(); | |
| 54 }, | |
| 55 function() { | |
| 56 onError('FAILED'); | |
| 57 }); | |
| 58 }; | |
| 59 | |
| 60 function onGetMetadataRequested(options, onSuccess, onError) { | |
| 61 restoreState(options.fileSystemId, function () { | |
| 62 var entryMetadata = | |
| 63 volumes[options.fileSystemId].metadata_[options.entryPath]; | |
| 64 if (!entryMetadata) | |
| 65 error('NOT_FOUND'); | |
| 66 else | |
| 67 onSuccess(entryMetadata); | |
| 68 }, onError); | |
| 69 }; | |
| 70 | |
| 71 function onReadDirectoryRequested(options, onSuccess, onError) { | |
| 72 restoreState(options.fileSystemId, function () { | |
| 73 var directoryMetadata = | |
| 74 volumes[options.fileSystemId].metadata_[options.directoryPath]; | |
| 75 if (!directoryMetadata) { | |
| 76 onError('NOT_FOUND'); | |
| 77 return; | |
| 78 } | |
| 79 if (!directoryMetadata.isDirectory) { | |
| 80 onError('NOT_A_DIRECTORY'); | |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 // Retrieve directory contents from metadata. | |
| 85 var entries = []; | |
| 86 for (var entry in volumes[options.fileSystemId].metadata_) { | |
| 87 // Do not add itself on the list. | |
| 88 if (entry == options.directoryPath) | |
| 89 continue; | |
| 90 // Check if the entry is a child of the requested directory. | |
| 91 if (entry.indexOf(options.directoryPath) != 0) | |
| 92 continue; | |
| 93 // Restrict to direct children only. | |
| 94 if (entry.substring(options.directoryPath.length + 1).indexOf('/') != -1) | |
| 95 continue; | |
| 96 | |
| 97 entries.push(volumes[options.fileSystemId].metadata_[entry]); | |
| 98 } | |
| 99 onSuccess(entries, false /* Last call. */); | |
| 100 }, onError); | |
| 101 }; | |
| 102 | |
| 103 function onOpenFileRequested(options, onSuccess, onError) { | |
| 104 restoreState(options.fileSystemId, function () { | |
| 105 if (options.mode != 'READ' || options.create) { | |
| 106 onError('INVALID_OPERATION'); | |
| 107 } else { | |
| 108 volumes[options.fileSystemId].openedFiles_[options.requestId] = | |
| 109 options.filePath; | |
| 110 onSuccess(); | |
| 111 } | |
| 112 }, onError); | |
| 113 }; | |
| 114 | |
| 115 function onCloseFileRequested(options, onSuccess, onError) { | |
| 116 restoreState(options.fileSystemId, function () { | |
| 117 if (!volumes[options.fileSystemId].openedFiles_[options.openRequestId]) { | |
| 118 onError('INVALID_OPERATION'); | |
| 119 } else { | |
| 120 delete volumes[options.fileSystemId].openedFiles_[options.openRequestId]; | |
| 121 onSuccess(); | |
| 122 } | |
| 123 }, onError); | |
| 124 }; | |
| 125 | |
| 126 function onReadFileRequested(options, onSuccess, onError) { | |
| 127 restoreState(options.fileSystemId, function () { | |
| 128 var filePath = | |
| 129 volumes[options.fileSystemId].openedFiles_[options.openRequestId]; | |
| 130 if (!filePath) { | |
| 131 onError('INVALID_OPERATION'); | |
| 132 return; | |
| 133 } | |
| 134 | |
| 135 var contents = volumes[options.fileSystemId].metadata_[filePath].contents; | |
|
mtomasz
2014/06/30 08:30:31
nit: metadata_ is private here. Let's make it publ
cmihail
2014/07/01 00:07:05
Done. Also modified the other variables.
| |
| 136 | |
| 137 // Write the contents as ASCII text. | |
| 138 var buffer = new ArrayBuffer(options.length); | |
| 139 var bufferView = new Uint8Array(buffer); | |
| 140 for (var i = 0; i < options.length; i++) { | |
| 141 bufferView[i] = contents.charCodeAt(i); | |
| 142 } | |
| 143 | |
| 144 onSuccess(buffer, false /* Last call. */); | |
| 145 }, onError); | |
| 146 }; | |
| 147 | |
| 148 // Save state in case of restart, extension suspend, crashes, etc. | |
|
mtomasz
2014/06/30 08:30:30
nit: restart -> restarts
nit: extension suspend ->
cmihail
2014/07/01 00:07:05
Done.
| |
| 149 function saveState() { | |
| 150 var state = {}; | |
| 151 for (var volumeId in volumes) { | |
| 152 var entryId = chrome.fileSystem.retainEntry(volumes[volumeId].entry_); | |
|
cmihail
2014/06/30 08:01:44
As a mention: retainEntry executes quite often. I
mtomasz
2014/06/30 13:35:32
That's a good point. I took a look at the C++ code
| |
| 153 state[volumeId] = { | |
| 154 entryId: entryId, | |
| 155 openedFiles: volumes[volumeId].openedFiles_ | |
| 156 }; | |
| 157 } | |
| 158 chrome.storage.local.set({state: state}); | |
| 159 } | |
| 160 | |
| 161 // Restore state. In this case the file system is already mounted and | |
| 162 // we only need to obtain the metadata, which is done lazily. | |
| 163 function restoreState(fileSystemId, onSuccess, onError) { | |
| 164 chrome.storage.local.get(['state'], function(result) { | |
| 165 // Check if metadata for the given file system is alread in memory. | |
| 166 if (volumes[fileSystemId]) { | |
| 167 onSuccess(); | |
| 168 return; | |
| 169 } | |
| 170 | |
| 171 chrome.fileSystem.restoreEntry( | |
| 172 result.state[fileSystemId].entryId, | |
| 173 function(entry) { | |
| 174 readMetadataFromFile(entry, false /* File system is mounted. */, | |
| 175 onSuccess, onError, result.state[fileSystemId].openedFiles); | |
| 176 }); | |
| 177 }); | |
| 178 } | |
| 179 | |
| 180 // openedFiles is an optional parameter. | |
| 181 function readMetadataFromFile(entry, mount, onSuccess, onError, openedFiles) { | |
|
mtomasz
2014/06/30 08:30:30
readMetadataFromFile shouldn't need |openedFiles|.
cmihail
2014/07/01 00:07:05
Done.
| |
| 182 entry.file(function(file) { | |
| 183 var fileReader = new FileReader(); | |
| 184 fileReader.onload = function(event) { | |
| 185 var metadata = JSON.parse(event.target.result); | |
| 186 volumes[entry.name] = new Volume(entry, mount, metadata, openedFiles); | |
| 187 onSuccess(); | |
| 188 }; | |
| 189 | |
| 190 fileReader.onerror = function(event) { | |
| 191 onError('FAILED'); | |
| 192 }; | |
| 193 | |
| 194 fileReader.readAsText(file); | |
| 195 }); | |
| 196 } | |
| 197 | |
| 198 // Event called on clicking the file with the extension mentioned | |
| 199 // in the manifest file. | |
|
mtomasz
2014/06/30 08:30:30
Not only file extension, but also if mime type is
cmihail
2014/07/01 00:07:06
Done.
| |
| 200 chrome.app.runtime.onLaunched.addListener(function(event) { | |
| 201 event.items.forEach(function(item) { | |
| 202 readMetadataFromFile(item.entry, true /* Mount file system. */, | |
| 203 function() {}, | |
| 204 function(error) { console.error(error); } ); | |
| 205 }); | |
| 206 }); | |
| 207 | |
| 208 // Event called on chromeos startup. | |
|
mtomasz
2014/06/30 08:30:31
... -> Event called on a profile startup.
cmihail
2014/07/01 00:07:05
Done.
| |
| 209 chrome.runtime.onStartup.addListener(function () { | |
| 210 chrome.storage.local.get(['state'], function(result) { | |
| 211 // Nothing to change. | |
| 212 if (!result.state) | |
| 213 return; | |
| 214 | |
| 215 // Remove opened files from state. The file entry restore | |
|
mtomasz
2014/06/30 08:30:31
I think we could clarify it a little bit, eg.:
...
cmihail
2014/07/01 00:07:05
Done. I added local storage state. It seems a bit
| |
| 216 // will be done by the restoreState function. | |
| 217 for (var volumeId in result.state) { | |
| 218 result.state[volumeId].openedFiles = {}; | |
| 219 } | |
| 220 chrome.storage.local.set({state: result.state}); | |
| 221 }); | |
| 222 }); | |
| 223 | |
| 224 // Event called when the extension is idle for about 10+ seconds and it will | |
|
mtomasz
2014/06/30 08:30:31
I think we can remove this comment. The idling per
cmihail
2014/07/01 00:07:05
That sounds good to me.
| |
| 225 // get killed to free resources. The extension state should be saved. | |
| 226 chrome.runtime.onSuspend.addListener(function() { | |
| 227 saveState(); | |
| 228 }); | |
| 229 | |
| 230 chrome.fileSystemProvider.onUnmountRequested.addListener( | |
| 231 onUnmountRequested); | |
| 232 chrome.fileSystemProvider.onGetMetadataRequested.addListener( | |
| 233 onGetMetadataRequested); | |
| 234 chrome.fileSystemProvider.onReadDirectoryRequested.addListener( | |
| 235 onReadDirectoryRequested); | |
| 236 chrome.fileSystemProvider.onOpenFileRequested.addListener( | |
| 237 onOpenFileRequested); | |
| 238 chrome.fileSystemProvider.onCloseFileRequested.addListener( | |
| 239 onCloseFileRequested); | |
| 240 chrome.fileSystemProvider.onReadFileRequested.addListener( | |
| 241 onReadFileRequested); | |
| OLD | NEW |