Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / Workspace.js
1 /*
2  * Copyright (C) 2012 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 {string} parentPath
34  * @param {string} name
35  * @param {string} originURL
36  * @param {string} url
37  * @param {!WebInspector.ResourceType} contentType
38  * @param {boolean} isEditable
39  * @param {boolean=} isContentScript
40  */
41 WebInspector.FileDescriptor = function(parentPath, name, originURL, url, contentType, isEditable, isContentScript)
42 {
43     this.parentPath = parentPath;
44     this.name = name;
45     this.originURL = originURL;
46     this.url = url;
47     this.contentType = contentType;
48     this.isEditable = isEditable;
49     this.isContentScript = isContentScript || false;
50 }
51
52 /**
53  * @interface
54  * @extends {WebInspector.EventTarget}
55  */
56 WebInspector.ProjectDelegate = function() { }
57
58 WebInspector.ProjectDelegate.Events = {
59     FileAdded: "FileAdded",
60     FileRemoved: "FileRemoved",
61     Reset: "Reset",
62 }
63
64 WebInspector.ProjectDelegate.prototype = {
65     /**
66      * @return {string}
67      */
68     id: function() { },
69
70     /**
71      * @return {string}
72      */
73     type: function() { },
74
75     /**
76      * @return {string}
77      */
78     displayName: function() { }, 
79
80     /**
81      * @param {string} path
82      * @param {function(?Date, ?number)} callback
83      */
84     requestMetadata: function(path, callback) { },
85
86     /**
87      * @param {string} path
88      * @param {function(?string)} callback
89      */
90     requestFileContent: function(path, callback) { },
91
92     /**
93      * @return {boolean}
94      */
95     canSetFileContent: function() { },
96
97     /**
98      * @param {string} path
99      * @param {string} newContent
100      * @param {function(?string)} callback
101      */
102     setFileContent: function(path, newContent, callback) { },
103
104     /**
105      * @return {boolean}
106      */
107     canRename: function() { },
108
109     /**
110      * @param {string} path
111      * @param {string} newName
112      * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
113      */
114     rename: function(path, newName, callback) { },
115
116     /**
117      * @param {string} path
118      */
119     refresh: function(path) { },
120
121     /**
122      * @param {string} path
123      */
124     excludeFolder: function(path) { },
125
126     /**
127      * @param {string} path
128      * @param {?string} name
129      * @param {string} content
130      * @param {function(?string)} callback
131      */
132     createFile: function(path, name, content, callback) { },
133
134     /**
135      * @param {string} path
136      */
137     deleteFile: function(path) { },
138
139     remove: function() { },
140
141     /**
142      * @param {string} path
143      * @param {string} query
144      * @param {boolean} caseSensitive
145      * @param {boolean} isRegex
146      * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
147      */
148     searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { },
149
150     /**
151      * @param {!Array.<string>} queries
152      * @param {!Array.<string>} fileQueries
153      * @param {boolean} caseSensitive
154      * @param {boolean} isRegex
155      * @param {!WebInspector.Progress} progress
156      * @param {function(!Array.<string>)} callback
157      */
158     findFilesMatchingSearchRequest: function(queries, fileQueries, caseSensitive, isRegex, progress, callback) { },
159
160     /**
161      * @param {!WebInspector.Progress} progress
162      * @param {function()} callback
163      */
164     indexContent: function(progress, callback) { }
165 }
166
167 /**
168  * @param {!WebInspector.Workspace} workspace
169  * @param {!WebInspector.ProjectDelegate} projectDelegate
170  * @constructor
171  */
172 WebInspector.Project = function(workspace, projectDelegate)
173 {
174     /** @type {!Object.<string, !{uiSourceCode: !WebInspector.UISourceCode, index: number}>} */
175     this._uiSourceCodesMap = {};
176     /** @type {!Array.<!WebInspector.UISourceCode>} */
177     this._uiSourceCodesList = [];
178     this._workspace = workspace;
179     this._projectDelegate = projectDelegate;
180     this._displayName = this._projectDelegate.displayName();
181     this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
182     this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
183     this._projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.Reset, this._reset, this);
184 }
185
186 WebInspector.Project.prototype = {
187     /**
188      * @return {string}
189      */
190     id: function()
191     {
192         return this._projectDelegate.id();
193     },
194
195     /**
196      * @return {string}
197      */
198     type: function()
199     {
200         return this._projectDelegate.type(); 
201     },
202
203     /**
204      * @return {string}
205      */
206     displayName: function() 
207     {
208         return this._displayName;
209     },
210
211     /**
212      * @return {boolean}
213      */
214     isServiceProject: function()
215     {
216         return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.LiveEdit;
217     },
218
219     _fileAdded: function(event)
220     {
221         var fileDescriptor = /** @type {!WebInspector.FileDescriptor} */ (event.data);
222         var path = fileDescriptor.parentPath ? fileDescriptor.parentPath + "/" + fileDescriptor.name : fileDescriptor.name;
223         var uiSourceCode = this.uiSourceCode(path);
224         if (uiSourceCode)
225             return;
226
227         uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.parentPath, fileDescriptor.name, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType, fileDescriptor.isEditable);
228         uiSourceCode.isContentScript = fileDescriptor.isContentScript;
229
230         this._uiSourceCodesMap[path] = {uiSourceCode: uiSourceCode, index: this._uiSourceCodesList.length};
231         this._uiSourceCodesList.push(uiSourceCode);
232         this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCode);
233     },
234
235     _fileRemoved: function(event)
236     {
237         var path = /** @type {string} */ (event.data);
238         this._removeFile(path);
239     },
240
241     /**
242      * @param {string} path
243      */
244     _removeFile: function(path)
245     {
246         var uiSourceCode = this.uiSourceCode(path);
247         if (!uiSourceCode)
248             return;
249
250         var entry = this._uiSourceCodesMap[path];
251         var movedUISourceCode = this._uiSourceCodesList[this._uiSourceCodesList.length - 1];
252         this._uiSourceCodesList[entry.index] = movedUISourceCode;
253         var movedEntry = this._uiSourceCodesMap[movedUISourceCode.path()];
254         movedEntry.index = entry.index;
255         this._uiSourceCodesList.splice(this._uiSourceCodesList.length - 1, 1);
256         delete this._uiSourceCodesMap[path];
257         this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeRemoved, entry.uiSourceCode);
258     },
259
260     _reset: function()
261     {
262         this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectWillReset, this);
263         this._uiSourceCodesMap = {};
264         this._uiSourceCodesList = [];
265     },
266
267     /**
268      * @return {!WebInspector.Workspace}
269      */
270     workspace: function()
271     {
272         return this._workspace;
273     },
274
275     /**
276      * @param {string} path
277      * @return {?WebInspector.UISourceCode}
278      */
279     uiSourceCode: function(path)
280     {
281         var entry = this._uiSourceCodesMap[path];
282         return entry ? entry.uiSourceCode : null;
283     },
284
285     /**
286      * @param {string} originURL
287      * @return {?WebInspector.UISourceCode}
288      */
289     uiSourceCodeForOriginURL: function(originURL)
290     {
291         for (var i = 0; i < this._uiSourceCodesList.length; ++i) {
292             var uiSourceCode = this._uiSourceCodesList[i];
293             if (uiSourceCode.originURL() === originURL)
294                 return uiSourceCode;
295         }
296         return null;
297     },
298
299     /**
300      * @return {!Array.<!WebInspector.UISourceCode>}
301      */
302     uiSourceCodes: function()
303     {
304         return this._uiSourceCodesList;
305     },
306
307     /**
308      * @param {!WebInspector.UISourceCode} uiSourceCode
309      * @param {function(?Date, ?number)} callback
310      */
311     requestMetadata: function(uiSourceCode, callback)
312     {
313         this._projectDelegate.requestMetadata(uiSourceCode.path(), callback);
314     },
315
316     /**
317      * @param {!WebInspector.UISourceCode} uiSourceCode
318      * @param {function(?string)} callback
319      */
320     requestFileContent: function(uiSourceCode, callback)
321     {
322         this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
323     },
324
325     /**
326      * @return {boolean}
327      */
328     canSetFileContent: function()
329     {
330         return this._projectDelegate.canSetFileContent();
331     },
332
333     /**
334      * @param {!WebInspector.UISourceCode} uiSourceCode
335      * @param {string} newContent
336      * @param {function(?string)} callback
337      */
338     setFileContent: function(uiSourceCode, newContent, callback)
339     {
340         this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, onSetContent.bind(this));
341
342         /**
343          * @param {?string} content
344          * @this {WebInspector.Project}
345          */
346         function onSetContent(content)
347         {
348             this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
349             callback(content);
350         }
351     },
352
353     /**
354      * @return {boolean}
355      */
356     canRename: function()
357     {
358         return this._projectDelegate.canRename();
359     },
360
361     /**
362      * @param {!WebInspector.UISourceCode} uiSourceCode
363      * @param {string} newName
364      * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
365      */
366     rename: function(uiSourceCode, newName, callback)
367     {
368         if (newName === uiSourceCode.name()) {
369             callback(true, uiSourceCode.name(), uiSourceCode.url, uiSourceCode.originURL(), uiSourceCode.contentType());
370             return;
371         }
372
373         this._projectDelegate.rename(uiSourceCode.path(), newName, innerCallback.bind(this));
374
375         /**
376          * @param {boolean} success
377          * @param {string=} newName
378          * @param {string=} newURL
379          * @param {string=} newOriginURL
380          * @param {!WebInspector.ResourceType=} newContentType
381          * @this {WebInspector.Project}
382          */
383         function innerCallback(success, newName, newURL, newOriginURL, newContentType)
384         {
385             if (!success || !newName) {
386                 callback(false);
387                 return;
388             }
389             var oldPath = uiSourceCode.path();
390             var newPath = uiSourceCode.parentPath() ? uiSourceCode.parentPath() + "/" + newName : newName;
391             this._uiSourceCodesMap[newPath] = this._uiSourceCodesMap[oldPath];
392             delete this._uiSourceCodesMap[oldPath];
393             callback(true, newName, newURL, newOriginURL, newContentType);
394         }
395     },
396
397     /**
398      * @param {string} path
399      */
400     refresh: function(path)
401     {
402         this._projectDelegate.refresh(path);
403     },
404
405     /**
406      * @param {string} path
407      */
408     excludeFolder: function(path)
409     {
410         this._projectDelegate.excludeFolder(path);
411         var uiSourceCodes = this._uiSourceCodesList.slice();
412         for (var i = 0; i < uiSourceCodes.length; ++i) {
413             var uiSourceCode = uiSourceCodes[i];
414             if (uiSourceCode.path().startsWith(path.substr(1)))
415                 this._removeFile(uiSourceCode.path());
416         }
417     },
418
419     /**
420      * @param {string} path
421      * @param {?string} name
422      * @param {string} content
423      * @param {function(?string)} callback
424      */
425     createFile: function(path, name, content, callback)
426     {
427         this._projectDelegate.createFile(path, name, content, innerCallback);
428
429         function innerCallback(filePath)
430         {
431             callback(filePath);
432         }
433     },
434
435     /**
436      * @param {string} path
437      */
438     deleteFile: function(path)
439     {
440         this._projectDelegate.deleteFile(path);
441     },
442
443     remove: function()
444     {
445         this._projectDelegate.remove();
446     },
447
448     /**
449      * @param {!WebInspector.UISourceCode} uiSourceCode
450      * @param {string} query
451      * @param {boolean} caseSensitive
452      * @param {boolean} isRegex
453      * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
454      */
455     searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
456     {
457         this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
458     },
459
460     /**
461      * @param {!Array.<string>} queries
462      * @param {!Array.<string>} fileQueries
463      * @param {boolean} caseSensitive
464      * @param {boolean} isRegex
465      * @param {!WebInspector.Progress} progress
466      * @param {function(!Array.<string>)} callback
467      */
468     findFilesMatchingSearchRequest: function(queries, fileQueries, caseSensitive, isRegex, progress, callback)
469     {
470         this._projectDelegate.findFilesMatchingSearchRequest(queries, fileQueries, caseSensitive, isRegex, progress, callback);
471     },
472
473     /**
474      * @param {!WebInspector.Progress} progress
475      * @param {function()} callback
476      */
477     indexContent: function(progress, callback)
478     {
479         this._projectDelegate.indexContent(progress, callback);
480     },
481
482     dispose: function()
483     {
484         this._projectDelegate.reset();
485     }
486 }
487
488 WebInspector.projectTypes = {
489     Debugger: "debugger",
490     LiveEdit: "liveedit",
491     Network: "network",
492     Snippets: "snippets",
493     FileSystem: "filesystem"
494 }
495
496 /**
497  * @constructor
498  * @extends {WebInspector.Object}
499  * @param {!WebInspector.FileSystemMapping} fileSystemMapping
500  */
501 WebInspector.Workspace = function(fileSystemMapping)
502 {
503     this._fileSystemMapping = fileSystemMapping;
504     /** @type {!Object.<string, !WebInspector.Project>} */
505     this._projects = {};
506     this._hasResourceContentTrackingExtensions = false;
507 }
508
509 WebInspector.Workspace.Events = {
510     UISourceCodeAdded: "UISourceCodeAdded",
511     UISourceCodeRemoved: "UISourceCodeRemoved",
512     UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
513     ProjectWillReset: "ProjectWillReset"
514 }
515
516 WebInspector.Workspace.prototype = {
517     /**
518      * @return {!Array.<!WebInspector.UISourceCode>}
519      */
520     unsavedSourceCodes: function()
521     {
522         function filterUnsaved(sourceCode)
523         {
524             return sourceCode.isDirty();
525         }
526         return this.uiSourceCodes().filter(filterUnsaved);
527     },
528
529     /**
530      * @param {string} projectId
531      * @param {string} path
532      * @return {?WebInspector.UISourceCode}
533      */
534     uiSourceCode: function(projectId, path)
535     {
536         var project = this._projects[projectId];
537         return project ? project.uiSourceCode(path) : null;
538     },
539
540     /**
541      * @param {string} originURL
542      * @return {?WebInspector.UISourceCode}
543      */
544     uiSourceCodeForOriginURL: function(originURL)
545     {
546         var networkProjects = this.projectsForType(WebInspector.projectTypes.Network)
547         for (var i = 0; i < networkProjects.length; ++i) {
548             var project = networkProjects[i];
549             var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
550             if (uiSourceCode)
551                 return uiSourceCode;
552         }
553         return null;
554     },
555
556     /**
557      * @param {string} type
558      * @return {!Array.<!WebInspector.UISourceCode>}
559      */
560     uiSourceCodesForProjectType: function(type)
561     {
562         var result = [];
563         for (var projectName in this._projects) {
564             var project = this._projects[projectName];
565             if (project.type() === type)
566                 result = result.concat(project.uiSourceCodes());
567         }
568         return result;
569     },
570
571     /**
572      * @param {!WebInspector.ProjectDelegate} projectDelegate
573      * @return {!WebInspector.Project}
574      */
575     addProject: function(projectDelegate)
576     {
577         var projectId = projectDelegate.id();
578         this._projects[projectId] = new WebInspector.Project(this, projectDelegate);
579         return this._projects[projectId];
580     },
581
582     /**
583      * @param {string} projectId
584      */
585     removeProject: function(projectId)
586     {
587         var project = this._projects[projectId];
588         if (!project)
589             return;
590         project.dispose();
591         delete this._projects[projectId];
592     },
593
594     /**
595      * @param {string} projectId
596      * @return {!WebInspector.Project}
597      */
598     project: function(projectId)
599     {
600         return this._projects[projectId];
601     },
602
603     /**
604      * @return {!Array.<!WebInspector.Project>}
605      */
606     projects: function()
607     {
608         return Object.values(this._projects);
609     },
610
611     /**
612      * @param {string} type
613      * @return {!Array.<!WebInspector.Project>}
614      */
615     projectsForType: function(type)
616     {
617         function filterByType(project)
618         {
619             return project.type() === type;
620         }
621         return this.projects().filter(filterByType);
622     },
623
624     /**
625      * @return {!Array.<!WebInspector.UISourceCode>}
626      */
627     uiSourceCodes: function()
628     {
629         var result = [];
630         for (var projectId in this._projects) {
631             var project = this._projects[projectId];
632             result = result.concat(project.uiSourceCodes());
633         }
634         return result;
635     },
636
637     /**
638      * @param {string} url
639      * @return {boolean}
640      */
641     hasMappingForURL: function(url)
642     {
643         return this._fileSystemMapping.hasMappingForURL(url);
644     },
645
646     /**
647      * @param {string} url
648      * @return {?WebInspector.UISourceCode}
649      */
650     _networkUISourceCodeForURL: function(url)
651     {
652         var splitURL = WebInspector.ParsedURL.splitURL(url);
653         var projectId = WebInspector.SimpleProjectDelegate.projectId(splitURL[0], WebInspector.projectTypes.Network);
654         var project = this.project(projectId);
655         return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
656     },
657
658     /**
659      * @param {string} url
660      * @return {?WebInspector.UISourceCode}
661      */
662     uiSourceCodeForURL: function(url)
663     {
664         var file = this._fileSystemMapping.fileForURL(url);
665         if (!file)
666             return this._networkUISourceCodeForURL(url);
667
668         var projectId = WebInspector.FileSystemProjectDelegate.projectId(file.fileSystemPath);
669         var project = this.project(projectId);
670         return project ? project.uiSourceCode(file.filePath) : null;
671     },
672
673     /**
674      * @param {string} fileSystemPath
675      * @param {string} filePath
676      * @return {string}
677      */
678     urlForPath: function(fileSystemPath, filePath)
679     {
680         return this._fileSystemMapping.urlForPath(fileSystemPath, filePath);
681     },
682
683     /**
684      * @param {!WebInspector.UISourceCode} networkUISourceCode
685      * @param {!WebInspector.UISourceCode} uiSourceCode
686      * @param {!WebInspector.FileSystemWorkspaceProvider} fileSystemWorkspaceProvider
687      */
688     addMapping: function(networkUISourceCode, uiSourceCode, fileSystemWorkspaceProvider)
689     {
690         var url = networkUISourceCode.url;
691         var path = uiSourceCode.path();
692         var fileSystemPath = fileSystemWorkspaceProvider.fileSystemPath(uiSourceCode);
693         this._fileSystemMapping.addMappingForResource(url, fileSystemPath, path);
694         WebInspector.suggestReload();
695     },
696
697     /**
698      * @param {!WebInspector.UISourceCode} uiSourceCode
699      */
700     removeMapping: function(uiSourceCode)
701     {
702         this._fileSystemMapping.removeMappingForURL(uiSourceCode.url);
703         WebInspector.suggestReload();
704     },
705
706     /**
707      * @param {boolean} hasExtensions
708      */
709     setHasResourceContentTrackingExtensions: function(hasExtensions)
710     {
711         this._hasResourceContentTrackingExtensions = hasExtensions;
712     },
713
714     /**
715      * @return {boolean}
716      */
717     hasResourceContentTrackingExtensions: function()
718     {
719         return this._hasResourceContentTrackingExtensions;
720     },
721
722     __proto__: WebInspector.Object.prototype
723 }
724
725 /**
726  * @type {!WebInspector.Workspace}
727  */
728 WebInspector.workspace;