Upstream version 7.35.144.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.Formatter || 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     Formatter: "formatter",
491     LiveEdit: "liveedit",
492     Network: "network",
493     Snippets: "snippets",
494     FileSystem: "filesystem"
495 }
496
497 /**
498  * @constructor
499  * @extends {WebInspector.Object}
500  * @param {!WebInspector.FileSystemMapping} fileSystemMapping
501  */
502 WebInspector.Workspace = function(fileSystemMapping)
503 {
504     this._fileSystemMapping = fileSystemMapping;
505     /** @type {!Object.<string, !WebInspector.Project>} */
506     this._projects = {};
507     this._hasResourceContentTrackingExtensions = false;
508 }
509
510 WebInspector.Workspace.Events = {
511     UISourceCodeAdded: "UISourceCodeAdded",
512     UISourceCodeRemoved: "UISourceCodeRemoved",
513     UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
514     ProjectWillReset: "ProjectWillReset"
515 }
516
517 WebInspector.Workspace.prototype = {
518     /**
519      * @return {!Array.<!WebInspector.UISourceCode>}
520      */
521     unsavedSourceCodes: function()
522     {
523         function filterUnsaved(sourceCode)
524         {
525             return sourceCode.isDirty();
526         }
527         return this.uiSourceCodes().filter(filterUnsaved);
528     },
529
530     /**
531      * @param {string} projectId
532      * @param {string} path
533      * @return {?WebInspector.UISourceCode}
534      */
535     uiSourceCode: function(projectId, path)
536     {
537         var project = this._projects[projectId];
538         return project ? project.uiSourceCode(path) : null;
539     },
540
541     /**
542      * @param {string} originURL
543      * @return {?WebInspector.UISourceCode}
544      */
545     uiSourceCodeForOriginURL: function(originURL)
546     {
547         var networkProjects = this.projectsForType(WebInspector.projectTypes.Network)
548         for (var i = 0; i < networkProjects.length; ++i) {
549             var project = networkProjects[i];
550             var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
551             if (uiSourceCode)
552                 return uiSourceCode;
553         }
554         return null;
555     },
556
557     /**
558      * @param {string} type
559      * @return {!Array.<!WebInspector.UISourceCode>}
560      */
561     uiSourceCodesForProjectType: function(type)
562     {
563         var result = [];
564         for (var projectName in this._projects) {
565             var project = this._projects[projectName];
566             if (project.type() === type)
567                 result = result.concat(project.uiSourceCodes());
568         }
569         return result;
570     },
571
572     /**
573      * @param {!WebInspector.ProjectDelegate} projectDelegate
574      * @return {!WebInspector.Project}
575      */
576     addProject: function(projectDelegate)
577     {
578         var projectId = projectDelegate.id();
579         this._projects[projectId] = new WebInspector.Project(this, projectDelegate);
580         return this._projects[projectId];
581     },
582
583     /**
584      * @param {string} projectId
585      */
586     removeProject: function(projectId)
587     {
588         var project = this._projects[projectId];
589         if (!project)
590             return;
591         project.dispose();
592         delete this._projects[projectId];
593     },
594
595     /**
596      * @param {string} projectId
597      * @return {!WebInspector.Project}
598      */
599     project: function(projectId)
600     {
601         return this._projects[projectId];
602     },
603
604     /**
605      * @return {!Array.<!WebInspector.Project>}
606      */
607     projects: function()
608     {
609         return Object.values(this._projects);
610     },
611
612     /**
613      * @param {string} type
614      * @return {!Array.<!WebInspector.Project>}
615      */
616     projectsForType: function(type)
617     {
618         function filterByType(project)
619         {
620             return project.type() === type;
621         }
622         return this.projects().filter(filterByType);
623     },
624
625     /**
626      * @return {!Array.<!WebInspector.UISourceCode>}
627      */
628     uiSourceCodes: function()
629     {
630         var result = [];
631         for (var projectId in this._projects) {
632             var project = this._projects[projectId];
633             result = result.concat(project.uiSourceCodes());
634         }
635         return result;
636     },
637
638     /**
639      * @param {string} url
640      * @return {boolean}
641      */
642     hasMappingForURL: function(url)
643     {
644         return this._fileSystemMapping.hasMappingForURL(url);
645     },
646
647     /**
648      * @param {string} url
649      * @return {?WebInspector.UISourceCode}
650      */
651     _networkUISourceCodeForURL: function(url)
652     {
653         var splitURL = WebInspector.ParsedURL.splitURL(url);
654         var projectId = WebInspector.SimpleProjectDelegate.projectId(splitURL[0], WebInspector.projectTypes.Network);
655         var project = this.project(projectId);
656         return project ? project.uiSourceCode(splitURL.slice(1).join("/")) : null;
657     },
658
659     /**
660      * @param {string} url
661      * @return {?WebInspector.UISourceCode}
662      */
663     uiSourceCodeForURL: function(url)
664     {
665         var file = this._fileSystemMapping.fileForURL(url);
666         if (!file)
667             return this._networkUISourceCodeForURL(url);
668
669         var projectId = WebInspector.FileSystemProjectDelegate.projectId(file.fileSystemPath);
670         var project = this.project(projectId);
671         return project ? project.uiSourceCode(file.filePath) : null;
672     },
673
674     /**
675      * @param {string} fileSystemPath
676      * @param {string} filePath
677      * @return {string}
678      */
679     urlForPath: function(fileSystemPath, filePath)
680     {
681         return this._fileSystemMapping.urlForPath(fileSystemPath, filePath);
682     },
683
684     /**
685      * @param {!WebInspector.UISourceCode} networkUISourceCode
686      * @param {!WebInspector.UISourceCode} uiSourceCode
687      * @param {!WebInspector.FileSystemWorkspaceProvider} fileSystemWorkspaceProvider
688      */
689     addMapping: function(networkUISourceCode, uiSourceCode, fileSystemWorkspaceProvider)
690     {
691         var url = networkUISourceCode.url;
692         var path = uiSourceCode.path();
693         var fileSystemPath = fileSystemWorkspaceProvider.fileSystemPath(uiSourceCode);
694         this._fileSystemMapping.addMappingForResource(url, fileSystemPath, path);
695     },
696
697     /**
698      * @param {!WebInspector.UISourceCode} uiSourceCode
699      */
700     removeMapping: function(uiSourceCode)
701     {
702         this._fileSystemMapping.removeMappingForURL(uiSourceCode.url);
703     },
704
705     /**
706      * @param {boolean} hasExtensions
707      */
708     setHasResourceContentTrackingExtensions: function(hasExtensions)
709     {
710         this._hasResourceContentTrackingExtensions = hasExtensions;
711     },
712
713     /**
714      * @return {boolean}
715      */
716     hasResourceContentTrackingExtensions: function()
717     {
718         return this._hasResourceContentTrackingExtensions;
719     },
720
721     __proto__: WebInspector.Object.prototype
722 }
723
724 /**
725  * @type {!WebInspector.Workspace}
726  */
727 WebInspector.workspace;