• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
36WebInspector.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
51WebInspector.FileSystemWorkspaceBinding._scriptExtensions = ["js", "java", "coffee", "ts", "dart"].keySet();
52WebInspector.FileSystemWorkspaceBinding._styleSheetExtensions = ["css", "scss", "sass", "less"].keySet();
53WebInspector.FileSystemWorkspaceBinding._documentExtensions = ["htm", "html", "asp", "aspx", "phtml", "jsp"].keySet();
54
55WebInspector.FileSystemWorkspaceBinding._lastRequestId = 0;
56
57/**
58 * @param {string} fileSystemPath
59 * @return {string}
60 */
61WebInspector.FileSystemWorkspaceBinding.projectId = function(fileSystemPath)
62{
63    return "filesystem:" + fileSystemPath;
64}
65
66WebInspector.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 */
191WebInspector.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
204WebInspector.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 */
588WebInspector.fileSystemWorkspaceBinding;
589