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 // Metadata is stored in files as serialized to JSON maps. See contents of | |
| 8 // example1.fake and example2.fake. | |
| 9 | |
| 10 // Multiple volumes can be opened at the same time. The key is the | |
| 11 // fileSystemId, which is the same as the file's entry.name. | |
| 12 // The value is a Volume object. | |
| 13 var volumes = {}; | |
| 14 | |
| 15 // Defines a volume object that contains information about mounted devices. | |
|
mtomasz
2014/07/01 00:40:33
nit: about mounted devices -> about a mounted file
cmihail
2014/07/01 01:27:20
Done.
| |
| 16 function Volume(entry, metadata, opt_openedFiles) { | |
| 17 // Used for restoring the opened file entry after resuming the event page. | |
| 18 this.entry = entry; | |
| 19 | |
| 20 // The volume metadata. Date object is serialized in JSON as string. | |
|
mtomasz
2014/07/01 00:40:33
nit: I think the second comment is confusing in th
cmihail
2014/07/01 01:27:21
Done.
| |
| 21 this.metadata = []; | |
| 22 for (var path in metadata) { | |
| 23 this.metadata[path] = metadata[path]; | |
| 24 this.metadata[path].modificationTime = | |
| 25 new Date(metadata[path].modificationTime); | |
| 26 } | |
| 27 | |
| 28 // A map with currently opened files. As key it has requestId of | |
|
mtomasz
2014/07/01 00:40:32
nit: As key it has requestId... -> The key is a re
cmihail
2014/07/01 01:27:21
Done.
| |
| 29 // openFileRequested and as a value the file path. | |
| 30 this.openedFiles = opt_openedFiles ? opt_openedFiles : {}; | |
| 31 }; | |
| 32 | |
| 33 function onUnmountRequested(options, onSuccess, onError) { | |
| 34 if (Object.keys(volumes[options.fileSystemId].openedFiles).length != 0) { | |
| 35 onError('IN_USE'); | |
| 36 return; | |
| 37 } | |
| 38 | |
| 39 chrome.fileSystemProvider.unmount( | |
| 40 {fileSystemId: options.fileSystemId}, | |
| 41 function() { | |
| 42 delete volumes[options.fileSystemId]; | |
| 43 saveState(); // Remove volume from local storage state. | |
| 44 onSuccess(); | |
| 45 }, | |
| 46 function() { | |
| 47 onError('FAILED'); | |
| 48 }); | |
| 49 }; | |
| 50 | |
| 51 function onGetMetadataRequested(options, onSuccess, onError) { | |
| 52 restoreState(options.fileSystemId, function () { | |
| 53 var entryMetadata = | |
| 54 volumes[options.fileSystemId].metadata[options.entryPath]; | |
| 55 if (!entryMetadata) | |
| 56 error('NOT_FOUND'); | |
| 57 else | |
| 58 onSuccess(entryMetadata); | |
| 59 }, onError); | |
| 60 }; | |
| 61 | |
| 62 function onReadDirectoryRequested(options, onSuccess, onError) { | |
| 63 restoreState(options.fileSystemId, function () { | |
| 64 var directoryMetadata = | |
| 65 volumes[options.fileSystemId].metadata[options.directoryPath]; | |
| 66 if (!directoryMetadata) { | |
| 67 onError('NOT_FOUND'); | |
| 68 return; | |
| 69 } | |
| 70 if (!directoryMetadata.isDirectory) { | |
| 71 onError('NOT_A_DIRECTORY'); | |
| 72 return; | |
| 73 } | |
| 74 | |
| 75 // Retrieve directory contents from metadata. | |
| 76 var entries = []; | |
| 77 for (var entry in volumes[options.fileSystemId].metadata) { | |
| 78 // Do not add itself on the list. | |
| 79 if (entry == options.directoryPath) | |
| 80 continue; | |
| 81 // Check if the entry is a child of the requested directory. | |
| 82 if (entry.indexOf(options.directoryPath) != 0) | |
| 83 continue; | |
| 84 // Restrict to direct children only. | |
| 85 if (entry.substring(options.directoryPath.length + 1).indexOf('/') != -1) | |
| 86 continue; | |
| 87 | |
| 88 entries.push(volumes[options.fileSystemId].metadata[entry]); | |
| 89 } | |
| 90 onSuccess(entries, false /* Last call. */); | |
| 91 }, onError); | |
| 92 }; | |
| 93 | |
| 94 function onOpenFileRequested(options, onSuccess, onError) { | |
| 95 restoreState(options.fileSystemId, function () { | |
| 96 if (options.mode != 'READ' || options.create) { | |
| 97 onError('INVALID_OPERATION'); | |
| 98 } else { | |
| 99 volumes[options.fileSystemId].openedFiles[options.requestId] = | |
| 100 options.filePath; | |
| 101 onSuccess(); | |
| 102 } | |
| 103 }, onError); | |
| 104 }; | |
| 105 | |
| 106 function onCloseFileRequested(options, onSuccess, onError) { | |
| 107 restoreState(options.fileSystemId, function () { | |
| 108 if (!volumes[options.fileSystemId].openedFiles[options.openRequestId]) { | |
| 109 onError('INVALID_OPERATION'); | |
| 110 } else { | |
| 111 delete volumes[options.fileSystemId].openedFiles[options.openRequestId]; | |
| 112 onSuccess(); | |
| 113 } | |
| 114 }, onError); | |
| 115 }; | |
| 116 | |
| 117 function onReadFileRequested(options, onSuccess, onError) { | |
| 118 restoreState(options.fileSystemId, function () { | |
| 119 var filePath = | |
| 120 volumes[options.fileSystemId].openedFiles[options.openRequestId]; | |
| 121 if (!filePath) { | |
| 122 onError('INVALID_OPERATION'); | |
| 123 return; | |
| 124 } | |
| 125 | |
| 126 var contents = volumes[options.fileSystemId].metadata[filePath].contents; | |
| 127 | |
| 128 // Write the contents as ASCII text. | |
| 129 var buffer = new ArrayBuffer(options.length); | |
| 130 var bufferView = new Uint8Array(buffer); | |
| 131 for (var i = 0; i < options.length; i++) { | |
| 132 bufferView[i] = contents.charCodeAt(i); | |
| 133 } | |
| 134 | |
| 135 onSuccess(buffer, false /* Last call. */); | |
| 136 }, onError); | |
| 137 }; | |
| 138 | |
| 139 // Save state in case of restarts, event page suspend, crashes, etc. | |
|
mtomasz
2014/07/01 00:40:32
nit: Save -> Saves
cmihail
2014/07/01 01:27:21
Done.
| |
| 140 function saveState() { | |
| 141 var state = {}; | |
| 142 for (var volumeId in volumes) { | |
| 143 var entryId = chrome.fileSystem.retainEntry(volumes[volumeId].entry); | |
| 144 state[volumeId] = { | |
| 145 entryId: entryId, | |
| 146 openedFiles: volumes[volumeId].openedFiles | |
| 147 }; | |
| 148 } | |
| 149 chrome.storage.local.set({state: state}); | |
| 150 } | |
| 151 | |
| 152 // Restore state. In this case the file system is already mounted and | |
|
mtomasz
2014/07/01 00:40:32
nit: Restore -> Restores. Comments for methods sho
cmihail
2014/07/01 01:27:22
Done.
| |
| 153 // we only need to obtain the metadata, which is done lazily. | |
|
mtomasz
2014/07/01 00:40:33
The comment about remounting is confusing. How abo
cmihail
2014/07/01 01:27:21
Done.
| |
| 154 function restoreState(fileSystemId, onSuccess, onError) { | |
| 155 chrome.storage.local.get(['state'], function(result) { | |
| 156 // Check if metadata for the given file system is alread in memory. | |
| 157 if (volumes[fileSystemId]) { | |
| 158 onSuccess(); | |
| 159 return; | |
| 160 } | |
| 161 | |
| 162 chrome.fileSystem.restoreEntry( | |
| 163 result.state[fileSystemId].entryId, | |
| 164 function(entry) { | |
| 165 readMetadataFromFile(entry, | |
| 166 function(metadata) { | |
| 167 volumes[fileSystemId] = new Volume(entry, metadata, | |
| 168 result.state[fileSystemId].openedFiles); | |
| 169 onSuccess(); | |
| 170 }, onError); | |
| 171 }); | |
| 172 }); | |
| 173 } | |
| 174 | |
| 175 // onSuccess has as parameter the metadata read in JSON format. | |
|
mtomasz
2014/07/01 00:40:33
The returned metadata is already deserialized, so
cmihail
2014/07/01 01:27:21
Yes. I wasn't clear. I was referring to the fact t
| |
| 176 function readMetadataFromFile(entry, onSuccess, onError) { | |
| 177 entry.file(function(file) { | |
| 178 var fileReader = new FileReader(); | |
| 179 fileReader.onload = function(event) { | |
| 180 onSuccess(JSON.parse(event.target.result)); | |
| 181 }; | |
| 182 | |
| 183 fileReader.onerror = function(event) { | |
| 184 onError('FAILED'); | |
| 185 }; | |
| 186 | |
| 187 fileReader.readAsText(file); | |
| 188 }); | |
| 189 } | |
| 190 | |
| 191 // Event called on clicking the file with the extension or mime type | |
| 192 // mentioned in the manifest file. | |
|
mtomasz
2014/07/01 00:40:32
nit: clicking on the file -> opening the file
nit:
cmihail
2014/07/01 01:27:21
Done.
| |
| 193 chrome.app.runtime.onLaunched.addListener(function(event) { | |
| 194 event.items.forEach(function(item) { | |
| 195 readMetadataFromFile(item.entry, | |
| 196 function(metadata) { | |
| 197 // Mount the volume and save its information in local storage | |
| 198 // in order to be able to recover the metadata in case of | |
| 199 // restarts, system crashes, etc. | |
| 200 volumes[item.entry.name] = new Volume(item.entry, metadata); | |
| 201 chrome.fileSystemProvider.mount( | |
| 202 {fileSystemId: item.entry.name, displayName: item.entry.name}, | |
|
mtomasz
2014/07/01 00:40:33
This may fail when two files with the same name ar
cmihail
2014/07/01 01:27:20
Done. For displayName I have kept item.entry.name
| |
| 203 function() { saveState(); }, | |
| 204 function() { console.error('Failed to mount.'); }); | |
| 205 }, | |
| 206 function(error) { | |
| 207 console.error(error); | |
| 208 }); | |
| 209 }); | |
| 210 }); | |
| 211 | |
| 212 // Event called on a profile startup. | |
| 213 chrome.runtime.onStartup.addListener(function () { | |
| 214 chrome.storage.local.get(['state'], function(result) { | |
| 215 // Nothing to change. | |
| 216 if (!result.state) | |
| 217 return; | |
| 218 | |
| 219 // Remove files opened before the profile shutdown from the local | |
| 220 // storage state. | |
|
mtomasz
2014/07/01 00:40:32
nit: local storage state -> local storage.
cmihail
2014/07/01 01:27:21
Done.
| |
| 221 for (var volumeId in result.state) { | |
| 222 result.state[volumeId].openedFiles = {}; | |
| 223 } | |
| 224 chrome.storage.local.set({state: result.state}); | |
| 225 }); | |
| 226 }); | |
| 227 | |
| 228 // Save the state before suspending the event page, so we can resume it | |
| 229 // once new events arrive. | |
| 230 chrome.runtime.onSuspend.addListener(function() { | |
| 231 saveState(); | |
| 232 }); | |
| 233 | |
| 234 chrome.fileSystemProvider.onUnmountRequested.addListener( | |
| 235 onUnmountRequested); | |
| 236 chrome.fileSystemProvider.onGetMetadataRequested.addListener( | |
| 237 onGetMetadataRequested); | |
| 238 chrome.fileSystemProvider.onReadDirectoryRequested.addListener( | |
| 239 onReadDirectoryRequested); | |
| 240 chrome.fileSystemProvider.onOpenFileRequested.addListener( | |
| 241 onOpenFileRequested); | |
| 242 chrome.fileSystemProvider.onCloseFileRequested.addListener( | |
| 243 onCloseFileRequested); | |
| 244 chrome.fileSystemProvider.onReadFileRequested.addListener( | |
| 245 onReadFileRequested); | |
| OLD | NEW |