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} isolatedFileSystemManager
     34  * @param {!WebInspector.Workspace} workspace
     35  */
     36 WebInspector.FileSystemWorkspaceBinding = function(isolatedFileSystemManager, workspace)
     37 {
     38     this._isolatedFileSystemManager = isolatedFileSystemManager;
     39     this._workspace = workspace;
     40     this._isolatedFileSystemManager.addEventListener(WebInspector.IsolatedFileSystemManager.Events.FileSystemAdded, this._fileSystemAdded, this);
     41     this._isolatedFileSystemManager.addEventListener(WebInspector.IsolatedFileSystemManager.Events.FileSystemRemoved, this._fileSystemRemoved, this);
     42     /** @type {!StringMap.<!WebInspector.FileSystemWorkspaceBinding.FileSystem>} */
     43     this._boundFileSystems = new StringMap();
     44 
     45     /** @type {!Object.<number, function(!Array.<string>)>} */
     46     this._callbacks = {};
     47     /** @type {!Object.<number, !WebInspector.Progress>} */
     48     this._progresses = {};
     49 }
     50 
     51 WebInspector.FileSystemWorkspaceBinding._scriptExtensions = ["js", "java", "coffee", "ts", "dart"].keySet();
     52 WebInspector.FileSystemWorkspaceBinding._styleSheetExtensions = ["css", "scss", "sass", "less"].keySet();
     53 WebInspector.FileSystemWorkspaceBinding._documentExtensions = ["htm", "html", "asp", "aspx", "phtml", "jsp"].keySet();
     54 
     55 WebInspector.FileSystemWorkspaceBinding._lastRequestId = 0;
     56 
     57 /**
     58  * @param {string} fileSystemPath
     59  * @return {string}
     60  */
     61 WebInspector.FileSystemWorkspaceBinding.projectId = function(fileSystemPath)
     62 {
     63     return "filesystem:" + fileSystemPath;
     64 }
     65 
     66 WebInspector.FileSystemWorkspaceBinding.prototype = {
     67     /**
     68      * @param {!WebInspector.Event} event
     69      */
     70     _fileSystemAdded: function(event)
     71     {
     72         var fileSystem = /** @type {!WebInspector.IsolatedFileSystem} */ (event.data);
     73         var boundFileSystem = new WebInspector.FileSystemWorkspaceBinding.FileSystem(this, fileSystem, this._workspace);
     74         this._boundFileSystems.put(fileSystem.normalizedPath(), boundFileSystem);
     75     },
     76 
     77     /**
     78      * @param {!WebInspector.Event} event
     79      */
     80     _fileSystemRemoved: function(event)
     81     {
     82         var fileSystem = /** @type {!WebInspector.IsolatedFileSystem} */ (event.data);
     83         var boundFileSystem = this._boundFileSystems.get(fileSystem.normalizedPath());
     84         boundFileSystem.dispose();
     85         this._boundFileSystems.remove(fileSystem.normalizedPath());
     86     },
     87 
     88     /**
     89      * @param {string} projectId
     90      * @return {string}
     91      */
     92     fileSystemPath: function(projectId)
     93     {
     94         var fileSystemPath = projectId.substr("filesystem:".length);
     95         var normalizedPath = WebInspector.IsolatedFileSystem.normalizePath(fileSystemPath);
     96         var boundFileSystem = this._boundFileSystems.get(normalizedPath);
     97         return projectId.substr("filesystem:".length);
     98     },
     99 
    100     /**
    101      * @return {number}
    102      */
    103     _nextId: function()
    104     {
    105         return ++WebInspector.FileSystemWorkspaceBinding._lastRequestId;
    106     },
    107 
    108     /**
    109      * @param {function(!Array.<string>)} callback
    110      * @return {number}
    111      */
    112     registerCallback: function(callback)
    113     {
    114         var requestId = this._nextId();
    115         this._callbacks[requestId] = callback;
    116         return requestId;
    117     },
    118 
    119     /**
    120      * @param {!WebInspector.Progress} progress
    121      * @return {number}
    122      */
    123     registerProgress: function(progress)
    124     {
    125         var requestId = this._nextId();
    126         this._progresses[requestId] = progress;
    127         return requestId;
    128     },
    129 
    130     /**
    131      * @param {number} requestId
    132      * @param {string} fileSystemPath
    133      * @param {number} totalWork
    134      */
    135     indexingTotalWorkCalculated: function(requestId, fileSystemPath, totalWork)
    136     {
    137         var progress = this._progresses[requestId];
    138         if (!progress)
    139             return;
    140         progress.setTotalWork(totalWork);
    141     },
    142 
    143     /**
    144      * @param {number} requestId
    145      * @param {string} fileSystemPath
    146      * @param {number} worked
    147      */
    148     indexingWorked: function(requestId, fileSystemPath, worked)
    149     {
    150         var progress = this._progresses[requestId];
    151         if (!progress)
    152             return;
    153         progress.worked(worked);
    154     },
    155 
    156     /**
    157      * @param {number} requestId
    158      * @param {string} fileSystemPath
    159      */
    160     indexingDone: function(requestId, fileSystemPath)
    161     {
    162         var progress = this._progresses[requestId];
    163         if (!progress)
    164             return;
    165         progress.done();
    166         delete this._progresses[requestId];
    167     },
    168 
    169     /**
    170      * @param {number} requestId
    171      * @param {string} fileSystemPath
    172      * @param {!Array.<string>} files
    173      */
    174     searchCompleted: function(requestId, fileSystemPath, files)
    175     {
    176         var callback = this._callbacks[requestId];
    177         if (!callback)
    178             return;
    179         callback.call(null, files);
    180         delete this._callbacks[requestId];
    181     },
    182 }
    183 
    184 /**
    185  * @constructor
    186  * @implements {WebInspector.ProjectDelegate}
    187  * @param {!WebInspector.IsolatedFileSystem} isolatedFileSystem
    188  * @param {!WebInspector.Workspace} workspace
    189  * @param {!WebInspector.FileSystemWorkspaceBinding} fileSystemWorkspaceBinding
    190  */
    191 WebInspector.FileSystemWorkspaceBinding.FileSystem = function(fileSystemWorkspaceBinding, isolatedFileSystem, workspace)
    192 {
    193     this._fileSystemWorkspaceBinding = fileSystemWorkspaceBinding;
    194     this._fileSystem = isolatedFileSystem;
    195     this._fileSystemURL = "file://" + this._fileSystem.normalizedPath() + "/";
    196     this._workspace = workspace;
    197 
    198     this._projectId = WebInspector.FileSystemWorkspaceBinding.projectId(this._fileSystem.path());
    199     console.assert(!this._workspace.project(this._projectId));
    200     this._projectStore = this._workspace.addProject(this._projectId, this);
    201     this.populate();
    202 }
    203 
    204 WebInspector.FileSystemWorkspaceBinding.FileSystem.prototype = {
    205     /**
    206      * @return {string}
    207      */
    208     type: function()
    209     {
    210         return WebInspector.projectTypes.FileSystem;
    211     },
    212 
    213     /**
    214      * @return {string}
    215      */
    216     fileSystemPath: function()
    217     {
    218         return this._fileSystem.path();
    219     },
    220 
    221     /**
    222      * @return {string}
    223      */
    224     displayName: function()
    225     {
    226         var normalizedPath = this._fileSystem.normalizedPath();
    227         return normalizedPath.substr(normalizedPath.lastIndexOf("/") + 1);
    228     },
    229 
    230     /**
    231      * @param {string} path
    232      * @return {string}
    233      */
    234     _filePathForPath: function(path)
    235     {
    236         return "/" + path;
    237     },
    238 
    239     /**
    240      * @param {string} path
    241      * @param {function(?string)} callback
    242      */
    243     requestFileContent: function(path, callback)
    244     {
    245         var filePath = this._filePathForPath(path);
    246         this._fileSystem.requestFileContent(filePath, callback);
    247     },
    248 
    249     /**
    250      * @param {string} path
    251      * @param {function(?Date, ?number)} callback
    252      */
    253     requestMetadata: function(path, callback)
    254     {
    255         var filePath = this._filePathForPath(path);
    256         this._fileSystem.requestMetadata(filePath, callback);
    257     },
    258 
    259     /**
    260      * @return {boolean}
    261      */
    262     canSetFileContent: function()
    263     {
    264         return true;
    265     },
    266 
    267     /**
    268      * @param {string} path
    269      * @param {string} newContent
    270      * @param {function(?string)} callback
    271      */
    272     setFileContent: function(path, newContent, callback)
    273     {
    274         var filePath = this._filePathForPath(path);
    275         this._fileSystem.setFileContent(filePath, newContent, callback.bind(this, ""));
    276     },
    277 
    278     /**
    279      * @return {boolean}
    280      */
    281     canRename: function()
    282     {
    283         return true;
    284     },
    285 
    286     /**
    287      * @param {string} path
    288      * @param {string} newName
    289      * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
    290      */
    291     rename: function(path, newName, callback)
    292     {
    293         var filePath = this._filePathForPath(path);
    294         this._fileSystem.renameFile(filePath, newName, innerCallback.bind(this));
    295 
    296         /**
    297          * @param {boolean} success
    298          * @param {string=} newName
    299          * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
    300          */
    301         function innerCallback(success, newName)
    302         {
    303             if (!success) {
    304                 callback(false, newName);
    305                 return;
    306             }
    307             var validNewName = /** @type {string} */ (newName);
    308             console.assert(validNewName);
    309             var slash = filePath.lastIndexOf("/");
    310             var parentPath = filePath.substring(0, slash);
    311             filePath = parentPath + "/" + validNewName;
    312             var newURL = this._workspace.urlForPath(this._fileSystem.path(), filePath);
    313             var extension = this._extensionForPath(validNewName);
    314             var newOriginURL = this._fileSystemURL + filePath
    315             var newContentType = this._contentTypeForExtension(extension);
    316             callback(true, validNewName, newURL, newOriginURL, newContentType);
    317         }
    318     },
    319 
    320     /**
    321      * @param {string} path
    322      * @param {string} query
    323      * @param {boolean} caseSensitive
    324      * @param {boolean} isRegex
    325      * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
    326      */
    327     searchInFileContent: function(path, query, caseSensitive, isRegex, callback)
    328     {
    329         var filePath = this._filePathForPath(path);
    330         this._fileSystem.requestFileContent(filePath, contentCallback);
    331 
    332         /**
    333          * @param {?string} content
    334          */
    335         function contentCallback(content)
    336         {
    337             var result = [];
    338             if (content !== null)
    339                 result = WebInspector.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex);
    340             callback(result);
    341         }
    342     },
    343 
    344     /**
    345      * @param {!WebInspector.ProjectSearchConfig} searchConfig
    346      * @param {!WebInspector.Progress} progress
    347      * @param {function(!Array.<string>)} callback
    348      */
    349     findFilesMatchingSearchRequest: function(searchConfig, progress, callback)
    350     {
    351         var result = null;
    352         var queriesToRun = searchConfig.queries().slice();
    353         if (!queriesToRun.length)
    354             queriesToRun.push("");
    355         progress.setTotalWork(queriesToRun.length);
    356         searchNextQuery.call(this);
    357 
    358         /**
    359          * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
    360          */
    361         function searchNextQuery()
    362         {
    363             if (!queriesToRun.length) {
    364                 matchFileQueries.call(null, result);
    365                 return;
    366             }
    367             var query = queriesToRun.shift();
    368             this._searchInPath(searchConfig.isRegex() ? "" : query, progress, innerCallback.bind(this));
    369         }
    370 
    371         /**
    372          * @param {!Array.<string>} files
    373          * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
    374          */
    375         function innerCallback(files)
    376         {
    377             files = files.sort();
    378             progress.worked(1);
    379             if (!result)
    380                 result = files;
    381             else
    382                 result = result.intersectOrdered(files, String.naturalOrderComparator);
    383             searchNextQuery.call(this);
    384         }
    385 
    386         /**
    387          * @param {!Array.<string>} files
    388          */
    389         function matchFileQueries(files)
    390         {
    391             files = files.filter(searchConfig.filePathMatchesFileQuery.bind(searchConfig));
    392             progress.done();
    393             callback(files);
    394         }
    395     },
    396 
    397     /**
    398      * @param {string} query
    399      * @param {!WebInspector.Progress} progress
    400      * @param {function(!Array.<string>)} callback
    401      */
    402     _searchInPath: function(query, progress, callback)
    403     {
    404         var requestId = this._fileSystemWorkspaceBinding.registerCallback(innerCallback.bind(this));
    405         InspectorFrontendHost.searchInPath(requestId, this._fileSystem.path(), query);
    406 
    407         /**
    408          * @param {!Array.<string>} files
    409          * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
    410          */
    411         function innerCallback(files)
    412         {
    413             /**
    414              * @param {string} fullPath
    415              * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
    416              */
    417             function trimAndNormalizeFileSystemPath(fullPath)
    418             {
    419                 var trimmedPath = fullPath.substr(this._fileSystem.path().length + 1);
    420                 if (WebInspector.isWin())
    421                     trimmedPath = trimmedPath.replace(/\\/g, "/");
    422                 return trimmedPath;
    423             }
    424 
    425             files = files.map(trimAndNormalizeFileSystemPath.bind(this));
    426             progress.worked(1);
    427             callback(files);
    428         }
    429     },
    430 
    431     /**
    432      * @param {!WebInspector.Progress} progress
    433      */
    434     indexContent: function(progress)
    435     {
    436         progress.setTotalWork(1);
    437         var requestId = this._fileSystemWorkspaceBinding.registerProgress(progress);
    438         progress.addEventListener(WebInspector.Progress.Events.Canceled, this._indexingCanceled.bind(this, requestId));
    439         InspectorFrontendHost.indexPath(requestId, this._fileSystem.path());
    440     },
    441 
    442     /**
    443      * @param {number} requestId
    444      */
    445     _indexingCanceled: function(requestId)
    446     {
    447         InspectorFrontendHost.stopIndexing(requestId);
    448     },
    449 
    450     /**
    451      * @param {string} path
    452      * @return {string}
    453      */
    454     _extensionForPath: function(path)
    455     {
    456         var extensionIndex = path.lastIndexOf(".");
    457         if (extensionIndex === -1)
    458             return "";
    459         return path.substring(extensionIndex + 1).toLowerCase();
    460     },
    461 
    462     /**
    463      * @param {string} extension
    464      * @return {!WebInspector.ResourceType}
    465      */
    466     _contentTypeForExtension: function(extension)
    467     {
    468         if (WebInspector.FileSystemWorkspaceBinding._scriptExtensions[extension])
    469             return WebInspector.resourceTypes.Script;
    470         if (WebInspector.FileSystemWorkspaceBinding._styleSheetExtensions[extension])
    471             return WebInspector.resourceTypes.Stylesheet;
    472         if (WebInspector.FileSystemWorkspaceBinding._documentExtensions[extension])
    473             return WebInspector.resourceTypes.Document;
    474         return WebInspector.resourceTypes.Other;
    475     },
    476 
    477     populate: function()
    478     {
    479         this._fileSystem.requestFilesRecursive("", this._addFile.bind(this));
    480     },
    481 
    482     /**
    483      * @param {string} path
    484      */
    485     refresh: function(path)
    486     {
    487         this._fileSystem.requestFilesRecursive(path, this._addFile.bind(this));
    488     },
    489 
    490     /**
    491      * @param {string} path
    492      */
    493     excludeFolder: function(path)
    494     {
    495         this._fileSystemWorkspaceBinding._isolatedFileSystemManager.mapping().addExcludedFolder(this._fileSystem.path(), path);
    496     },
    497 
    498     /**
    499      * @param {string} path
    500      * @param {?string} name
    501      * @param {string} content
    502      * @param {function(?string)} callback
    503      */
    504     createFile: function(path, name, content, callback)
    505     {
    506         this._fileSystem.createFile(path, name, innerCallback.bind(this));
    507         var createFilePath;
    508 
    509         /**
    510          * @param {?string} filePath
    511          * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
    512          */
    513         function innerCallback(filePath)
    514         {
    515             if (!filePath) {
    516                 callback(null);
    517                 return;
    518             }
    519             createFilePath = filePath;
    520             if (!content) {
    521                 contentSet.call(this);
    522                 return;
    523             }
    524             this._fileSystem.setFileContent(filePath, content, contentSet.bind(this));
    525         }
    526 
    527         /**
    528          * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
    529          */
    530         function contentSet()
    531         {
    532             this._addFile(createFilePath);
    533             callback(createFilePath);
    534         }
    535     },
    536 
    537     /**
    538      * @param {string} path
    539      */
    540     deleteFile: function(path)
    541     {
    542         this._fileSystem.deleteFile(path);
    543         this._removeFile(path);
    544     },
    545 
    546     remove: function()
    547     {
    548         this._fileSystemWorkspaceBinding._isolatedFileSystemManager.removeFileSystem(this._fileSystem.path());
    549     },
    550 
    551     /**
    552      * @param {string} filePath
    553      */
    554     _addFile: function(filePath)
    555     {
    556         if (!filePath)
    557             console.assert(false);
    558 
    559         var slash = filePath.lastIndexOf("/");
    560         var parentPath = filePath.substring(0, slash);
    561         var name = filePath.substring(slash + 1);
    562 
    563         var url = this._workspace.urlForPath(this._fileSystem.path(), filePath);
    564         var extension = this._extensionForPath(name);
    565         var contentType = this._contentTypeForExtension(extension);
    566 
    567         var fileDescriptor = new WebInspector.FileDescriptor(parentPath, name, this._fileSystemURL + filePath, url, contentType);
    568         this._projectStore.addFile(fileDescriptor);
    569     },
    570 
    571     /**
    572      * @param {string} path
    573      */
    574     _removeFile: function(path)
    575     {
    576         this._projectStore.removeFile(path);
    577     },
    578 
    579     dispose: function()
    580     {
    581         this._workspace.removeProject(this._projectId);
    582     }
    583 }
    584 
    585 /**
    586  * @type {!WebInspector.FileSystemWorkspaceBinding}
    587  */
    588 WebInspector.fileSystemWorkspaceBinding;
    589