Home | History | Annotate | Download | only in front_end
      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  */
     36 WebInspector.IsolatedFileSystem = function(manager, path, name, rootURL)
     37 {
     38     this._manager = manager;
     39     this._path = path;
     40     this._name = name;
     41     this._rootURL = rootURL;
     42 }
     43 
     44 WebInspector.IsolatedFileSystem.errorMessage = function(error)
     45 {
     46     var msg;
     47     switch (error.code) {
     48     case FileError.QUOTA_EXCEEDED_ERR:
     49         msg = "QUOTA_EXCEEDED_ERR";
     50         break;
     51     case FileError.NOT_FOUND_ERR:
     52         msg = "NOT_FOUND_ERR";
     53         break;
     54     case FileError.SECURITY_ERR:
     55         msg = "SECURITY_ERR";
     56         break;
     57     case FileError.INVALID_MODIFICATION_ERR:
     58         msg = "INVALID_MODIFICATION_ERR";
     59         break;
     60     case FileError.INVALID_STATE_ERR:
     61         msg = "INVALID_STATE_ERR";
     62         break;
     63     default:
     64         msg = "Unknown Error";
     65         break;
     66     };
     67 
     68     return "File system error: " + msg;
     69 }
     70 
     71 WebInspector.IsolatedFileSystem.prototype = {
     72     /**
     73      * @return {string}
     74      */
     75     path: function()
     76     {
     77         return this._path;
     78     },
     79 
     80     /**
     81      * @return {string}
     82      */
     83     name: function()
     84     {
     85         return this._name;
     86     },
     87 
     88     /**
     89      * @return {string}
     90      */
     91     rootURL: function()
     92     {
     93         return this._rootURL;
     94     },
     95 
     96     /**
     97      * @param {function(?DOMFileSystem)} callback
     98      */
     99     _requestFileSystem: function(callback)
    100     {
    101         this._manager.requestDOMFileSystem(this._path, callback);
    102     },
    103 
    104     /**
    105      * @param {string} path
    106      * @param {function(string)} callback
    107      */
    108     requestFilesRecursive: function(path, callback)
    109     {
    110         this._requestFileSystem(fileSystemLoaded.bind(this));
    111 
    112         var domFileSystem;
    113         /**
    114          * @param {?DOMFileSystem} fs
    115          * @this {WebInspector.IsolatedFileSystem}
    116          */
    117         function fileSystemLoaded(fs)
    118         {
    119             domFileSystem = /** @type {!DOMFileSystem} */ (fs);
    120             console.assert(domFileSystem);
    121             this._requestEntries(domFileSystem, path, innerCallback.bind(this));
    122         }
    123 
    124         /**
    125          * @param {!Array.<!FileEntry>} entries
    126          * @this {WebInspector.IsolatedFileSystem}
    127          */
    128         function innerCallback(entries)
    129         {
    130             for (var i = 0; i < entries.length; ++i) {
    131                 var entry = entries[i];
    132                 if (!entry.isDirectory) {
    133                     if (this._manager.mapping().isFileExcluded(this._path, entry.fullPath))
    134                         continue;
    135                     callback(entry.fullPath.substr(1));
    136                 }
    137                 else {
    138                     if (this._manager.mapping().isFileExcluded(this._path, entry.fullPath + "/"))
    139                         continue;
    140                     this._requestEntries(domFileSystem, entry.fullPath, innerCallback.bind(this));
    141                 }
    142             }
    143         }
    144     },
    145 
    146     /**
    147      * @param {string} path
    148      * @param {?string} name
    149      * @param {function(?string)} callback
    150      */
    151     createFile: function(path, name, callback)
    152     {
    153         this._requestFileSystem(fileSystemLoaded.bind(this));
    154         var newFileIndex = 1;
    155         if (!name)
    156             name = "NewFile";
    157         var nameCandidate;
    158 
    159         /**
    160          * @param {?DOMFileSystem} fs
    161          * @this {WebInspector.IsolatedFileSystem}
    162          */
    163         function fileSystemLoaded(fs)
    164         {
    165             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
    166             console.assert(domFileSystem);
    167             domFileSystem.root.getDirectory(path, null, dirEntryLoaded.bind(this), errorHandler.bind(this));
    168         }
    169 
    170         /**
    171          * @param {!DirectoryEntry} dirEntry
    172          * @this {WebInspector.IsolatedFileSystem}
    173          */
    174         function dirEntryLoaded(dirEntry)
    175         {
    176             var nameCandidate = name;
    177             if (newFileIndex > 1)
    178                 nameCandidate += newFileIndex;
    179             ++newFileIndex;
    180             dirEntry.getFile(nameCandidate, { create: true, exclusive: true }, fileCreated, fileCreationError.bind(this));
    181 
    182             function fileCreated(entry)
    183             {
    184                 callback(entry.fullPath.substr(1));
    185             }
    186 
    187             /**
    188              * @this {WebInspector.IsolatedFileSystem}
    189              */
    190             function fileCreationError(error)
    191             {
    192                 if (error.code === FileError.INVALID_MODIFICATION_ERR) {
    193                     dirEntryLoaded.call(this, dirEntry);
    194                     return;
    195                 }
    196 
    197                 var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    198                 console.error(errorMessage + " when testing if file exists '" + (this._path + "/" + path + "/" + nameCandidate) + "'");
    199                 callback(null);
    200             }
    201         }
    202 
    203         /**
    204          * @this {WebInspector.IsolatedFileSystem}
    205          */
    206         function errorHandler(error)
    207         {
    208             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    209             var filePath = this._path + "/" + path;
    210             if (nameCandidate)
    211                 filePath += "/" + nameCandidate;
    212             console.error(errorMessage + " when getting content for file '" + (filePath) + "'");
    213             callback(null);
    214         }
    215     },
    216 
    217     /**
    218      * @param {string} path
    219      */
    220     deleteFile: function(path)
    221     {
    222         this._requestFileSystem(fileSystemLoaded.bind(this));
    223 
    224         /**
    225          * @param {?DOMFileSystem} fs
    226          * @this {WebInspector.IsolatedFileSystem}
    227          */
    228         function fileSystemLoaded(fs)
    229         {
    230             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
    231             console.assert(domFileSystem);
    232             domFileSystem.root.getFile(path, null, fileEntryLoaded.bind(this), errorHandler.bind(this));
    233         }
    234 
    235         /**
    236          * @param {!FileEntry} fileEntry
    237          * @this {WebInspector.IsolatedFileSystem}
    238          */
    239         function fileEntryLoaded(fileEntry)
    240         {
    241             fileEntry.remove(fileEntryRemoved.bind(this), errorHandler.bind(this));
    242         }
    243 
    244         function fileEntryRemoved()
    245         {
    246         }
    247 
    248         /**
    249          * @param {!FileError} error
    250          * @this {WebInspector.IsolatedFileSystem}
    251          */
    252         function errorHandler(error)
    253         {
    254             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    255             console.error(errorMessage + " when deleting file '" + (this._path + "/" + path) + "'");
    256         }
    257     },
    258 
    259     /**
    260      * @param {string} path
    261      * @param {function(?Date, ?number)} callback
    262      */
    263     requestMetadata: function(path, callback)
    264     {
    265         this._requestFileSystem(fileSystemLoaded.bind(this));
    266 
    267         /**
    268          * @param {?DOMFileSystem} fs
    269          */
    270         function fileSystemLoaded(fs)
    271         {
    272             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
    273             console.assert(domFileSystem);
    274             domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler);
    275         }
    276 
    277         /**
    278          * @param {!FileEntry} entry
    279          */
    280         function fileEntryLoaded(entry)
    281         {
    282             entry.getMetadata(successHandler, errorHandler);
    283         }
    284 
    285         /**
    286          * @param {!Metadata} metadata
    287          */
    288         function successHandler(metadata)
    289         {
    290             callback(metadata.modificationTime, metadata.size);
    291         }
    292 
    293         /**
    294          * @param {!FileError} error
    295          */
    296         function errorHandler(error)
    297         {
    298             callback(null, null);
    299         }
    300     },
    301 
    302     /**
    303      * @param {string} path
    304      * @param {function(?string)} callback
    305      */
    306     requestFileContent: function(path, callback)
    307     {
    308         this._requestFileSystem(fileSystemLoaded.bind(this));
    309 
    310         /**
    311          * @param {?DOMFileSystem} fs
    312          * @this {WebInspector.IsolatedFileSystem}
    313          */
    314         function fileSystemLoaded(fs)
    315         {
    316             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
    317             console.assert(domFileSystem);
    318             domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler.bind(this));
    319         }
    320 
    321         /**
    322          * @param {!FileEntry} entry
    323          * @this {WebInspector.IsolatedFileSystem}
    324          */
    325         function fileEntryLoaded(entry)
    326         {
    327             entry.file(fileLoaded, errorHandler.bind(this));
    328         }
    329 
    330         /**
    331          * @param {!Blob} file
    332          */
    333         function fileLoaded(file)
    334         {
    335             var reader = new FileReader();
    336             reader.onloadend = readerLoadEnd;
    337             reader.readAsText(file);
    338         }
    339 
    340         /**
    341          * @this {!FileReader}
    342          */
    343         function readerLoadEnd()
    344         {
    345             callback(/** @type {string} */ (this.result));
    346         }
    347 
    348         /**
    349          * @this {WebInspector.IsolatedFileSystem}
    350          */
    351         function errorHandler(error)
    352         {
    353             if (error.code === FileError.NOT_FOUND_ERR) {
    354                 callback(null);
    355                 return;
    356             }
    357 
    358             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    359             console.error(errorMessage + " when getting content for file '" + (this._path + "/" + path) + "'");
    360             callback(null);
    361         }
    362     },
    363 
    364     /**
    365      * @param {string} path
    366      * @param {string} content
    367      * @param {function()} callback
    368      */
    369     setFileContent: function(path, content, callback)
    370     {
    371         this._requestFileSystem(fileSystemLoaded);
    372 
    373         /**
    374          * @param {?DOMFileSystem} fs
    375          * @this {WebInspector.IsolatedFileSystem}
    376          */
    377         function fileSystemLoaded(fs)
    378         {
    379             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
    380             console.assert(domFileSystem);
    381             domFileSystem.root.getFile(path, { create: true }, fileEntryLoaded, errorHandler.bind(this));
    382         }
    383 
    384         /**
    385          * @param {!FileEntry} entry
    386          * @this {WebInspector.IsolatedFileSystem}
    387          */
    388         function fileEntryLoaded(entry)
    389         {
    390             entry.createWriter(fileWriterCreated, errorHandler.bind(this));
    391         }
    392 
    393         /**
    394          * @param {!FileWriter} fileWriter
    395          * @this {WebInspector.IsolatedFileSystem}
    396          */
    397         function fileWriterCreated(fileWriter)
    398         {
    399             fileWriter.onerror = errorHandler.bind(this);
    400             fileWriter.onwriteend = fileTruncated;
    401             fileWriter.truncate(0);
    402 
    403             function fileTruncated()
    404             {
    405                 fileWriter.onwriteend = writerEnd;
    406                 var blob = new Blob([content], { type: "text/plain" });
    407                 fileWriter.write(blob);
    408             }
    409         }
    410 
    411         function writerEnd()
    412         {
    413             callback();
    414         }
    415 
    416         /**
    417          * @this {WebInspector.IsolatedFileSystem}
    418          */
    419         function errorHandler(error)
    420         {
    421             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    422             console.error(errorMessage + " when setting content for file '" + (this._path + "/" + path) + "'");
    423             callback();
    424         }
    425     },
    426 
    427     /**
    428      * @param {string} path
    429      * @param {string} newName
    430      * @param {function(boolean, string=)} callback
    431      */
    432     renameFile: function(path, newName, callback)
    433     {
    434         newName = newName ? newName.trim() : newName;
    435         if (!newName || newName.indexOf("/") !== -1) {
    436             callback(false);
    437             return;
    438         }
    439         var fileEntry;
    440         var dirEntry;
    441         var newFileEntry;
    442         this._requestFileSystem(fileSystemLoaded);
    443 
    444         /**
    445          * @param {?DOMFileSystem} fs
    446          * @this {WebInspector.IsolatedFileSystem}
    447          */
    448         function fileSystemLoaded(fs)
    449         {
    450             var domFileSystem = /** @type {!DOMFileSystem} */ (fs);
    451             console.assert(domFileSystem);
    452             domFileSystem.root.getFile(path, null, fileEntryLoaded, errorHandler.bind(this));
    453         }
    454 
    455         /**
    456          * @param {!FileEntry} entry
    457          * @this {WebInspector.IsolatedFileSystem}
    458          */
    459         function fileEntryLoaded(entry)
    460         {
    461             if (entry.name === newName) {
    462                 callback(false);
    463                 return;
    464             }
    465 
    466             fileEntry = entry;
    467             fileEntry.getParent(dirEntryLoaded, errorHandler.bind(this));
    468         }
    469 
    470         /**
    471          * @param {!Entry} entry
    472          */
    473         function dirEntryLoaded(entry)
    474         {
    475             dirEntry = entry;
    476             dirEntry.getFile(newName, null, newFileEntryLoaded, newFileEntryLoadErrorHandler);
    477         }
    478 
    479         /**
    480          * @param {!FileEntry} entry
    481          */
    482         function newFileEntryLoaded(entry)
    483         {
    484             callback(false);
    485         }
    486 
    487         /**
    488          * @this {WebInspector.IsolatedFileSystem}
    489          */
    490         function newFileEntryLoadErrorHandler(error)
    491         {
    492             if (error.code !== FileError.NOT_FOUND_ERR) {
    493                 callback(false);
    494                 return;
    495             }
    496             fileEntry.moveTo(dirEntry, newName, fileRenamed, errorHandler.bind(this));
    497         }
    498 
    499         /**
    500          * @param {!FileEntry} entry
    501          */
    502         function fileRenamed(entry)
    503         {
    504             callback(true, entry.name);
    505         }
    506 
    507         /**
    508          * @this {WebInspector.IsolatedFileSystem}
    509          */
    510         function errorHandler(error)
    511         {
    512             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    513             console.error(errorMessage + " when renaming file '" + (this._path + "/" + path) + "' to '" + newName + "'");
    514             callback(false);
    515         }
    516     },
    517 
    518     /**
    519      * @param {!DirectoryEntry} dirEntry
    520      * @param {function(!Array.<!FileEntry>)} callback
    521      */
    522     _readDirectory: function(dirEntry, callback)
    523     {
    524         var dirReader = dirEntry.createReader();
    525         var entries = [];
    526 
    527         function innerCallback(results)
    528         {
    529             if (!results.length)
    530                 callback(entries.sort());
    531             else {
    532                 entries = entries.concat(toArray(results));
    533                 dirReader.readEntries(innerCallback, errorHandler);
    534             }
    535         }
    536 
    537         function toArray(list)
    538         {
    539             return Array.prototype.slice.call(list || [], 0);
    540         }
    541 
    542         dirReader.readEntries(innerCallback, errorHandler);
    543 
    544         function errorHandler(error)
    545         {
    546             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    547             console.error(errorMessage + " when reading directory '" + dirEntry.fullPath + "'");
    548             callback([]);
    549         }
    550     },
    551 
    552     /**
    553      * @param {!DOMFileSystem} domFileSystem
    554      * @param {string} path
    555      * @param {function(!Array.<!FileEntry>)} callback
    556      */
    557     _requestEntries: function(domFileSystem, path, callback)
    558     {
    559         domFileSystem.root.getDirectory(path, null, innerCallback.bind(this), errorHandler);
    560 
    561         /**
    562          * @param {!DirectoryEntry} dirEntry
    563          * @this {WebInspector.IsolatedFileSystem}
    564          */
    565         function innerCallback(dirEntry)
    566         {
    567             this._readDirectory(dirEntry, callback)
    568         }
    569 
    570         function errorHandler(error)
    571         {
    572             var errorMessage = WebInspector.IsolatedFileSystem.errorMessage(error);
    573             console.error(errorMessage + " when requesting entry '" + path + "'");
    574             callback([]);
    575         }
    576     }
    577 }
    578