37d65b4a353cf5272d6af6bb9d341ef7d7ea70fc
[platform/core/api/webapi-plugins.git] / src / filesystem / js / file_system_manager.js
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 function FileSystemStorage(data) {
18     Object.defineProperties(this, {
19         label: { value: data.label, writable: false, enumerable: true },
20         type: { value: data.type, writable: false, enumerable: true },
21         state: { value: data.state, writable: false, enumerable: true }
22     });
23 }
24
25 var FileStreamManager = function() {
26     this.nextId = 0;
27 };
28
29 FileStreamManager.prototype.getNextFileHandleId = function() {
30     return ++this.nextId;
31 };
32
33 var fileStreamManager = new FileStreamManager();
34
35 function FileSystemManager() {
36     var limits = native_.getResultObject(native_.callSync('FileSystemManagerGetLimits'));
37     Object.defineProperties(this, {
38         maxNameLength: { value: limits[0], writable: false, enumerable: true },
39         maxPathLength: { value: limits[1], writable: false, enumerable: true }
40     });
41 }
42
43 FileSystemManager.prototype.openFile = function() {
44     var args = validator_.validateArgs(arguments, [
45         { name: 'path', type: types_.STRING },
46         { name: 'openMode', type: types_.ENUM, values: type_.getValues(FileMode) },
47         { name: 'makeParents', type: types_.BOOLEAN, optional: true }
48     ]);
49
50     if (!args.has.makeParents) {
51         args.makeParents = true;
52     }
53
54     var data = {
55         path: commonFS_.toRealPath(args.path),
56         openMode: args.openMode,
57         makeParents: args.makeParents,
58         id: fileStreamManager.getNextFileHandleId()
59     };
60
61     if (!data.path) {
62         throw new WebAPIException(
63             WebAPIException.NOT_FOUND_ERR,
64             'Invalid path: ' + args.path
65         );
66     }
67
68     var result = native_.callSync('FileSystemManagerOpenFile', data);
69     if (native_.isFailure(result)) {
70         throw native_.getErrorObject(result);
71     } else {
72         return new FileHandle(data.id, args.path, args.openMode);
73     }
74 };
75
76 FileSystemManager.prototype.createDirectory = function() {
77     var args = validator_.validateArgs(arguments, [
78         { name: 'path', type: types_.STRING },
79         { name: 'makeParents', type: types_.BOOLEAN, optional: true },
80         {
81             name: 'successCallback',
82             type: types_.FUNCTION,
83             optional: true,
84             nullable: true
85         },
86         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
87     ]);
88
89     if (!args.has.makeParents) {
90         args.makeParents = true;
91     }
92
93     var data = { path: commonFS_.toRealPath(args.path), makeParents: args.makeParents };
94
95     if (!data.path) {
96         throw new WebAPIException(
97             WebAPIException.INVALID_VALUES_ERR,
98             'Invalid path: ' + args.path
99         );
100     }
101
102     var callback = function(result) {
103         if (native_.isFailure(result)) {
104             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
105         } else {
106             var path = native_.getResultObject(result);
107             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
108         }
109     };
110
111     var result = native_.call('FileSystemManagerCreateDirectory', data, callback);
112     if (native_.isFailure(result)) {
113         throw native_.getErrorObject(result);
114     }
115 };
116
117 FileSystemManager.prototype.deleteFile = function() {
118     var args = validator_.validateArgs(arguments, [
119         { name: 'path', type: types_.STRING },
120         {
121             name: 'successCallback',
122             type: types_.FUNCTION,
123             optional: true,
124             nullable: true
125         },
126         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
127     ]);
128
129     var data = { path: commonFS_.toRealPath(args.path) };
130
131     if (!data.path) {
132         throw new WebAPIException(
133             WebAPIException.INVALID_VALUES_ERR,
134             'Invalid path: ' + args.path
135         );
136     }
137
138     var callback = function(result) {
139         if (native_.isFailure(result)) {
140             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
141         } else {
142             var path = native_.getResultObject(result);
143             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
144         }
145     };
146
147     var result = native_.call('FileSystemManagerDeleteFile', data, callback);
148     if (native_.isFailure(result)) {
149         throw native_.getErrorObject(result);
150     }
151 };
152
153 FileSystemManager.prototype.deleteDirectory = function() {
154     var args = validator_.validateArgs(arguments, [
155         { name: 'path', type: types_.STRING },
156         { name: 'recursive', type: types_.BOOLEAN, optional: true },
157         {
158             name: 'successCallback',
159             type: types_.FUNCTION,
160             optional: true,
161             nullable: true
162         },
163         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
164     ]);
165
166     if (!args.has.recursive) {
167         args.recursive = true;
168     }
169
170     var realPath = commonFS_.toRealPath(args.path);
171     if (!realPath) {
172         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
173     }
174
175     var data = { path: realPath, recursive: args.recursive };
176
177     var callback = function(result) {
178         if (native_.isFailure(result)) {
179             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
180         } else {
181             var path = native_.getResultObject(result);
182             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
183         }
184     };
185
186     var result = native_.call('FileSystemManagerDeleteDirectory', data, callback);
187     if (native_.isFailure(result)) {
188         throw native_.getErrorObject(result);
189     }
190 };
191
192 FileSystemManager.prototype.copyFile = function() {
193     var args = validator_.validateArgs(arguments, [
194         { name: 'path', type: types_.STRING },
195         { name: 'destinationPath', type: types_.STRING },
196         { name: 'overwrite', type: types_.BOOLEAN, optional: true },
197         {
198             name: 'successCallback',
199             type: types_.FUNCTION,
200             optional: true,
201             nullable: true
202         },
203         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
204     ]);
205
206     if (!args.has.overwrite) {
207         args.overwrite = false;
208     }
209
210     var data = {
211         path: commonFS_.toRealPath(args.path),
212         destinationPath: commonFS_.toRealPath(args.destinationPath),
213         overwrite: args.overwrite
214     };
215
216     if (!data.path) {
217         throw new WebAPIException(
218             WebAPIException.INVALID_VALUES_ERR,
219             'Invalid path: ' + args.path
220         );
221     }
222     if (!data.destinationPath) {
223         throw new WebAPIException(
224             WebAPIException.INVALID_VALUES_ERR,
225             'Invalid path: ' + args.destinationPath
226         );
227     }
228
229     var callback = function(result) {
230         if (native_.isFailure(result)) {
231             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
232         } else {
233             var path = native_.getResultObject(result);
234             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
235         }
236     };
237
238     var result = native_.call('FileSystemManagerCopyFile', data, callback);
239     if (native_.isFailure(result)) {
240         throw native_.getErrorObject(result);
241     }
242 };
243
244 FileSystemManager.prototype.copyDirectory = function() {
245     var args = validator_.validateArgs(arguments, [
246         { name: 'path', type: types_.STRING },
247         { name: 'destinationPath', type: types_.STRING },
248         { name: 'overwrite', type: types_.BOOLEAN, optional: true },
249         {
250             name: 'successCallback',
251             type: types_.FUNCTION,
252             optional: true,
253             nullable: true
254         },
255         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
256     ]);
257
258     var realPath = commonFS_.toRealPath(args.path);
259     var realDestinationPath = commonFS_.toRealPath(args.destinationPath);
260     if (!realPath || !realDestinationPath) {
261         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
262     }
263
264     if (!args.has.overwrite) {
265         args.overwrite = false;
266     }
267
268     var data = {
269         path: realPath,
270         destinationPath: realDestinationPath,
271         overwrite: args.overwrite
272     };
273
274     var callback = function(result) {
275         if (native_.isFailure(result)) {
276             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
277         } else {
278             var path = native_.getResultObject(result);
279             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
280         }
281     };
282
283     var result = native_.call('FileSystemManagerCopyDirectory', data, callback);
284     if (native_.isFailure(result)) {
285         throw native_.getErrorObject(result);
286     }
287 };
288
289 FileSystemManager.prototype.moveFile = function() {
290     var args = validator_.validateArgs(arguments, [
291         { name: 'path', type: types_.STRING },
292         { name: 'destinationPath', type: types_.STRING },
293         { name: 'overwrite', type: types_.BOOLEAN, optional: true },
294         {
295             name: 'successCallback',
296             type: types_.FUNCTION,
297             optional: true,
298             nullable: true
299         },
300         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
301     ]);
302
303     var realPath = commonFS_.toRealPath(args.path);
304     var realDestinationPath = commonFS_.toRealPath(args.destinationPath);
305     if (!realPath || !realDestinationPath) {
306         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
307     }
308
309     if (!args.has.overwrite) {
310         args.overwrite = false;
311     }
312
313     var data = {
314         path: realPath,
315         destinationPath: realDestinationPath,
316         overwrite: args.overwrite
317     };
318
319     var callback = function(result) {
320         if (native_.isFailure(result)) {
321             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
322         } else {
323             var path = native_.getResultObject(result);
324             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
325         }
326     };
327
328     var result = native_.call('FileSystemManagerMoveFile', data, callback);
329     if (native_.isFailure(result)) {
330         throw native_.getErrorObject(result);
331     }
332 };
333
334 FileSystemManager.prototype.moveDirectory = function() {
335     var args = validator_.validateArgs(arguments, [
336         { name: 'path', type: types_.STRING },
337         { name: 'destinationPath', type: types_.STRING },
338         { name: 'overwrite', type: types_.BOOLEAN, optional: true },
339         {
340             name: 'successCallback',
341             type: types_.FUNCTION,
342             optional: true,
343             nullable: true
344         },
345         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
346     ]);
347
348     var realPath = commonFS_.toRealPath(args.path);
349     var realDestinationPath = commonFS_.toRealPath(args.destinationPath);
350     if (!realPath || !realDestinationPath) {
351         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
352     }
353
354     if (!args.has.overwrite) {
355         args.overwrite = false;
356     }
357
358     var data = {
359         path: realPath,
360         destinationPath: realDestinationPath,
361         overwrite: args.overwrite
362     };
363
364     var callback = function(result) {
365         if (native_.isFailure(result)) {
366             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
367         } else {
368             var path = native_.getResultObject(result);
369             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
370         }
371     };
372
373     var result = native_.call('FileSystemManagerMoveDirectory', data, callback);
374     if (native_.isFailure(result)) {
375         throw native_.getErrorObject(result);
376     }
377 };
378
379 FileSystemManager.prototype.rename = function() {
380     var args = validator_.validateArgs(arguments, [
381         { name: 'path', type: types_.STRING },
382         { name: 'newName', type: types_.STRING },
383         {
384             name: 'successCallback',
385             type: types_.FUNCTION,
386             optional: true,
387             nullable: true
388         },
389         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true }
390     ]);
391
392     if (-1 !== args.newName.indexOf('/') || -1 !== args.newName.indexOf('\x00')) {
393         throw new WebAPIException(
394             WebAPIException.INVALID_VALUES_ERR,
395             'newName contains invalid character.'
396         );
397     }
398
399     var realPath = commonFS_.toRealPath(args.path);
400     if (!realPath) {
401         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
402     }
403
404     var data = { path: realPath, newName: args.newName };
405
406     var callback = function(result) {
407         if (native_.isFailure(result)) {
408             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
409         } else {
410             var path = native_.getResultObject(result);
411             native_.callIfPossible(args.successCallback, commonFS_.toVirtualPath(path));
412         }
413     };
414
415     var result = native_.call('FileSystemManagerRename', data, callback);
416     if (native_.isFailure(result)) {
417         throw native_.getErrorObject(result);
418     }
419 };
420
421 function throwIfNotDate(argument, name) {
422     if (argument instanceof Date) {
423         return true;
424     }
425     throw new WebAPIException(
426         WebAPIException.TYPE_MISMATCH_ERR,
427         'Argument "' + name + '" in a filter is not of type Date.'
428     );
429 }
430
431 FileSystemManager.prototype.listDirectory = function() {
432     var args = validator_.validateArgs(arguments, [
433         { name: 'path', type: types_.STRING },
434         { name: 'successCallback', type: types_.FUNCTION, optional: true },
435         { name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true },
436         { name: 'filter', type: types_.DICTIONARY, optional: true, nullable: true }
437     ]);
438
439     if (!args.has.filter) {
440         args.filter = {};
441     }
442
443     if (args.filter.hasOwnProperty('startModified')) {
444         throwIfNotDate(args.filter.startModified, 'startModified');
445         args.filter.startModified = args.filter.startModified.getTime() / 1000;
446     }
447     if (args.filter.hasOwnProperty('endModified')) {
448         throwIfNotDate(args.filter.endModified, 'endModified');
449         args.filter.endModified = args.filter.endModified.getTime() / 1000;
450     }
451     if (args.filter.hasOwnProperty('startCreated')) {
452         throwIfNotDate(args.filter.startCreated, 'startCreated');
453         args.filter.startCreated = args.filter.startCreated.getTime() / 1000;
454     }
455     if (args.filter.hasOwnProperty('endCreated')) {
456         throwIfNotDate(args.filter.endCreated, 'endCreated');
457         args.filter.endCreated = args.filter.endCreated.getTime() / 1000;
458     }
459
460     var data = { path: commonFS_.toRealPath(args.path), filter: args.filter };
461
462     if (!data.path) {
463         throw new WebAPIException(
464             WebAPIException.INVALID_VALUES_ERR,
465             'Invalid path: ' + args.path
466         );
467     }
468
469     var callback = function(result) {
470         if (native_.isFailure(result)) {
471             native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
472         } else {
473             var obj = native_.getResultObject(result);
474             var names = obj.names;
475             if (args.filter.hasOwnProperty('name')) {
476                 var regex_name = stringToRegex(args.filter.name);
477                 for (var i = names.length - 1; i >= 0; i--) {
478                     if (!regex_name.test(names[i])) {
479                         names.splice(i, 1);
480                     }
481                 }
482             }
483             native_.callIfPossible(
484                 args.successCallback,
485                 names,
486                 commonFS_.toVirtualPath(obj.path)
487             );
488         }
489     };
490
491     var result = native_.call('FileSystemManagerListDirectory', data, callback);
492     if (native_.isFailure(result)) {
493         throw native_.getErrorObject(result);
494     }
495 };
496
497 FileSystemManager.prototype.toURI = function() {
498     var args = validator_.validateArgs(arguments, [
499         { name: 'path', type: types_.STRING }
500     ]);
501
502     // The toRealPath function will convert any string to absolute path, if possible.
503     // The function returns undefined for path, which starts with not-existing
504     // virtual root.
505     var realPath = commonFS_.toRealPath(args.path);
506
507     if (!realPath) {
508         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
509     }
510
511     return 'file://' + realPath;
512 };
513
514 FileSystemManager.prototype.isFile = function() {
515     var args = validator_.validateArgs(arguments, [
516         { name: 'path', type: types_.STRING }
517     ]);
518     // The toRealPath function will convert any string to absolute path, if possible.
519     // The function returns undefined for path, which starts with not-existing
520     // virtual root.
521     var realPath = commonFS_.toRealPath(args.path);
522
523     if (!realPath) {
524         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
525     }
526
527     var data = { path: realPath };
528
529     var result = native_.callSync('FileSystemManagerIsFile', data);
530     if (native_.isFailure(result)) {
531         throw native_.getErrorObject(result);
532     } else {
533         return native_.getResultObject(result);
534     }
535 };
536
537 FileSystemManager.prototype.isDirectory = function() {
538     var args = validator_.validateArgs(arguments, [
539         { name: 'path', type: types_.STRING }
540     ]);
541     // The toRealPath function will convert any string to absolute path, if possible.
542     // The function returns undefined for path, which starts with not-existing
543     // virtual root.
544     var realPath = commonFS_.toRealPath(args.path);
545
546     if (!realPath) {
547         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
548     }
549
550     var data = { path: realPath };
551
552     var result = native_.callSync('FileSystemManagerIsDirectory', data);
553     if (native_.isFailure(result)) {
554         throw native_.getErrorObject(result);
555     } else {
556         return native_.getResultObject(result);
557     }
558 };
559
560 FileSystemManager.prototype.pathExists = function() {
561     var args = validator_.validateArgs(arguments, [
562         { name: 'path', type: types_.STRING }
563     ]);
564     // The toRealPath function will convert any string to absolute path, if possible.
565     // The function returns undefined for path, which starts with not-existing
566     // virtual root.
567     var realPath = commonFS_.toRealPath(args.path);
568
569     if (!realPath) {
570         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Invalid path.');
571     }
572     var data = { path: realPath };
573
574     var result = native_.callSync('FileSystemManagerPathExists', data);
575     if (native_.isFailure(result)) {
576         throw native_.getErrorObject(result);
577     } else {
578         return native_.getResultObject(result);
579     }
580 };
581
582 FileSystemManager.prototype.getDirName = function() {
583     var args = validator_.validateArgs(arguments, [
584         { name: 'path', type: types_.STRING }
585     ]);
586     var path = args.path;
587
588     path = commonFS_.mergeMultipleSlashes(path);
589     if (path.startsWith('file://')) {
590         path = path.substring('file://'.length - 1, path.length - 1);
591     }
592
593     if (path.startsWith('/') && 0 === path.lastIndexOf('/')) {
594         // handle the "/" and "/file.ext"
595         return '/';
596     } else if (path.endsWith('/')) {
597         // cut the last '/'
598         path = path.substring(0, path.length - 1);
599     }
600
601     var index = path.lastIndexOf('/');
602     if (-1 !== index) {
603         path = path.substring(0, index); // cut the directory/file the path points to
604     }
605     return path;
606 };
607
608 function resolve() {
609     privUtils_.warn(
610         'DEPRECATION WARNING: FileSystemManager.resolve() is deprecated since ' +
611             'Tizen 5.0. Use FileHandle and FileSystemManager interfaces instead.'
612     );
613
614     var args = validator_.validateArgs(arguments, [
615         { name: 'location', type: types_.STRING },
616         { name: 'onsuccess', type: types_.FUNCTION },
617         { name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true },
618         {
619             name: 'mode',
620             type: types_.ENUM,
621             values: Object.keys(FileMode),
622             optional: true,
623             nullable: true
624         }
625     ]);
626
627     if (!args.has.mode) {
628         args.mode = 'rw';
629     } else if ('rwo' == args.mode) {
630         throw new WebAPIException(
631             WebAPIException.INVALID_VALUES_ERR,
632             'rwo mode was introduced in version 5.0 and is not supported in earlier ' +
633                 'version methods'
634         );
635     }
636
637     // resolving a path on unmounted storage should result in exception
638     var storage = commonFS_.getStorage(args.location.split('/')[0]);
639     if (storage && FileSystemStorageState.MOUNTED !== storage.state) {
640         setTimeout(function() {
641             native_.callIfPossible(
642                 args.onerror,
643                 new WebAPIException(
644                     WebAPIException.NOT_FOUND_ERR,
645                     'Storage is not mounted.'
646                 )
647             );
648         }, 0);
649         return;
650     }
651
652     // Validation against '.' and '..' directories used in path - not allowed
653     var result = commonFS_.checkPathWithoutDots(args.location);
654     if (!result) {
655         // path contains dots - it is not allowed - return InvalidValuesError
656         setTimeout(function() {
657             native_.callIfPossible(
658                 args.onerror,
659                 new WebAPIException(
660                     WebAPIException.INVALID_VALUES_ERR,
661                     'Path contains \'.\' or \'..\' - it is not allowed.'
662                 )
663             );
664         }, 0);
665         return;
666     }
667
668     var _realPath = commonFS_.toRealPath(args.location);
669
670     if (!_realPath) {
671         // invalid real path means that virtual root does not exist
672         setTimeout(function() {
673             native_.callIfPossible(
674                 args.onerror,
675                 new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Invalid path.')
676             );
677         }, 0);
678         return;
679     }
680
681     var _isLocationAllowed = commonFS_.isLocationAllowed(_realPath);
682
683     if (args.mode !== 'r' && !_isLocationAllowed) {
684         setTimeout(function() {
685             native_.callIfPossible(
686                 args.onerror,
687                 new WebAPIException(
688                     WebAPIException.INVALID_VALUES_ERR,
689                     'Provided arguments are not valid.'
690                 )
691             );
692         }, 0);
693         return;
694     }
695
696     var data = { location: _realPath };
697
698     var callback = function(result) {
699         if (native_.isFailure(result)) {
700             native_.callIfPossible(args.onerror, native_.getErrorObject(result));
701             return;
702         }
703
704         var aStatObj = native_.getResultObject(result);
705         var _result = commonFS_.getFileInfo(aStatObj, false, args.mode);
706         if (_result.readOnly && args.mode !== 'r') {
707             native_.callIfPossible(
708                 args.onerror,
709                 new WebAPIException(WebAPIException.IO_ERR, 'File is read-only.')
710             );
711         } else {
712             native_.callIfPossible(args.onsuccess, new File(_result));
713         }
714     };
715
716     var ret = native_.call('FileStat', data, callback);
717     if (native_.isFailure(ret)) {
718         throw native_.getErrorObject(ret);
719     }
720 }
721
722 FileSystemManager.prototype.resolve = function() {
723     resolve.apply(this, arguments);
724 };
725
726 function getStorage() {
727     xwalk.utils.checkPrivilegeAccess(xwalk.utils.privilege.FILESYSTEM_READ);
728     var args = validator_.validateArgs(arguments, [
729         { name: 'label', type: types_.STRING },
730         { name: 'onsuccess', type: types_.FUNCTION },
731         { name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true }
732     ]);
733
734     setTimeout(function() {
735         var storage = commonFS_.getStorage(args.label);
736
737         if (!storage) {
738             native_.callIfPossible(
739                 args.onerror,
740                 new WebAPIException(WebAPIException.NOT_FOUND_ERR, 'Storage not found.')
741             );
742         } else {
743             native_.callIfPossible(args.onsuccess, new FileSystemStorage(storage));
744         }
745     }, 0);
746 }
747
748 FileSystemManager.prototype.getStorage = function() {
749     getStorage.apply(this, arguments);
750 };
751
752 function listStorages() {
753     xwalk.utils.checkPrivilegeAccess(xwalk.utils.privilege.FILESYSTEM_READ);
754     var args = validator_.validateArgs(arguments, [
755         { name: 'onsuccess', type: types_.FUNCTION },
756         { name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true }
757     ]);
758
759     setTimeout(function() {
760         var storages = [];
761         var cache = commonFS_.getAllStorages();
762         for (var i = 0; i < cache.length; ++i) {
763             storages.push(new FileSystemStorage(cache[i]));
764         }
765
766         native_.callIfPossible(args.onsuccess, storages);
767     }, 0);
768 }
769
770 FileSystemManager.prototype.listStorages = function() {
771     listStorages.apply(this, arguments);
772 };
773
774 var callbackId = 0;
775 var callbacks = {};
776
777 function nextCallbackId() {
778     return ++callbackId;
779 }
780
781 function _StorageStateChangeListener(result) {
782     commonFS_.clearCache();
783     var storage = new FileSystemStorage(result);
784     for (var id in callbacks) {
785         native_.callIfPossible(callbacks[id], storage);
786     }
787 }
788
789 function addStorageStateChangeListener() {
790     var args = validator_.validateArgs(arguments, [
791         { name: 'onsuccess', type: types_.FUNCTION },
792         { name: 'onerror', type: types_.FUNCTION, optional: true, nullable: true }
793     ]);
794
795     var register = false;
796     if (type_.isEmptyObject(callbacks)) {
797         register = true;
798     }
799
800     var id = nextCallbackId();
801     callbacks[id] = args.onsuccess;
802
803     if (register) {
804         native_.addListener('StorageStateChangeListener', _StorageStateChangeListener);
805         var result = native_.callSync(
806             'FileSystemManagerAddStorageStateChangeListener',
807             {}
808         );
809
810         if (native_.isFailure(result)) {
811             throw native_.getErrorObject(result);
812         }
813     }
814
815     return id;
816 }
817
818 FileSystemManager.prototype.addStorageStateChangeListener = function() {
819     return addStorageStateChangeListener.apply(this, arguments);
820 };
821
822 function removeStorageStateChangeListener() {
823     var args = validator_.validateArgs(arguments, [
824         { name: 'watchId', type: types_.LONG }
825     ]);
826
827     if (!arguments.length) {
828         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, 'Missing watchId');
829     }
830     var id = args.watchId;
831
832     if (type_.isNullOrUndefined(callbacks[id])) {
833         return;
834     }
835
836     delete callbacks[id];
837
838     if (type_.isEmptyObject(callbacks)) {
839         var result = native_.callSync(
840             'FileSystemManagerRemoveStorageStateChangeListener',
841             {}
842         );
843         if (native_.isFailure(result)) {
844             throw native_.getErrorObject(result);
845         }
846     }
847 }
848
849 FileSystemManager.prototype.removeStorageStateChangeListener = function() {
850     removeStorageStateChangeListener.apply(this, arguments);
851 };
852
853 exports = new FileSystemManager();