Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / workspace / IsolatedFileSystem.js
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @param {!WebInspector.IsolatedFileSystemManager} manager
34  * @param {string} path
35  * @param {string} name
36  * @param {string} rootURL
37  */
38 WebInspector.IsolatedFileSystem = function(manager, path, name, rootURL)
39 {
40     this._manager = manager;
41     this._path = path;
42     this._name = name;
43     this._rootURL = rootURL;
44 }
45
46 /**
47  * @param {!FileError} error
48  * @return {string}
49  */
50 WebInspector.IsolatedFileSystem.errorMessage = function(error)
51 {
52     return WebInspector.UIString("File system error: %s", error.message);
53 }
54
55 /**
56  * @param {string} fileSystemPath
57  * @return {string}
58  */
59 WebInspector.IsolatedFileSystem.normalizePath = function(fileSystemPath)
60 {
61     if (WebInspector.isWin())
62         return fileSystemPath.replace(/\\/g, "/");
63     return fileSystemPath;
64 }
65
66 WebInspector.IsolatedFileSystem.prototype = {
67     /**
68      * @return {string}
69      */
70     path: function()
71     {
72         return this._path;
73     },
74
75     /**
76      * @return {string}
77      */
78     normalizedPath: function()
79     {
80         if (this._normalizedPath)
81             return this._normalizedPath;
82         this._normalizedPath = WebInspector.IsolatedFileSystem.normalizePath(this._path);
83         return this._normalizedPath;
84     },
85
86     /**
87      * @return {string}
88      */
89     name: function()
90     {
91         return this._name;
92     },
93
94     /**
95      * @return {string}
96      */
97     rootURL: function()
98     {
99         return this._rootURL;
100     },
101
102     /**
103      * @param {function(?DOMFileSystem)} callback
104      */
105     _requestFileSystem: function(callback)
106     {
107         this._manager.requestDOMFileSystem(this._path, callback);
108     },
109
110     /**
111      * @param {string} path
112      * @param {function(string)} fileCallback
113      * @param {function()=} finishedCallback
114      */
115     requestFilesRecursive: function(path, fileCallback, finishedCallback)
116     {
117         var domFileSystem;
118         var pendingRequests = 0;
119         this._requestFileSystem(fileSystemLoaded.bind(this));
120         /**
121          * @param {?DOMFileSystem} fs
122          * @this {WebInspector.IsolatedFileSystem}
123          */
124         function fileSystemLoaded(fs)
125         {
126             domFileSystem = /** @type {!DOMFileSystem} */ (fs);
127             console.assert(domFileSystem);
128             ++pendingRequests;
129             this._requestEntries(domFileSystem, path, innerCallback.bind(this));
130         }
131
132         /**
133          * @param {!Array.<!FileEntry>} entries
134          * @this {WebInspector.IsolatedFileSystem}
135          */
136         function innerCallback(entries)
137         {
138             for (var i = 0; i < entries.length; ++i) {
139                 var entry = entries[i];
140                 if (!entry.isDirectory) {
141                     if (this._manager.mapping().isFileExcluded(this._path, entry.fullPath))
142                         continue;
143                     fileCallback(entry.fullPath.substr(1));
144                 }
145                 else {
146                     if (this._manager.mapping().isFileExcluded(this._path, entry.fullPath + "/"))
147                         continue;
148                     ++pendingRequests;
149                     this._requestEntries(domFileSystem, entry.fullPath, innerCallback.bind(this));
150                 }
151             }
152             if (finishedCallback && (--pendingRequests === 0))
153                 finishedCallback();
154         }
155     },
156
157     /**
158      * @param {string} path
159      * @param {?string} name
160      * @param {function(?string)} callback
161      */
162     createFile: function(path, name, callback)
163     {
164         this._requestFileSystem(fileSystemLoaded.bind(this));
165         var newFileIndex = 1;
166         if (!name)
167             name = "NewFile";
168         var nameCandidate;
169
170         /**
171          * @param {?DOMFileSystem} fs
172          * @this {WebInspector.IsolatedFileSystem}
173          */
174         function fileSystemLoaded(fs)
175         {
176             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
177             console.assert(domFileSystem);
178             domFileSystem.root.getDirectory(path, null, dirEntryLoaded.bind(this), errorHandler.bind(this));
179         }
180
181         /**
182          * @param {!DirectoryEntry} dirEntry
183          * @this {WebInspector.IsolatedFileSystem}
184          */
185         function dirEntryLoaded(dirEntry)
186         {
187             var nameCandidate = name;
188             if (newFileIndex > 1)
189                 nameCandidate += newFileIndex;
190             ++newFileIndex;
191             dirEntry.getFile(nameCandidate, { create: true, exclusive: true }, fileCreated, fileCreationError.bind(this));
192
193             function fileCreated(entry)
194             {
195                 callback(entry.fullPath.substr(1));
196             }
197
198             /**
199              * @this {WebInspector.IsolatedFileSystem}
200              */
201             function fileCreationError(error)
202             {
203                 if (error.code === FileError.INVALID_MODIFICATION_ERR) {
204                     dirEntryLoaded.call(this, dirEntry);
205                     return;
206                 }
207
208                 var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
209                 console.error(errorMessage + " when testing if file exists '" + (this._path + "/" + path + "/" + nameCandidate) + "'");
210                 callback(null);
211             }
212         }
213
214         /**
215          * @this {WebInspector.IsolatedFileSystem}
216          */
217         function errorHandler(error)
218         {
219             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
220             var filePath = this._path + "/" + path;
221             if (nameCandidate)
222                 filePath += "/" + nameCandidate;
223             console.error(errorMessage + " when getting content for file '" + (filePath) + "'");
224             callback(null);
225         }
226     },
227
228     /**
229      * @param {string} path
230      */
231     deleteFile: function(path)
232     {
233         this._requestFileSystem(fileSystemLoaded.bind(this));
234
235         /**
236          * @param {?DOMFileSystem} fs
237          * @this {WebInspector.IsolatedFileSystem}
238          */
239         function fileSystemLoaded(fs)
240         {
241             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
242             console.assert(domFileSystem);
243             domFileSystem.root.getFile(path, null, fileEntryLoaded.bind(this), errorHandler.bind(this));
244         }
245
246         /**
247          * @param {!FileEntry} fileEntry
248          * @this {WebInspector.IsolatedFileSystem}
249          */
250         function fileEntryLoaded(fileEntry)
251         {
252             fileEntry.remove(fileEntryRemoved, errorHandler.bind(this));
253         }
254
255         function fileEntryRemoved()
256         {
257         }
258
259         /**
260          * @param {!FileError} error
261          * @this {WebInspector.IsolatedFileSystem}
262          */
263         function errorHandler(error)
264         {
265             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
266             console.error(errorMessage + " when deleting file '" + (this._path + "/" + path) + "'");
267         }
268     },
269
270     /**
271      * @param {string} path
272      * @param {function(?Date, ?number)} callback
273      */
274     requestMetadata: function(path, callback)
275     {
276         this._requestFileSystem(fileSystemLoaded);
277
278         /**
279          * @param {?DOMFileSystem} fs
280          */
281         function fileSystemLoaded(fs)
282         {
283             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
284             console.assert(domFileSystem);
285             domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler);
286         }
287
288         /**
289          * @param {!FileEntry} entry
290          */
291         function fileEntryLoaded(entry)
292         {
293             entry.getMetadata(successHandler, errorHandler);
294         }
295
296         /**
297          * @param {!Metadata} metadata
298          */
299         function successHandler(metadata)
300         {
301             callback(metadata.modificationTime, metadata.size);
302         }
303
304         /**
305          * @param {!FileError} error
306          */
307         function errorHandler(error)
308         {
309             callback(null, null);
310         }
311     },
312
313     /**
314      * @param {string} path
315      * @param {function(?string)} callback
316      */
317     requestFileContent: function(path, callback)
318     {
319         this._requestFileSystem(fileSystemLoaded.bind(this));
320
321         /**
322          * @param {?DOMFileSystem} fs
323          * @this {WebInspector.IsolatedFileSystem}
324          */
325         function fileSystemLoaded(fs)
326         {
327             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
328             console.assert(domFileSystem);
329             domFileSystem.root.getFile(path, null, fileEntryLoaded.bind(this), errorHandler.bind(this));
330         }
331
332         /**
333          * @param {!FileEntry} entry
334          * @this {WebInspector.IsolatedFileSystem}
335          */
336         function fileEntryLoaded(entry)
337         {
338             entry.file(fileLoaded, errorHandler.bind(this));
339         }
340
341         /**
342          * @param {!Blob} file
343          */
344         function fileLoaded(file)
345         {
346             var reader = new FileReader();
347             reader.onloadend = readerLoadEnd;
348             reader.readAsText(file);
349         }
350
351         /**
352          * @this {!FileReader}
353          */
354         function readerLoadEnd()
355         {
356             /** @type {?string} */
357             var string = null;
358             try {
359                 string = /** @type {string} */ (this.result);
360             } catch (e) {
361                 console.error("Can't read file: " + path + ": " + e);
362             }
363             callback(string);
364         }
365
366         /**
367          * @this {WebInspector.IsolatedFileSystem}
368          */
369         function errorHandler(error)
370         {
371             if (error.code === FileError.NOT_FOUND_ERR) {
372                 callback(null);
373                 return;
374             }
375
376             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
377             console.error(errorMessage + " when getting content for file '" + (this._path + "/" + path) + "'");
378             callback(null);
379         }
380     },
381
382     /**
383      * @param {string} path
384      * @param {string} content
385      * @param {function()} callback
386      */
387     setFileContent: function(path, content, callback)
388     {
389         this._requestFileSystem(fileSystemLoaded.bind(this));
390         WebInspector.userMetrics.FileSavedInWorkspace.record();
391
392         /**
393          * @param {?DOMFileSystem} fs
394          * @this {WebInspector.IsolatedFileSystem}
395          */
396         function fileSystemLoaded(fs)
397         {
398             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
399             console.assert(domFileSystem);
400             domFileSystem.root.getFile(path, { create: true }, fileEntryLoaded.bind(this), errorHandler.bind(this));
401         }
402
403         /**
404          * @param {!FileEntry} entry
405          * @this {WebInspector.IsolatedFileSystem}
406          */
407         function fileEntryLoaded(entry)
408         {
409             entry.createWriter(fileWriterCreated.bind(this), errorHandler.bind(this));
410         }
411
412         /**
413          * @param {!FileWriter} fileWriter
414          * @this {WebInspector.IsolatedFileSystem}
415          */
416         function fileWriterCreated(fileWriter)
417         {
418             fileWriter.onerror = errorHandler.bind(this);
419             fileWriter.onwriteend = fileTruncated;
420             fileWriter.truncate(0);
421
422             function fileTruncated()
423             {
424                 fileWriter.onwriteend = writerEnd;
425                 var blob = new Blob([content], { type: "text/plain" });
426                 fileWriter.write(blob);
427             }
428         }
429
430         function writerEnd()
431         {
432             callback();
433         }
434
435         /**
436          * @this {WebInspector.IsolatedFileSystem}
437          */
438         function errorHandler(error)
439         {
440             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
441             console.error(errorMessage + " when setting content for file '" + (this._path + "/" + path) + "'");
442             callback();
443         }
444     },
445
446     /**
447      * @param {string} path
448      * @param {string} newName
449      * @param {function(boolean, string=)} callback
450      */
451     renameFile: function(path, newName, callback)
452     {
453         newName = newName ? newName.trim() : newName;
454         if (!newName || newName.indexOf("/") !== -1) {
455             callback(false);
456             return;
457         }
458         var fileEntry;
459         var dirEntry;
460         var newFileEntry;
461         this._requestFileSystem(fileSystemLoaded.bind(this));
462
463         /**
464          * @param {?DOMFileSystem} fs
465          * @this {WebInspector.IsolatedFileSystem}
466          */
467         function fileSystemLoaded(fs)
468         {
469             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
470             console.assert(domFileSystem);
471             domFileSystem.root.getFile(path, null, fileEntryLoaded.bind(this), errorHandler.bind(this));
472         }
473
474         /**
475          * @param {!FileEntry} entry
476          * @this {WebInspector.IsolatedFileSystem}
477          */
478         function fileEntryLoaded(entry)
479         {
480             if (entry.name === newName) {
481                 callback(false);
482                 return;
483             }
484
485             fileEntry = entry;
486             fileEntry.getParent(dirEntryLoaded.bind(this), errorHandler.bind(this));
487         }
488
489         /**
490          * @param {!Entry} entry
491          * @this {WebInspector.IsolatedFileSystem}
492          */
493         function dirEntryLoaded(entry)
494         {
495             dirEntry = entry;
496             dirEntry.getFile(newName, null, newFileEntryLoaded, newFileEntryLoadErrorHandler.bind(this));
497         }
498
499         /**
500          * @param {!FileEntry} entry
501          */
502         function newFileEntryLoaded(entry)
503         {
504             callback(false);
505         }
506
507         /**
508          * @this {WebInspector.IsolatedFileSystem}
509          */
510         function newFileEntryLoadErrorHandler(error)
511         {
512             if (error.code !== FileError.NOT_FOUND_ERR) {
513                 callback(false);
514                 return;
515             }
516             fileEntry.moveTo(dirEntry, newName, fileRenamed, errorHandler.bind(this));
517         }
518
519         /**
520          * @param {!FileEntry} entry
521          */
522         function fileRenamed(entry)
523         {
524             callback(true, entry.name);
525         }
526
527         /**
528          * @this {WebInspector.IsolatedFileSystem}
529          */
530         function errorHandler(error)
531         {
532             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
533             console.error(errorMessage + " when renaming file '" + (this._path + "/" + path) + "' to '" + newName + "'");
534             callback(false);
535         }
536     },
537
538     /**
539      * @param {!DirectoryEntry} dirEntry
540      * @param {function(!Array.<!FileEntry>)} callback
541      */
542     _readDirectory: function(dirEntry, callback)
543     {
544         var dirReader = dirEntry.createReader();
545         var entries = [];
546
547         function innerCallback(results)
548         {
549             if (!results.length)
550                 callback(entries.sort());
551             else {
552                 entries = entries.concat(toArray(results));
553                 dirReader.readEntries(innerCallback, errorHandler);
554             }
555         }
556
557         function toArray(list)
558         {
559             return Array.prototype.slice.call(list || [], 0);
560         }
561
562         dirReader.readEntries(innerCallback, errorHandler);
563
564         function errorHandler(error)
565         {
566             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
567             console.error(errorMessage + " when reading directory '" + dirEntry.fullPath + "'");
568             callback([]);
569         }
570     },
571
572     /**
573      * @param {!DOMFileSystem} domFileSystem
574      * @param {string} path
575      * @param {function(!Array.<!FileEntry>)} callback
576      */
577     _requestEntries: function(domFileSystem, path, callback)
578     {
579         domFileSystem.root.getDirectory(path, null, innerCallback.bind(this), errorHandler);
580
581         /**
582          * @param {!DirectoryEntry} dirEntry
583          * @this {WebInspector.IsolatedFileSystem}
584          */
585         function innerCallback(dirEntry)
586         {
587             this._readDirectory(dirEntry, callback)
588         }
589
590         function errorHandler(error)
591         {
592             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
593             console.error(errorMessage + " when requesting entry '" + path + "'");
594             callback([]);
595         }
596     }
597 }