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