2 * Copyright (C) 2012 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.
34 WebInspector.ProjectSearchConfig = function() {}
36 WebInspector.ProjectSearchConfig.prototype = {
40 query: function() { },
45 ignoreCase: function() { },
50 isRegex: function() { },
53 * @return {!Array.<string>}
55 queries: function() { },
58 * @param {string} filePath
61 filePathMatchesFileQuery: function(filePath) { }
66 * @param {string} parentPath
67 * @param {string} name
68 * @param {string} originURL
70 * @param {!WebInspector.ResourceType} contentType
72 WebInspector.FileDescriptor = function(parentPath, name, originURL, url, contentType)
74 this.parentPath = parentPath;
76 this.originURL = originURL;
78 this.contentType = contentType;
84 WebInspector.ProjectDelegate = function() { }
86 WebInspector.ProjectDelegate.prototype = {
95 displayName: function() { },
103 * @param {string} path
104 * @param {function(?Date, ?number)} callback
106 requestMetadata: function(path, callback) { },
109 * @param {string} path
110 * @param {function(?string)} callback
112 requestFileContent: function(path, callback) { },
117 canSetFileContent: function() { },
120 * @param {string} path
121 * @param {string} newContent
122 * @param {function(?string)} callback
124 setFileContent: function(path, newContent, callback) { },
129 canRename: function() { },
132 * @param {string} path
133 * @param {string} newName
134 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
136 rename: function(path, newName, callback) { },
139 * @param {string} path
140 * @param {function()=} callback
142 refresh: function(path, callback) { },
145 * @param {string} path
147 excludeFolder: function(path) { },
150 * @param {string} path
151 * @param {?string} name
152 * @param {string} content
153 * @param {function(?string)} callback
155 createFile: function(path, name, content, callback) { },
158 * @param {string} path
160 deleteFile: function(path) { },
162 remove: function() { },
165 * @param {string} path
166 * @param {string} query
167 * @param {boolean} caseSensitive
168 * @param {boolean} isRegex
169 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
171 searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { },
174 * @param {!WebInspector.ProjectSearchConfig} searchConfig
175 * @param {!Array.<string>} filesMathingFileQuery
176 * @param {!WebInspector.Progress} progress
177 * @param {function(!Array.<string>)} callback
179 findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback) { },
182 * @param {!WebInspector.Progress} progress
184 indexContent: function(progress) { }
189 * @param {!WebInspector.Project} project
191 WebInspector.ProjectStore = function(project)
193 this._project = project;
196 WebInspector.ProjectStore.prototype = {
198 * @param {!WebInspector.FileDescriptor} fileDescriptor
200 addFile: function(fileDescriptor)
202 this._project._addFile(fileDescriptor);
206 * @param {string} path
208 removeFile: function(path)
210 this._project._removeFile(path);
214 * @return {!WebInspector.Project}
218 return this._project;
224 * @extends {WebInspector.Object}
225 * @param {!WebInspector.Workspace} workspace
226 * @param {string} projectId
227 * @param {!WebInspector.ProjectDelegate} projectDelegate
229 WebInspector.Project = function(workspace, projectId, projectDelegate)
231 /** @type {!Map.<string, !{uiSourceCode: !WebInspector.UISourceCode, index: number}>} */
232 this._uiSourceCodesMap = new Map();
233 /** @type {!Array.<!WebInspector.UISourceCode>} */
234 this._uiSourceCodesList = [];
235 this._workspace = workspace;
236 this._projectId = projectId;
237 this._projectDelegate = projectDelegate;
238 this._url = this._projectDelegate.url();
239 this._displayName = this._projectDelegate.displayName();
245 WebInspector.Project.Events = {
246 DisplayNameUpdated: "DisplayNameUpdated"
249 WebInspector.Project.prototype = {
255 return this._projectId;
263 return this._projectDelegate.type();
269 displayName: function()
271 return this._displayName;
275 * @param {string} displayName
277 setDisplayName: function(displayName)
279 if (this._displayName === displayName)
281 this._displayName = displayName;
282 this.dispatchEventToListeners(WebInspector.Project.Events.DisplayNameUpdated);
296 isServiceProject: function()
298 return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.Formatter || this._projectDelegate.type() === WebInspector.projectTypes.LiveEdit ||
299 this._projectDelegate.type() === WebInspector.projectTypes.Service;
303 * @param {!WebInspector.FileDescriptor} fileDescriptor
305 _addFile: function(fileDescriptor)
307 var path = fileDescriptor.parentPath ? fileDescriptor.parentPath + "/" + fileDescriptor.name : fileDescriptor.name;
308 var uiSourceCode = this.uiSourceCode(path);
312 uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.parentPath, fileDescriptor.name, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType);
314 this._uiSourceCodesMap.set(path, {uiSourceCode: uiSourceCode, index: this._uiSourceCodesList.length});
315 this._uiSourceCodesList.push(uiSourceCode);
316 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCode);
320 * @param {string} path
322 _removeFile: function(path)
324 var uiSourceCode = this.uiSourceCode(path);
328 var entry = this._uiSourceCodesMap.get(path);
329 var movedUISourceCode = this._uiSourceCodesList[this._uiSourceCodesList.length - 1];
330 this._uiSourceCodesList[entry.index] = movedUISourceCode;
331 var movedEntry = this._uiSourceCodesMap.get(movedUISourceCode.path());
332 movedEntry.index = entry.index;
333 this._uiSourceCodesList.splice(this._uiSourceCodesList.length - 1, 1);
334 this._uiSourceCodesMap.delete(path);
335 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeRemoved, entry.uiSourceCode);
340 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectRemoved, this);
341 this._uiSourceCodesMap = new Map();
342 this._uiSourceCodesList = [];
346 * @return {!WebInspector.Workspace}
348 workspace: function()
350 return this._workspace;
354 * @param {string} path
355 * @return {?WebInspector.UISourceCode}
357 uiSourceCode: function(path)
359 var entry = this._uiSourceCodesMap.get(path);
360 return entry ? entry.uiSourceCode : null;
364 * @param {string} originURL
365 * @return {?WebInspector.UISourceCode}
367 uiSourceCodeForOriginURL: function(originURL)
369 for (var i = 0; i < this._uiSourceCodesList.length; ++i) {
370 var uiSourceCode = this._uiSourceCodesList[i];
371 if (uiSourceCode.originURL() === originURL)
378 * @return {!Array.<!WebInspector.UISourceCode>}
380 uiSourceCodes: function()
382 return this._uiSourceCodesList;
386 * @param {!WebInspector.UISourceCode} uiSourceCode
387 * @param {function(?Date, ?number)} callback
389 requestMetadata: function(uiSourceCode, callback)
391 this._projectDelegate.requestMetadata(uiSourceCode.path(), callback);
395 * @param {!WebInspector.UISourceCode} uiSourceCode
396 * @param {function(?string)} callback
398 requestFileContent: function(uiSourceCode, callback)
400 this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
406 canSetFileContent: function()
408 return this._projectDelegate.canSetFileContent();
412 * @param {!WebInspector.UISourceCode} uiSourceCode
413 * @param {string} newContent
414 * @param {function(?string)} callback
416 setFileContent: function(uiSourceCode, newContent, callback)
418 this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, onSetContent.bind(this));
421 * @param {?string} content
422 * @this {WebInspector.Project}
424 function onSetContent(content)
426 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
434 canRename: function()
436 return this._projectDelegate.canRename();
440 * @param {!WebInspector.UISourceCode} uiSourceCode
441 * @param {string} newName
442 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
444 rename: function(uiSourceCode, newName, callback)
446 if (newName === uiSourceCode.name()) {
447 callback(true, uiSourceCode.name(), uiSourceCode.url, uiSourceCode.originURL(), uiSourceCode.contentType());
451 this._projectDelegate.rename(uiSourceCode.path(), newName, innerCallback.bind(this));
454 * @param {boolean} success
455 * @param {string=} newName
456 * @param {string=} newURL
457 * @param {string=} newOriginURL
458 * @param {!WebInspector.ResourceType=} newContentType
459 * @this {WebInspector.Project}
461 function innerCallback(success, newName, newURL, newOriginURL, newContentType)
463 if (!success || !newName) {
467 var oldPath = uiSourceCode.path();
468 var newPath = uiSourceCode.parentPath() ? uiSourceCode.parentPath() + "/" + newName : newName;
469 this._uiSourceCodesMap.set(newPath, this._uiSourceCodesMap.get(oldPath));
470 this._uiSourceCodesMap.delete(oldPath);
471 callback(true, newName, newURL, newOriginURL, newContentType);
476 * @param {string} path
477 * @param {function()=} callback
479 refresh: function(path, callback)
481 this._projectDelegate.refresh(path, callback);
485 * @param {string} path
487 excludeFolder: function(path)
489 this._projectDelegate.excludeFolder(path);
490 var uiSourceCodes = this._uiSourceCodesList.slice();
491 for (var i = 0; i < uiSourceCodes.length; ++i) {
492 var uiSourceCode = uiSourceCodes[i];
493 if (uiSourceCode.path().startsWith(path.substr(1)))
494 this._removeFile(uiSourceCode.path());
499 * @param {string} path
500 * @param {?string} name
501 * @param {string} content
502 * @param {function(?string)} callback
504 createFile: function(path, name, content, callback)
506 this._projectDelegate.createFile(path, name, content, innerCallback);
508 function innerCallback(filePath)
515 * @param {string} path
517 deleteFile: function(path)
519 this._projectDelegate.deleteFile(path);
524 this._projectDelegate.remove();
528 * @param {!WebInspector.UISourceCode} uiSourceCode
529 * @param {string} query
530 * @param {boolean} caseSensitive
531 * @param {boolean} isRegex
532 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
534 searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
536 this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
540 * @param {!WebInspector.ProjectSearchConfig} searchConfig
541 * @param {!Array.<string>} filesMathingFileQuery
542 * @param {!WebInspector.Progress} progress
543 * @param {function(!Array.<string>)} callback
545 findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback)
547 this._projectDelegate.findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, progress, callback);
551 * @param {!WebInspector.Progress} progress
553 indexContent: function(progress)
555 this._projectDelegate.indexContent(progress);
558 __proto__: WebInspector.Object.prototype
564 WebInspector.projectTypes = {
565 Debugger: "debugger",
566 Formatter: "formatter",
567 LiveEdit: "liveedit",
569 Snippets: "snippets",
570 FileSystem: "filesystem",
571 ContentScripts: "contentscripts",
577 * @extends {WebInspector.Object}
578 * @param {!WebInspector.FileSystemMapping} fileSystemMapping
580 WebInspector.Workspace = function(fileSystemMapping)
582 this._fileSystemMapping = fileSystemMapping;
583 /** @type {!Object.<string, !WebInspector.Project>} */
585 this._hasResourceContentTrackingExtensions = false;
586 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.RevealSourceLine, this._revealSourceLine, this);
589 WebInspector.Workspace.Events = {
590 UISourceCodeAdded: "UISourceCodeAdded",
591 UISourceCodeRemoved: "UISourceCodeRemoved",
592 UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
593 ProjectAdded: "ProjectAdded",
594 ProjectRemoved: "ProjectRemoved"
597 WebInspector.Workspace.prototype = {
599 * @return {!Array.<!WebInspector.UISourceCode>}
601 unsavedSourceCodes: function()
604 * @param {!WebInspector.UISourceCode} sourceCode
607 function filterUnsaved(sourceCode)
609 return sourceCode.isDirty();
612 var unsavedSourceCodes = [];
613 var projects = this.projectsForType(WebInspector.projectTypes.FileSystem);
614 for (var i = 0; i < projects.length; ++i)
615 unsavedSourceCodes = unsavedSourceCodes.concat(projects[i].uiSourceCodes().filter(filterUnsaved));
617 return unsavedSourceCodes;
621 * @param {string} projectId
622 * @param {string} path
623 * @return {?WebInspector.UISourceCode}
625 uiSourceCode: function(projectId, path)
627 var project = this._projects[projectId];
628 return project ? project.uiSourceCode(path) : null;
632 * @param {string} originURL
633 * @return {?WebInspector.UISourceCode}
635 uiSourceCodeForOriginURL: function(originURL)
637 var projects = this.projectsForType(WebInspector.projectTypes.Network);
638 projects = projects.concat(this.projectsForType(WebInspector.projectTypes.ContentScripts));
639 for (var i = 0; i < projects.length; ++i) {
640 var project = projects[i];
641 var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
649 * @param {string} type
650 * @return {!Array.<!WebInspector.UISourceCode>}
652 uiSourceCodesForProjectType: function(type)
655 for (var projectName in this._projects) {
656 var project = this._projects[projectName];
657 if (project.type() === type)
658 result = result.concat(project.uiSourceCodes());
664 * @param {string} projectId
665 * @param {!WebInspector.ProjectDelegate} projectDelegate
666 * @return {!WebInspector.ProjectStore}
668 addProject: function(projectId, projectDelegate)
670 var project = new WebInspector.Project(this, projectId, projectDelegate);
671 this._projects[projectId] = project;
672 var projectStore = new WebInspector.ProjectStore(project);
673 this.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectAdded, project);
678 * @param {string} projectId
680 removeProject: function(projectId)
682 var project = this._projects[projectId];
685 delete this._projects[projectId];
690 * @param {string} projectId
691 * @return {!WebInspector.Project}
693 project: function(projectId)
695 return this._projects[projectId];
699 * @return {!Array.<!WebInspector.Project>}
703 return Object.values(this._projects);
707 * @param {string} type
708 * @return {!Array.<!WebInspector.Project>}
710 projectsForType: function(type)
712 function filterByType(project)
714 return project.type() === type;
716 return this.projects().filter(filterByType);
720 * @return {!Array.<!WebInspector.UISourceCode>}
722 uiSourceCodes: function()
725 for (var projectId in this._projects) {
726 var project = this._projects[projectId];
727 result = result.concat(project.uiSourceCodes());
733 * @param {string} url
736 hasMappingForURL: function(url)
738 return this._fileSystemMapping.hasMappingForURL(url);
742 * @param {string} url
743 * @return {?WebInspector.UISourceCode}
745 _networkUISourceCodeForURL: function(url)
747 var splitURL = WebInspector.ParsedURL.splitURLIntoPathComponents(url);
748 var projectId = splitURL[0];
749 var project = this.project(projectId);
750 return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
754 * @param {string} url
755 * @return {?WebInspector.UISourceCode}
757 _contentScriptUISourceCodeForURL: function(url)
759 var splitURL = WebInspector.ParsedURL.splitURLIntoPathComponents(url);
760 var projectId = "contentscripts:" + splitURL[0];
761 var project = this.project(projectId);
762 return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
766 * @param {string} url
767 * @return {?WebInspector.UISourceCode}
769 uiSourceCodeForURL: function(url)
771 var file = this._fileSystemMapping.fileForURL(url);
773 return this._networkUISourceCodeForURL(url) || this._contentScriptUISourceCodeForURL(url);
775 var projectId = WebInspector.FileSystemWorkspaceBinding.projectId(file.fileSystemPath);
776 var project = this.project(projectId);
777 return project ? project.uiSourceCode(file.filePath) : null;
781 * @param {string} fileSystemPath
782 * @param {string} filePath
785 urlForPath: function(fileSystemPath, filePath)
787 return this._fileSystemMapping.urlForPath(fileSystemPath, filePath);
791 * @param {!WebInspector.UISourceCode} networkUISourceCode
792 * @param {!WebInspector.UISourceCode} uiSourceCode
793 * @param {!WebInspector.FileSystemWorkspaceBinding} fileSystemWorkspaceBinding
795 addMapping: function(networkUISourceCode, uiSourceCode, fileSystemWorkspaceBinding)
797 var url = networkUISourceCode.url;
798 var path = uiSourceCode.path();
799 var fileSystemPath = fileSystemWorkspaceBinding.fileSystemPath(uiSourceCode.project().id());
800 this._fileSystemMapping.addMappingForResource(url, fileSystemPath, path);
804 * @param {!WebInspector.UISourceCode} uiSourceCode
806 removeMapping: function(uiSourceCode)
808 this._fileSystemMapping.removeMappingForURL(uiSourceCode.url);
812 * @param {boolean} hasExtensions
814 setHasResourceContentTrackingExtensions: function(hasExtensions)
816 this._hasResourceContentTrackingExtensions = hasExtensions;
822 hasResourceContentTrackingExtensions: function()
824 return this._hasResourceContentTrackingExtensions;
828 * @param {!WebInspector.Event} event
830 _revealSourceLine: function(event)
832 var url = /** @type {string} */ (event.data["url"]);
833 var lineNumber = /** @type {number} */ (event.data["lineNumber"]);
834 var columnNumber = /** @type {number} */ (event.data["columnNumber"]);
836 var uiSourceCode = this.uiSourceCodeForURL(url);
838 WebInspector.Revealer.reveal(uiSourceCode.uiLocation(lineNumber, columnNumber));
843 * @param {!WebInspector.Event} event
844 * @this {WebInspector.Workspace}
846 function listener(event)
848 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
849 if (uiSourceCode.url === url) {
850 WebInspector.Revealer.reveal(uiSourceCode.uiLocation(lineNumber, columnNumber));
851 this.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, listener, this);
855 this.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, listener, this);
858 __proto__: WebInspector.Object.prototype
862 * @type {!WebInspector.Workspace}
864 WebInspector.workspace;