Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / workspace / Workspace.js
bloba26ba362d3104966423dc2547d9a0df83f19b734
1 /*
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
6 * met:
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.
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.
31 /**
32 * @interface
34 WebInspector.ProjectSearchConfig = function() {}
36 WebInspector.ProjectSearchConfig.prototype = {
37 /**
38 * @return {string}
40 query: function() { },
42 /**
43 * @return {boolean}
45 ignoreCase: function() { },
47 /**
48 * @return {boolean}
50 isRegex: function() { },
52 /**
53 * @return {!Array.<string>}
55 queries: function() { },
57 /**
58 * @param {string} filePath
59 * @return {boolean}
61 filePathMatchesFileQuery: function(filePath) { }
64 /**
65 * @constructor
66 * @param {string} parentPath
67 * @param {string} name
68 * @param {string} originURL
69 * @param {string} url
70 * @param {!WebInspector.ResourceType} contentType
72 WebInspector.FileDescriptor = function(parentPath, name, originURL, url, contentType)
74 this.parentPath = parentPath;
75 this.name = name;
76 this.originURL = originURL;
77 this.url = url;
78 this.contentType = contentType;
81 /**
82 * @interface
83 * @extends {WebInspector.EventTarget}
85 WebInspector.ProjectDelegate = function() { }
87 WebInspector.ProjectDelegate.Events = {
88 FileAdded: "FileAdded",
89 FileRemoved: "FileRemoved",
92 WebInspector.ProjectDelegate.prototype = {
93 /**
94 * @return {string}
96 type: function() { },
98 /**
99 * @return {string}
101 displayName: function() { },
104 * @return {string}
106 url: function() { },
109 * @param {string} path
110 * @param {function(?Date, ?number)} callback
112 requestMetadata: function(path, callback) { },
115 * @param {string} path
116 * @param {function(?string)} callback
118 requestFileContent: function(path, callback) { },
121 * @return {boolean}
123 canSetFileContent: function() { },
126 * @param {string} path
127 * @param {string} newContent
128 * @param {function(?string)} callback
130 setFileContent: function(path, newContent, callback) { },
133 * @return {boolean}
135 canRename: function() { },
138 * @param {string} path
139 * @param {string} newName
140 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
142 rename: function(path, newName, callback) { },
145 * @param {string} path
146 * @param {function()=} callback
148 refresh: function(path, callback) { },
151 * @param {string} path
153 excludeFolder: function(path) { },
156 * @param {string} path
157 * @param {?string} name
158 * @param {string} content
159 * @param {function(?string)} callback
161 createFile: function(path, name, content, callback) { },
164 * @param {string} path
166 deleteFile: function(path) { },
168 remove: function() { },
171 * @param {string} path
172 * @param {string} query
173 * @param {boolean} caseSensitive
174 * @param {boolean} isRegex
175 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
177 searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { },
180 * @param {!WebInspector.ProjectSearchConfig} searchConfig
181 * @param {!Array.<string>} filesMathingFileQuery
182 * @param {!WebInspector.Progress} progress
183 * @param {function(!Array.<string>)} callback
185 findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback) { },
188 * @param {!WebInspector.Progress} progress
190 indexContent: function(progress) { }
194 * @constructor
195 * @extends {WebInspector.Object}
196 * @param {!WebInspector.Workspace} workspace
197 * @param {string} projectId
198 * @param {!WebInspector.ProjectDelegate} projectDelegate
200 WebInspector.Project = function(workspace, projectId, projectDelegate)
202 /** @type {!Map.<string, !{uiSourceCode: !WebInspector.UISourceCode, index: number}>} */
203 this._uiSourceCodesMap = new Map();
204 /** @type {!Array.<!WebInspector.UISourceCode>} */
205 this._uiSourceCodesList = [];
206 this._workspace = workspace;
207 this._projectId = projectId;
208 this._projectDelegate = projectDelegate;
209 this._url = this._projectDelegate.url();
210 this._displayName = this._projectDelegate.displayName();
211 projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
212 projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
216 * @enum {string}
218 WebInspector.Project.Events = {
219 DisplayNameUpdated: "DisplayNameUpdated"
222 WebInspector.Project.prototype = {
224 * @return {string}
226 id: function()
228 return this._projectId;
232 * @return {string}
234 type: function()
236 return this._projectDelegate.type();
240 * @return {string}
242 displayName: function()
244 return this._displayName;
248 * @param {string} displayName
250 setDisplayName: function(displayName)
252 if (this._displayName === displayName)
253 return;
254 this._displayName = displayName;
255 this.dispatchEventToListeners(WebInspector.Project.Events.DisplayNameUpdated);
259 * @return {string}
261 url: function()
263 return this._url;
267 * @return {boolean}
269 isServiceProject: function()
271 return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.Formatter || this._projectDelegate.type() === WebInspector.projectTypes.Service;
276 * @param {!WebInspector.Event} event
278 _fileAdded: function(event)
280 var fileDescriptor = /** @type {!WebInspector.FileDescriptor} */ (event.data);
281 var path = fileDescriptor.parentPath ? fileDescriptor.parentPath + "/" + fileDescriptor.name : fileDescriptor.name;
282 var uiSourceCode = this.uiSourceCode(path);
283 if (uiSourceCode)
284 return;
286 uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.parentPath, fileDescriptor.name, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType);
288 this._uiSourceCodesMap.set(path, {uiSourceCode: uiSourceCode, index: this._uiSourceCodesList.length});
289 this._uiSourceCodesList.push(uiSourceCode);
290 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCode);
294 * @param {!WebInspector.Event} event
296 _fileRemoved: function(event)
298 var path = /** @type {string} */ (event.data);
299 this._removeFile(path);
303 * @param {string} path
305 _removeFile: function(path)
307 var uiSourceCode = this.uiSourceCode(path);
308 if (!uiSourceCode)
309 return;
311 var entry = this._uiSourceCodesMap.get(path);
312 var movedUISourceCode = this._uiSourceCodesList[this._uiSourceCodesList.length - 1];
313 this._uiSourceCodesList[entry.index] = movedUISourceCode;
314 var movedEntry = this._uiSourceCodesMap.get(movedUISourceCode.path());
315 movedEntry.index = entry.index;
316 this._uiSourceCodesList.splice(this._uiSourceCodesList.length - 1, 1);
317 this._uiSourceCodesMap.delete(path);
318 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeRemoved, entry.uiSourceCode);
321 _remove: function()
323 this._projectDelegate.removeEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
324 this._projectDelegate.removeEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
325 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectRemoved, this);
326 this._uiSourceCodesMap = new Map();
327 this._uiSourceCodesList = [];
331 * @return {!WebInspector.Workspace}
333 workspace: function()
335 return this._workspace;
339 * @param {string} path
340 * @return {?WebInspector.UISourceCode}
342 uiSourceCode: function(path)
344 var entry = this._uiSourceCodesMap.get(path);
345 return entry ? entry.uiSourceCode : null;
349 * @param {string} originURL
350 * @return {?WebInspector.UISourceCode}
352 uiSourceCodeForOriginURL: function(originURL)
354 for (var i = 0; i < this._uiSourceCodesList.length; ++i) {
355 var uiSourceCode = this._uiSourceCodesList[i];
356 if (uiSourceCode.originURL() === originURL)
357 return uiSourceCode;
359 return null;
363 * @return {!Array.<!WebInspector.UISourceCode>}
365 uiSourceCodes: function()
367 return this._uiSourceCodesList;
371 * @param {!WebInspector.UISourceCode} uiSourceCode
372 * @param {function(?Date, ?number)} callback
374 requestMetadata: function(uiSourceCode, callback)
376 this._projectDelegate.requestMetadata(uiSourceCode.path(), callback);
380 * @param {!WebInspector.UISourceCode} uiSourceCode
381 * @param {function(?string)} callback
383 requestFileContent: function(uiSourceCode, callback)
385 this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
389 * @return {boolean}
391 canSetFileContent: function()
393 return this._projectDelegate.canSetFileContent();
397 * @param {!WebInspector.UISourceCode} uiSourceCode
398 * @param {string} newContent
399 * @param {function(?string)} callback
401 setFileContent: function(uiSourceCode, newContent, callback)
403 this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, onSetContent.bind(this));
406 * @param {?string} content
407 * @this {WebInspector.Project}
409 function onSetContent(content)
411 this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
412 callback(content);
417 * @return {boolean}
419 canRename: function()
421 return this._projectDelegate.canRename();
425 * @param {!WebInspector.UISourceCode} uiSourceCode
426 * @param {string} newName
427 * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
429 rename: function(uiSourceCode, newName, callback)
431 if (newName === uiSourceCode.name()) {
432 callback(true, uiSourceCode.name(), uiSourceCode.networkURL(), uiSourceCode.originURL(), uiSourceCode.contentType());
433 return;
436 this._projectDelegate.rename(uiSourceCode.path(), newName, innerCallback.bind(this));
439 * @param {boolean} success
440 * @param {string=} newName
441 * @param {string=} newURL
442 * @param {string=} newOriginURL
443 * @param {!WebInspector.ResourceType=} newContentType
444 * @this {WebInspector.Project}
446 function innerCallback(success, newName, newURL, newOriginURL, newContentType)
448 if (!success || !newName) {
449 callback(false);
450 return;
452 var oldPath = uiSourceCode.path();
453 var newPath = uiSourceCode.parentPath() ? uiSourceCode.parentPath() + "/" + newName : newName;
454 var value = /** @type {!{uiSourceCode: !WebInspector.UISourceCode, index: number}} */ (this._uiSourceCodesMap.get(oldPath));
455 this._uiSourceCodesMap.set(newPath, value);
456 this._uiSourceCodesMap.delete(oldPath);
457 callback(true, newName, newURL, newOriginURL, newContentType);
462 * @param {string} path
463 * @param {function()=} callback
465 refresh: function(path, callback)
467 this._projectDelegate.refresh(path, callback);
471 * @param {string} path
473 excludeFolder: function(path)
475 this._projectDelegate.excludeFolder(path);
476 var uiSourceCodes = this._uiSourceCodesList.slice();
477 for (var i = 0; i < uiSourceCodes.length; ++i) {
478 var uiSourceCode = uiSourceCodes[i];
479 if (uiSourceCode.path().startsWith(path.substr(1)))
480 this._removeFile(uiSourceCode.path());
485 * @param {string} path
486 * @param {?string} name
487 * @param {string} content
488 * @param {function(?string)} callback
490 createFile: function(path, name, content, callback)
492 this._projectDelegate.createFile(path, name, content, innerCallback);
494 function innerCallback(filePath)
496 callback(filePath);
501 * @param {string} path
503 deleteFile: function(path)
505 this._projectDelegate.deleteFile(path);
508 remove: function()
510 this._projectDelegate.remove();
514 * @param {!WebInspector.UISourceCode} uiSourceCode
515 * @param {string} query
516 * @param {boolean} caseSensitive
517 * @param {boolean} isRegex
518 * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
520 searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
522 this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
526 * @param {!WebInspector.ProjectSearchConfig} searchConfig
527 * @param {!Array.<string>} filesMathingFileQuery
528 * @param {!WebInspector.Progress} progress
529 * @param {function(!Array.<string>)} callback
531 findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback)
533 this._projectDelegate.findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, progress, callback);
537 * @param {!WebInspector.Progress} progress
539 indexContent: function(progress)
541 this._projectDelegate.indexContent(progress);
544 __proto__: WebInspector.Object.prototype
548 * @enum {string}
550 WebInspector.projectTypes = {
551 Debugger: "debugger",
552 Formatter: "formatter",
553 Network: "network",
554 Snippets: "snippets",
555 FileSystem: "filesystem",
556 ContentScripts: "contentscripts",
557 Service: "service"
561 * @constructor
562 * @extends {WebInspector.Object}
563 * @param {!WebInspector.FileSystemMapping} fileSystemMapping
565 WebInspector.Workspace = function(fileSystemMapping)
567 this._fileSystemMapping = fileSystemMapping;
568 /** @type {!Object.<string, !WebInspector.Project>} */
569 this._projects = {};
570 this._hasResourceContentTrackingExtensions = false;
573 WebInspector.Workspace.Events = {
574 UISourceCodeAdded: "UISourceCodeAdded",
575 UISourceCodeRemoved: "UISourceCodeRemoved",
576 UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
577 UISourceCodeWorkingCopyChanged: "UISourceCodeWorkingCopyChanged",
578 ProjectAdded: "ProjectAdded",
579 ProjectRemoved: "ProjectRemoved"
582 WebInspector.Workspace.prototype = {
584 * @return {!Array.<!WebInspector.UISourceCode>}
586 unsavedSourceCodes: function()
589 * @param {!WebInspector.UISourceCode} sourceCode
590 * @return {boolean}
592 function filterUnsaved(sourceCode)
594 return sourceCode.isDirty();
597 var unsavedSourceCodes = [];
598 var projects = this.projectsForType(WebInspector.projectTypes.FileSystem);
599 for (var i = 0; i < projects.length; ++i)
600 unsavedSourceCodes = unsavedSourceCodes.concat(projects[i].uiSourceCodes().filter(filterUnsaved));
602 return unsavedSourceCodes;
606 * @param {string} projectId
607 * @param {string} path
608 * @return {?WebInspector.UISourceCode}
610 uiSourceCode: function(projectId, path)
612 var project = this._projects[projectId];
613 return project ? project.uiSourceCode(path) : null;
617 * @param {string} originURL
618 * @return {?WebInspector.UISourceCode}
620 uiSourceCodeForOriginURL: function(originURL)
622 var projects = this.projectsForType(WebInspector.projectTypes.Network);
623 projects = projects.concat(this.projectsForType(WebInspector.projectTypes.ContentScripts));
624 for (var i = 0; i < projects.length; ++i) {
625 var project = projects[i];
626 var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
627 if (uiSourceCode)
628 return uiSourceCode;
630 return null;
634 * @param {string} originURL
635 * @return {?WebInspector.UISourceCode}
637 filesystemUISourceCode: function(originURL)
639 var projects = this.projectsForType(WebInspector.projectTypes.FileSystem);
640 for (var i = 0; i < projects.length; ++i) {
641 var project = projects[i];
642 var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
643 if (uiSourceCode)
644 return uiSourceCode;
646 return null;
650 * @param {string} type
651 * @return {!Array.<!WebInspector.UISourceCode>}
653 uiSourceCodesForProjectType: function(type)
655 var result = [];
656 for (var projectName in this._projects) {
657 var project = this._projects[projectName];
658 if (project.type() === type)
659 result = result.concat(project.uiSourceCodes());
661 return result;
665 * @param {string} projectId
666 * @param {!WebInspector.ProjectDelegate} projectDelegate
667 * @return {!WebInspector.Project}
669 addProject: function(projectId, projectDelegate)
671 var project = new WebInspector.Project(this, projectId, projectDelegate);
672 this._projects[projectId] = project;
673 this.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectAdded, project);
674 return project;
678 * @param {string} projectId
680 removeProject: function(projectId)
682 var project = this._projects[projectId];
683 if (!project)
684 return;
685 delete this._projects[projectId];
686 project._remove();
690 * @param {string} projectId
691 * @return {!WebInspector.Project}
693 project: function(projectId)
695 return this._projects[projectId];
699 * @return {!Array.<!WebInspector.Project>}
701 projects: function()
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()
724 var result = [];
725 for (var projectId in this._projects) {
726 var project = this._projects[projectId];
727 result = result.concat(project.uiSourceCodes());
729 return result;
733 * @param {boolean} hasExtensions
735 setHasResourceContentTrackingExtensions: function(hasExtensions)
737 this._hasResourceContentTrackingExtensions = hasExtensions;
741 * @return {boolean}
743 hasResourceContentTrackingExtensions: function()
745 return this._hasResourceContentTrackingExtensions;
748 __proto__: WebInspector.Object.prototype
752 * @type {!WebInspector.Workspace}
754 WebInspector.workspace;