Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / FileSystemWorkspaceBinding.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} 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         WebInspector.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         WebInspector.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;