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() { },
98 * @param {string} path
99 * @param {function(?Date, ?number)} callback
101 requestMetadata: function(path, callback) { },
104 * @param {string} path
105 * @param {function(?string)} callback
107 requestFileContent: function(path, callback) { },
112 canSetFileContent: function() { },
115 * @param {string} path
116 * @param {string} newContent
117 * @param {function(?string)} callback
119 setFileContent: function(path, newContent, callback) { },
124 canRename: function() { },
127 * @param {string} path
128 * @param {string} newName
129 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
131 rename: function(path, newName, callback) { },
134 * @param {string} path
135 * @param {function()=} callback
137 refresh: function(path, callback) { },
140 * @param {string} path
142 excludeFolder: function(path) { },
145 * @param {string} path
146 * @param {?string} name
147 * @param {string} content
148 * @param {function(?string)} callback
150 createFile: function(path, name, content, callback) { },
153 * @param {string} path
155 deleteFile: function(path) { },
157 remove: function() { },
160 * @param {string} path
161 * @param {string} query
162 * @param {boolean} caseSensitive
163 * @param {boolean} isRegex
164 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
166 searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { },
169 * @param {!WebInspector.ProjectSearchConfig} searchConfig
170 * @param {!Array.<string>} filesMathingFileQuery
171 * @param {!WebInspector.Progress} progress
172 * @param {function(!Array.<string>)} callback
174 findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback) { },
177 * @param {!WebInspector.Progress} progress
179 indexContent: function(progress) { }
184 * @param {!WebInspector.Project} project
186 WebInspector.ProjectStore = function(project)
188 this._project = project;
191 WebInspector.ProjectStore.prototype = {
193 * @param {!WebInspector.FileDescriptor} fileDescriptor
195 addFile: function(fileDescriptor)
197 this._project._addFile(fileDescriptor);
201 * @param {string} path
203 removeFile: function(path)
205 this._project._removeFile(path);
209 * @return {!WebInspector.Project}
213 return this._project;
218 * @param {!WebInspector.Workspace} workspace
219 * @param {string} projectId
220 * @param {!WebInspector.ProjectDelegate} projectDelegate
223 WebInspector.Project = function(workspace, projectId, projectDelegate)
225 /** @type {!Object.<string, !{uiSourceCode: !WebInspector.UISourceCode, index: number}>} */
226 this._uiSourceCodesMap = {};
227 /** @type {!Array.<!WebInspector.UISourceCode>} */
228 this._uiSourceCodesList = [];
229 this._workspace = workspace;
230 this._projectId = projectId;
231 this._projectDelegate = projectDelegate;
232 this._displayName = this._projectDelegate.displayName();
235 WebInspector.Project.prototype = {
241 return this._projectId;
249 return this._projectDelegate.type();
255 displayName: function()
257 return this._displayName;
263 isServiceProject: function()
265 return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.Formatter || this._projectDelegate.type() === WebInspector.projectTypes.LiveEdit;
269 * @param {!WebInspector.FileDescriptor} fileDescriptor
271 _addFile: function(fileDescriptor)
273 var path = fileDescriptor.parentPath ? fileDescriptor.parentPath + "/" + fileDescriptor.name : fileDescriptor.name;
274 var uiSourceCode = this.uiSourceCode(path);
278 uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.parentPath, fileDescriptor.name, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType);
280 this._uiSourceCodesMap[path] = {uiSourceCode: uiSourceCode, index: this._uiSourceCodesList.length};
281 this._uiSourceCodesList.push(uiSourceCode);
282 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCode);
286 * @param {string} path
288 _removeFile: function(path)
290 var uiSourceCode = this.uiSourceCode(path);
294 var entry = this._uiSourceCodesMap[path];
295 var movedUISourceCode = this._uiSourceCodesList[this._uiSourceCodesList.length - 1];
296 this._uiSourceCodesList[entry.index] = movedUISourceCode;
297 var movedEntry = this._uiSourceCodesMap[movedUISourceCode.path()];
298 movedEntry.index = entry.index;
299 this._uiSourceCodesList.splice(this._uiSourceCodesList.length - 1, 1);
300 delete this._uiSourceCodesMap[path];
301 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeRemoved, entry.uiSourceCode);
306 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectRemoved, this);
307 this._uiSourceCodesMap = {};
308 this._uiSourceCodesList = [];
312 * @return {!WebInspector.Workspace}
314 workspace: function()
316 return this._workspace;
320 * @param {string} path
321 * @return {?WebInspector.UISourceCode}
323 uiSourceCode: function(path)
325 var entry = this._uiSourceCodesMap[path];
326 return entry ? entry.uiSourceCode : null;
330 * @param {string} originURL
331 * @return {?WebInspector.UISourceCode}
333 uiSourceCodeForOriginURL: function(originURL)
335 for (var i = 0; i < this._uiSourceCodesList.length; ++i) {
336 var uiSourceCode = this._uiSourceCodesList[i];
337 if (uiSourceCode.originURL() === originURL)
344 * @return {!Array.<!WebInspector.UISourceCode>}
346 uiSourceCodes: function()
348 return this._uiSourceCodesList;
352 * @param {!WebInspector.UISourceCode} uiSourceCode
353 * @param {function(?Date, ?number)} callback
355 requestMetadata: function(uiSourceCode, callback)
357 this._projectDelegate.requestMetadata(uiSourceCode.path(), callback);
361 * @param {!WebInspector.UISourceCode} uiSourceCode
362 * @param {function(?string)} callback
364 requestFileContent: function(uiSourceCode, callback)
366 this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
372 canSetFileContent: function()
374 return this._projectDelegate.canSetFileContent();
378 * @param {!WebInspector.UISourceCode} uiSourceCode
379 * @param {string} newContent
380 * @param {function(?string)} callback
382 setFileContent: function(uiSourceCode, newContent, callback)
384 this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, onSetContent.bind(this));
387 * @param {?string} content
388 * @this {WebInspector.Project}
390 function onSetContent(content)
392 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
400 canRename: function()
402 return this._projectDelegate.canRename();
406 * @param {!WebInspector.UISourceCode} uiSourceCode
407 * @param {string} newName
408 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
410 rename: function(uiSourceCode, newName, callback)
412 if (newName === uiSourceCode.name()) {
413 callback(true, uiSourceCode.name(), uiSourceCode.url, uiSourceCode.originURL(), uiSourceCode.contentType());
417 this._projectDelegate.rename(uiSourceCode.path(), newName, innerCallback.bind(this));
420 * @param {boolean} success
421 * @param {string=} newName
422 * @param {string=} newURL
423 * @param {string=} newOriginURL
424 * @param {!WebInspector.ResourceType=} newContentType
425 * @this {WebInspector.Project}
427 function innerCallback(success, newName, newURL, newOriginURL, newContentType)
429 if (!success || !newName) {
433 var oldPath = uiSourceCode.path();
434 var newPath = uiSourceCode.parentPath() ? uiSourceCode.parentPath() + "/" + newName : newName;
435 this._uiSourceCodesMap[newPath] = this._uiSourceCodesMap[oldPath];
436 delete this._uiSourceCodesMap[oldPath];
437 callback(true, newName, newURL, newOriginURL, newContentType);
442 * @param {string} path
443 * @param {function()=} callback
445 refresh: function(path, callback)
447 this._projectDelegate.refresh(path, callback);
451 * @param {string} path
453 excludeFolder: function(path)
455 this._projectDelegate.excludeFolder(path);
456 var uiSourceCodes = this._uiSourceCodesList.slice();
457 for (var i = 0; i < uiSourceCodes.length; ++i) {
458 var uiSourceCode = uiSourceCodes[i];
459 if (uiSourceCode.path().startsWith(path.substr(1)))
460 this._removeFile(uiSourceCode.path());
465 * @param {string} path
466 * @param {?string} name
467 * @param {string} content
468 * @param {function(?string)} callback
470 createFile: function(path, name, content, callback)
472 this._projectDelegate.createFile(path, name, content, innerCallback);
474 function innerCallback(filePath)
481 * @param {string} path
483 deleteFile: function(path)
485 this._projectDelegate.deleteFile(path);
490 this._projectDelegate.remove();
494 * @param {!WebInspector.UISourceCode} uiSourceCode
495 * @param {string} query
496 * @param {boolean} caseSensitive
497 * @param {boolean} isRegex
498 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
500 searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
502 this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
506 * @param {!WebInspector.ProjectSearchConfig} searchConfig
507 * @param {!Array.<string>} filesMathingFileQuery
508 * @param {!WebInspector.Progress} progress
509 * @param {function(!Array.<string>)} callback
511 findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback)
513 this._projectDelegate.findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, progress, callback);
517 * @param {!WebInspector.Progress} progress
519 indexContent: function(progress)
521 this._projectDelegate.indexContent(progress);
528 WebInspector.projectTypes = {
529 Debugger: "debugger",
530 Formatter: "formatter",
531 LiveEdit: "liveedit",
533 Snippets: "snippets",
534 FileSystem: "filesystem",
535 ContentScripts: "contentscripts"
540 * @extends {WebInspector.Object}
541 * @param {!WebInspector.FileSystemMapping} fileSystemMapping
543 WebInspector.Workspace = function(fileSystemMapping)
545 this._fileSystemMapping = fileSystemMapping;
546 /** @type {!Object.<string, !WebInspector.Project>} */
548 this._hasResourceContentTrackingExtensions = false;
549 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.RevealSourceLine, this._revealSourceLine, this);
552 WebInspector.Workspace.Events = {
553 UISourceCodeAdded: "UISourceCodeAdded",
554 UISourceCodeRemoved: "UISourceCodeRemoved",
555 UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
556 ProjectRemoved: "ProjectRemoved"
559 WebInspector.Workspace.prototype = {
561 * @return {!Array.<!WebInspector.UISourceCode>}
563 unsavedSourceCodes: function()
565 function filterUnsaved(sourceCode)
567 return sourceCode.isDirty();
569 return this.uiSourceCodes().filter(filterUnsaved);
573 * @param {string} projectId
574 * @param {string} path
575 * @return {?WebInspector.UISourceCode}
577 uiSourceCode: function(projectId, path)
579 var project = this._projects[projectId];
580 return project ? project.uiSourceCode(path) : null;
584 * @param {string} originURL
585 * @return {?WebInspector.UISourceCode}
587 uiSourceCodeForOriginURL: function(originURL)
589 var projects = this.projectsForType(WebInspector.projectTypes.Network);
590 projects = projects.concat(this.projectsForType(WebInspector.projectTypes.ContentScripts));
591 for (var i = 0; i < projects.length; ++i) {
592 var project = projects[i];
593 var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
601 * @param {string} type
602 * @return {!Array.<!WebInspector.UISourceCode>}
604 uiSourceCodesForProjectType: function(type)
607 for (var projectName in this._projects) {
608 var project = this._projects[projectName];
609 if (project.type() === type)
610 result = result.concat(project.uiSourceCodes());
616 * @param {string} projectId
617 * @param {!WebInspector.ProjectDelegate} projectDelegate
618 * @return {!WebInspector.ProjectStore}
620 addProject: function(projectId, projectDelegate)
622 var project = new WebInspector.Project(this, projectId, projectDelegate);
623 this._projects[projectId] = project;
624 var projectStore = new WebInspector.ProjectStore(project);
629 * @param {string} projectId
631 removeProject: function(projectId)
633 var project = this._projects[projectId];
636 delete this._projects[projectId];
641 * @param {string} projectId
642 * @return {!WebInspector.Project}
644 project: function(projectId)
646 return this._projects[projectId];
650 * @return {!Array.<!WebInspector.Project>}
654 return Object.values(this._projects);
658 * @param {string} type
659 * @return {!Array.<!WebInspector.Project>}
661 projectsForType: function(type)
663 function filterByType(project)
665 return project.type() === type;
667 return this.projects().filter(filterByType);
671 * @return {!Array.<!WebInspector.UISourceCode>}
673 uiSourceCodes: function()
676 for (var projectId in this._projects) {
677 var project = this._projects[projectId];
678 result = result.concat(project.uiSourceCodes());
684 * @param {string} url
687 hasMappingForURL: function(url)
689 return this._fileSystemMapping.hasMappingForURL(url);
693 * @param {string} url
694 * @return {?WebInspector.UISourceCode}
696 _networkUISourceCodeForURL: function(url)
698 var splitURL = WebInspector.ParsedURL.splitURL(url);
699 var projectId = splitURL[0];
700 var project = this.project(projectId);
701 return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
705 * @param {string} url
706 * @return {?WebInspector.UISourceCode}
708 _contentScriptUISourceCodeForURL: function(url)
710 var splitURL = WebInspector.ParsedURL.splitURL(url);
711 var projectId = "contentscripts:" + splitURL[0];
712 var project = this.project(projectId);
713 return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
717 * @param {string} url
718 * @return {?WebInspector.UISourceCode}
720 uiSourceCodeForURL: function(url)
722 var file = this._fileSystemMapping.fileForURL(url);
724 return this._networkUISourceCodeForURL(url) || this._contentScriptUISourceCodeForURL(url);
726 var projectId = WebInspector.FileSystemWorkspaceBinding.projectId(file.fileSystemPath);
727 var project = this.project(projectId);
728 return project ? project.uiSourceCode(file.filePath) : null;
732 * @param {string} fileSystemPath
733 * @param {string} filePath
736 urlForPath: function(fileSystemPath, filePath)
738 return this._fileSystemMapping.urlForPath(fileSystemPath, filePath);
742 * @param {!WebInspector.UISourceCode} networkUISourceCode
743 * @param {!WebInspector.UISourceCode} uiSourceCode
744 * @param {!WebInspector.FileSystemWorkspaceBinding} fileSystemWorkspaceBinding
746 addMapping: function(networkUISourceCode, uiSourceCode, fileSystemWorkspaceBinding)
748 var url = networkUISourceCode.url;
749 var path = uiSourceCode.path();
750 var fileSystemPath = fileSystemWorkspaceBinding.fileSystemPath(uiSourceCode.project().id());
751 this._fileSystemMapping.addMappingForResource(url, fileSystemPath, path);
755 * @param {!WebInspector.UISourceCode} uiSourceCode
757 removeMapping: function(uiSourceCode)
759 this._fileSystemMapping.removeMappingForURL(uiSourceCode.url);
763 * @param {boolean} hasExtensions
765 setHasResourceContentTrackingExtensions: function(hasExtensions)
767 this._hasResourceContentTrackingExtensions = hasExtensions;
773 hasResourceContentTrackingExtensions: function()
775 return this._hasResourceContentTrackingExtensions;
779 * @param {!WebInspector.Event} event
781 _revealSourceLine: function(event)
783 var url = /** @type {string} */ (event.data["url"]);
784 var lineNumber = /** @type {number} */ (event.data["lineNumber"]);
785 var columnNumber = /** @type {number} */ (event.data["columnNumber"]);
787 var uiSourceCode = this.uiSourceCodeForURL(url);
789 WebInspector.Revealer.reveal(uiSourceCode.uiLocation(lineNumber, columnNumber));
794 * @param {!WebInspector.Event} event
795 * @this {WebInspector.Workspace}
797 function listener(event)
799 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
800 if (uiSourceCode.url === url) {
801 WebInspector.Revealer.reveal(uiSourceCode.uiLocation(lineNumber, columnNumber));
802 this.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, listener, this);
806 this.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, listener, this);
809 __proto__: WebInspector.Object.prototype
813 * @type {!WebInspector.Workspace}
815 WebInspector.workspace;