2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @param {!WebInspector.IsolatedFileSystemManager} isolatedFileSystemManager
34 * @param {!WebInspector.Workspace} workspace
35 * @param {!WebInspector.NetworkMapping} networkMapping
37 WebInspector.FileSystemWorkspaceBinding = function(isolatedFileSystemManager, workspace, networkMapping)
39 this._isolatedFileSystemManager = isolatedFileSystemManager;
40 this._workspace = workspace;
41 // FIXME: This dependency should be removed from here once we do not need URL to create a UISourceCode.
42 this._networkMapping = networkMapping;
43 this._isolatedFileSystemManager.addEventListener(WebInspector.IsolatedFileSystemManager.Events.FileSystemAdded, this._fileSystemAdded, this);
44 this._isolatedFileSystemManager.addEventListener(WebInspector.IsolatedFileSystemManager.Events.FileSystemRemoved, this._fileSystemRemoved, this);
45 /** @type {!Map.<string, !WebInspector.FileSystemWorkspaceBinding.FileSystem>} */
46 this._boundFileSystems = new Map();
48 /** @type {!Object.<number, function(!Array.<string>)>} */
50 /** @type {!Object.<number, !WebInspector.Progress>} */
51 this._progresses = {};
53 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, this._onIndexingTotalWorkCalculated, this);
54 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.IndexingWorked, this._onIndexingWorked, this);
55 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.IndexingDone, this._onIndexingDone, this);
56 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.SearchCompleted, this._onSearchCompleted, this);
59 WebInspector.FileSystemWorkspaceBinding._styleSheetExtensions = ["css", "scss", "sass", "less"].keySet();
60 WebInspector.FileSystemWorkspaceBinding._documentExtensions = ["htm", "html", "asp", "aspx", "phtml", "jsp"].keySet();
62 WebInspector.FileSystemWorkspaceBinding._lastRequestId = 0;
65 * @param {string} fileSystemPath
68 WebInspector.FileSystemWorkspaceBinding.projectId = function(fileSystemPath)
70 return "filesystem:" + fileSystemPath;
73 WebInspector.FileSystemWorkspaceBinding.prototype = {
75 * @param {!WebInspector.Event} event
77 _fileSystemAdded: function(event)
79 var fileSystem = /** @type {!WebInspector.IsolatedFileSystem} */ (event.data);
80 var boundFileSystem = new WebInspector.FileSystemWorkspaceBinding.FileSystem(this, fileSystem, this._workspace, this._networkMapping);
81 this._boundFileSystems.set(fileSystem.normalizedPath(), boundFileSystem);
85 * @param {!WebInspector.Event} event
87 _fileSystemRemoved: function(event)
89 var fileSystem = /** @type {!WebInspector.IsolatedFileSystem} */ (event.data);
90 var boundFileSystem = this._boundFileSystems.get(fileSystem.normalizedPath());
91 boundFileSystem.dispose();
92 this._boundFileSystems.remove(fileSystem.normalizedPath());
96 * @param {string} projectId
99 fileSystemPath: function(projectId)
101 var fileSystemPath = projectId.substr("filesystem:".length);
102 var normalizedPath = WebInspector.IsolatedFileSystem.normalizePath(fileSystemPath);
103 return projectId.substr("filesystem:".length);
111 return ++WebInspector.FileSystemWorkspaceBinding._lastRequestId;
115 * @param {function(!Array.<string>)} callback
118 registerCallback: function(callback)
120 var requestId = this._nextId();
121 this._callbacks[requestId] = callback;
126 * @param {!WebInspector.Progress} progress
129 registerProgress: function(progress)
131 var requestId = this._nextId();
132 this._progresses[requestId] = progress;
137 * @param {!WebInspector.Event} event
139 _onIndexingTotalWorkCalculated: function(event)
141 var requestId = /** @type {number} */ (event.data["requestId"]);
142 var totalWork = /** @type {number} */ (event.data["totalWork"]);
144 var progress = this._progresses[requestId];
147 progress.setTotalWork(totalWork);
151 * @param {!WebInspector.Event} event
153 _onIndexingWorked: function(event)
155 var requestId = /** @type {number} */ (event.data["requestId"]);
156 var worked = /** @type {number} */ (event.data["worked"]);
158 var progress = this._progresses[requestId];
161 progress.worked(worked);
162 if (progress.isCanceled()) {
163 InspectorFrontendHost.stopIndexing(requestId);
164 this._onIndexingDone(event);
169 * @param {!WebInspector.Event} event
171 _onIndexingDone: function(event)
173 var requestId = /** @type {number} */ (event.data["requestId"]);
175 var progress = this._progresses[requestId];
179 delete this._progresses[requestId];
183 * @param {!WebInspector.Event} event
185 _onSearchCompleted: function(event)
187 var requestId = /** @type {number} */ (event.data["requestId"]);
188 var files = /** @type {!Array.<string>} */ (event.data["files"]);
190 var callback = this._callbacks[requestId];
193 callback.call(null, files);
194 delete this._callbacks[requestId];
200 * @extends {WebInspector.Object}
201 * @implements {WebInspector.ProjectDelegate}
202 * @param {!WebInspector.FileSystemWorkspaceBinding} fileSystemWorkspaceBinding
203 * @param {!WebInspector.IsolatedFileSystem} isolatedFileSystem
204 * @param {!WebInspector.Workspace} workspace
205 * @param {!WebInspector.NetworkMapping} networkMapping
207 WebInspector.FileSystemWorkspaceBinding.FileSystem = function(fileSystemWorkspaceBinding, isolatedFileSystem, workspace, networkMapping)
209 WebInspector.Object.call(this);
210 this._fileSystemWorkspaceBinding = fileSystemWorkspaceBinding;
211 this._fileSystem = isolatedFileSystem;
212 this._fileSystemBaseURL = "file://" + this._fileSystem.normalizedPath() + "/";
213 this._fileSystemProjectURL = "filesystem:" + this._fileSystem.normalizedPath();
214 this._workspace = workspace;
215 // FIXME: This dependency should be removed from here once we do not need URL to create a UISourceCode.
216 this._networkMapping = networkMapping;
218 this._projectId = WebInspector.FileSystemWorkspaceBinding.projectId(this._fileSystem.path());
219 console.assert(!this._workspace.project(this._projectId));
220 this._workspace.addProject(this._projectId, this);
224 WebInspector.FileSystemWorkspaceBinding.FileSystem.prototype = {
231 return WebInspector.projectTypes.FileSystem;
237 fileSystemPath: function()
239 return this._fileSystem.path();
246 displayName: function()
248 var normalizedPath = this._fileSystem.normalizedPath();
249 return normalizedPath.substr(normalizedPath.lastIndexOf("/") + 1);
258 return this._fileSystemProjectURL;
262 * @param {string} path
265 _filePathForPath: function(path)
272 * @param {string} path
273 * @param {function(?string)} callback
275 requestFileContent: function(path, callback)
277 var filePath = this._filePathForPath(path);
278 this._fileSystem.requestFileContent(filePath, callback);
283 * @param {string} path
284 * @param {function(?Date, ?number)} callback
286 requestMetadata: function(path, callback)
288 var filePath = this._filePathForPath(path);
289 this._fileSystem.requestMetadata(filePath, callback);
296 canSetFileContent: function()
303 * @param {string} path
304 * @param {string} newContent
305 * @param {function(?string)} callback
307 setFileContent: function(path, newContent, callback)
309 var filePath = this._filePathForPath(path);
310 this._fileSystem.setFileContent(filePath, newContent, callback.bind(this, ""));
317 canRename: function()
324 * @param {string} path
325 * @param {string} newName
326 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
328 rename: function(path, newName, callback)
330 var filePath = this._filePathForPath(path);
331 this._fileSystem.renameFile(filePath, newName, innerCallback.bind(this));
334 * @param {boolean} success
335 * @param {string=} newName
336 * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
338 function innerCallback(success, newName)
341 callback(false, newName);
344 var validNewName = /** @type {string} */ (newName);
345 console.assert(validNewName);
346 var slash = filePath.lastIndexOf("/");
347 var parentPath = filePath.substring(0, slash);
348 filePath = parentPath + "/" + validNewName;
349 filePath = filePath.substr(1);
350 var newURL = this._networkMapping.urlForPath(this._fileSystem.path(), filePath);
351 var extension = this._extensionForPath(validNewName);
352 var newOriginURL = this._fileSystemBaseURL + filePath;
353 var newContentType = this._contentTypeForExtension(extension);
354 callback(true, validNewName, newURL, newOriginURL, newContentType);
360 * @param {string} path
361 * @param {string} query
362 * @param {boolean} caseSensitive
363 * @param {boolean} isRegex
364 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
366 searchInFileContent: function(path, query, caseSensitive, isRegex, callback)
368 var filePath = this._filePathForPath(path);
369 this._fileSystem.requestFileContent(filePath, contentCallback);
372 * @param {?string} content
374 function contentCallback(content)
377 if (content !== null)
378 result = WebInspector.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex);
385 * @param {!WebInspector.ProjectSearchConfig} searchConfig
386 * @param {!Array.<string>} filesMathingFileQuery
387 * @param {!WebInspector.Progress} progress
388 * @param {function(!Array.<string>)} callback
390 findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback)
392 var result = filesMathingFileQuery;
393 var queriesToRun = searchConfig.queries().slice();
394 if (!queriesToRun.length)
395 queriesToRun.push("");
396 progress.setTotalWork(queriesToRun.length);
397 searchNextQuery.call(this);
400 * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
402 function searchNextQuery()
404 if (!queriesToRun.length) {
409 var query = queriesToRun.shift();
410 this._searchInPath(searchConfig.isRegex() ? "" : query, progress, innerCallback.bind(this));
414 * @param {!Array.<string>} files
415 * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
417 function innerCallback(files)
419 files = files.sort();
421 result = result.intersectOrdered(files, String.naturalOrderComparator);
422 searchNextQuery.call(this);
427 * @param {string} query
428 * @param {!WebInspector.Progress} progress
429 * @param {function(!Array.<string>)} callback
431 _searchInPath: function(query, progress, callback)
433 var requestId = this._fileSystemWorkspaceBinding.registerCallback(innerCallback.bind(this));
434 InspectorFrontendHost.searchInPath(requestId, this._fileSystem.path(), query);
437 * @param {!Array.<string>} files
438 * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
440 function innerCallback(files)
443 * @param {string} fullPath
444 * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
446 function trimAndNormalizeFileSystemPath(fullPath)
448 var trimmedPath = fullPath.substr(this._fileSystem.path().length + 1);
449 if (WebInspector.isWin())
450 trimmedPath = trimmedPath.replace(/\\/g, "/");
454 files = files.map(trimAndNormalizeFileSystemPath.bind(this));
462 * @param {!WebInspector.Progress} progress
464 indexContent: function(progress)
466 progress.setTotalWork(1);
467 var requestId = this._fileSystemWorkspaceBinding.registerProgress(progress);
468 InspectorFrontendHost.indexPath(requestId, this._fileSystem.path());
472 * @param {string} path
475 _extensionForPath: function(path)
477 var extensionIndex = path.lastIndexOf(".");
478 if (extensionIndex === -1)
480 return path.substring(extensionIndex + 1).toLowerCase();
484 * @param {string} extension
485 * @return {!WebInspector.ResourceType}
487 _contentTypeForExtension: function(extension)
489 if (WebInspector.FileSystemWorkspaceBinding._styleSheetExtensions[extension])
490 return WebInspector.resourceTypes.Stylesheet;
491 if (WebInspector.FileSystemWorkspaceBinding._documentExtensions[extension])
492 return WebInspector.resourceTypes.Document;
493 return WebInspector.resourceTypes.Script;
498 this._fileSystem.requestFilesRecursive("", this._addFile.bind(this));
503 * @param {string} path
504 * @param {function()=} callback
506 refresh: function(path, callback)
508 this._fileSystem.requestFilesRecursive(path, this._addFile.bind(this), callback);
513 * @param {string} path
515 excludeFolder: function(path)
517 this._fileSystemWorkspaceBinding._isolatedFileSystemManager.excludedFolderManager().addExcludedFolder(this._fileSystem.path(), path);
522 * @param {string} path
523 * @param {?string} name
524 * @param {string} content
525 * @param {function(?string)} callback
527 createFile: function(path, name, content, callback)
529 this._fileSystem.createFile(path, name, innerCallback.bind(this));
533 * @param {?string} filePath
534 * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
536 function innerCallback(filePath)
542 createFilePath = filePath;
544 contentSet.call(this);
547 this._fileSystem.setFileContent(filePath, content, contentSet.bind(this));
551 * @this {WebInspector.FileSystemWorkspaceBinding.FileSystem}
553 function contentSet()
555 this._addFile(createFilePath);
556 callback(createFilePath);
562 * @param {string} path
564 deleteFile: function(path)
566 this._fileSystem.deleteFile(path);
567 this._removeFile(path);
575 this._fileSystemWorkspaceBinding._isolatedFileSystemManager.removeFileSystem(this._fileSystem.path());
579 * @param {string} filePath
581 _addFile: function(filePath)
584 console.assert(false);
586 var slash = filePath.lastIndexOf("/");
587 var parentPath = filePath.substring(0, slash);
588 var name = filePath.substring(slash + 1);
590 var url = this._networkMapping.urlForPath(this._fileSystem.path(), filePath);
591 var extension = this._extensionForPath(name);
592 var contentType = this._contentTypeForExtension(extension);
594 var fileDescriptor = new WebInspector.FileDescriptor(parentPath, name, this._fileSystemBaseURL + filePath, url, contentType);
595 this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileAdded, fileDescriptor);
599 * @param {string} path
601 _removeFile: function(path)
603 this.dispatchEventToListeners(WebInspector.ProjectDelegate.Events.FileRemoved, path);
608 this._workspace.removeProject(this._projectId);
611 __proto__: WebInspector.Object.prototype
615 * @type {!WebInspector.FileSystemWorkspaceBinding}
617 WebInspector.fileSystemWorkspaceBinding;