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 |