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