Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / search / SourcesSearchScope.js
1 /*
2  * Copyright (C) 2011 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  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20  * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /**
30  * @constructor
31  * @implements {WebInspector.SearchScope}
32  */
33 WebInspector.SourcesSearchScope = function()
34 {
35     // FIXME: Add title once it is used by search controller.
36     this._searchId = 0;
37     this._workspace = WebInspector.workspace;
38 }
39
40 WebInspector.SourcesSearchScope.prototype = {
41     /**
42      * @param {!WebInspector.Progress} progress
43      * @param {function(boolean)} indexingFinishedCallback
44      */
45     performIndexing: function(progress, indexingFinishedCallback)
46     {
47         this.stopSearch();
48
49         var projects = this._projects();
50         var compositeProgress = new WebInspector.CompositeProgress(progress);
51         progress.addEventListener(WebInspector.Progress.Events.Canceled, indexingCanceled);
52         for (var i = 0; i < projects.length; ++i) {
53             var project = projects[i];
54             var projectProgress = compositeProgress.createSubProgress(project.uiSourceCodes().length);
55             project.indexContent(projectProgress);
56         }
57         compositeProgress.addEventListener(WebInspector.Progress.Events.Done, indexingFinishedCallback.bind(this, true));
58
59         function indexingCanceled()
60         {
61             indexingFinishedCallback(false);
62             progress.done();
63         }
64     },
65
66     /**
67      * @return {!Array.<!WebInspector.Project>}
68      */
69     _projects: function()
70     {
71         /**
72          * @param {!WebInspector.Project} project
73          * @return {boolean}
74          */
75         function filterOutServiceProjects(project)
76         {
77             return !project.isServiceProject() || project.type() === WebInspector.projectTypes.Formatter;
78         }
79
80         /**
81          * @param {!WebInspector.Project} project
82          * @return {boolean}
83          */
84         function filterOutContentScriptsIfNeeded(project)
85         {
86             return WebInspector.settings.searchInContentScripts.get() || project.type() !== WebInspector.projectTypes.ContentScripts;
87         }
88
89         return this._workspace.projects().filter(filterOutServiceProjects).filter(filterOutContentScriptsIfNeeded);
90     },
91
92     /**
93      * @param {!WebInspector.ProjectSearchConfig} searchConfig
94      * @param {!WebInspector.Progress} progress
95      * @param {function(!WebInspector.FileBasedSearchResult)} searchResultCallback
96      * @param {function(boolean)} searchFinishedCallback
97      */
98     performSearch: function(searchConfig, progress, searchResultCallback, searchFinishedCallback)
99     {
100         this.stopSearch();
101         this._searchResultCallback = searchResultCallback;
102         this._searchFinishedCallback = searchFinishedCallback;
103         this._searchConfig = searchConfig;
104
105         var projects = this._projects();
106         var barrier = new CallbackBarrier();
107         var compositeProgress = new WebInspector.CompositeProgress(progress);
108         for (var i = 0; i < projects.length; ++i) {
109             var project = projects[i];
110             var weight = project.uiSourceCodes().length;
111             var projectProgress = new WebInspector.CompositeProgress(compositeProgress.createSubProgress(weight));
112             var findMatchingFilesProgress = projectProgress.createSubProgress();
113             var searchContentProgress = projectProgress.createSubProgress();
114             var barrierCallback = barrier.createCallback();
115             var callback = this._processMatchingFilesForProject.bind(this, this._searchId, project, searchContentProgress, barrierCallback);
116             project.findFilesMatchingSearchRequest(searchConfig, findMatchingFilesProgress, callback);
117         }
118         barrier.callWhenDone(this._searchFinishedCallback.bind(this, true));
119     },
120
121     /**
122      * @param {number} searchId
123      * @param {!WebInspector.Project} project
124      * @param {!WebInspector.Progress} progress
125      * @param {function()} callback
126      * @param {!Array.<string>} files
127      */
128     _processMatchingFilesForProject: function(searchId, project, progress, callback, files)
129     {
130         if (searchId !== this._searchId) {
131             this._searchFinishedCallback(false);
132             return;
133         }
134
135         addDirtyFiles.call(this);
136
137         if (!files.length) {
138             progress.done();
139             callback();
140             return;
141         }
142
143         progress.setTotalWork(files.length);
144
145         var fileIndex = 0;
146         var maxFileContentRequests = 20;
147         var callbacksLeft = 0;
148
149         for (var i = 0; i < maxFileContentRequests && i < files.length; ++i)
150             scheduleSearchInNextFileOrFinish.call(this);
151
152         /**
153          * @this {WebInspector.SourcesSearchScope}
154          */
155         function addDirtyFiles()
156         {
157             var matchingFiles = StringSet.fromArray(files);
158             var uiSourceCodes = project.uiSourceCodes();
159             for (var i = 0; i < uiSourceCodes.length; ++i) {
160                 if (!uiSourceCodes[i].isDirty())
161                     continue;
162                 var path = uiSourceCodes[i].path();
163                 if (!matchingFiles.contains(path) && this._searchConfig.filePathMatchesFileQuery(path))
164                     files.push(path);
165             }
166         }
167
168         /**
169          * @param {string} path
170          * @this {WebInspector.SourcesSearchScope}
171          */
172         function searchInNextFile(path)
173         {
174             var uiSourceCode = project.uiSourceCode(path);
175             if (!uiSourceCode) {
176                 --callbacksLeft;
177                 progress.worked(1);
178                 scheduleSearchInNextFileOrFinish.call(this);
179                 return;
180             }
181             if (uiSourceCode.isDirty())
182                 contentLoaded.call(this, uiSourceCode.path(), uiSourceCode.workingCopy());
183             else
184                 uiSourceCode.checkContentUpdated(contentUpdated.bind(this, uiSourceCode));
185         }
186
187         /**
188          * @param {!WebInspector.UISourceCode} uiSourceCode
189          * @this {WebInspector.SourcesSearchScope}
190          */
191         function contentUpdated(uiSourceCode)
192         {
193             uiSourceCode.requestContent(contentLoaded.bind(this, uiSourceCode.path()));
194         }
195
196         /**
197          * @this {WebInspector.SourcesSearchScope}
198          */
199         function scheduleSearchInNextFileOrFinish()
200         {
201             if (fileIndex >= files.length) {
202                 if (!callbacksLeft) {
203                     progress.done();
204                     callback();
205                     return;
206                 }
207                 return;
208             }
209
210             ++callbacksLeft;
211             var path = files[fileIndex++];
212             setTimeout(searchInNextFile.bind(this, path), 0);
213         }
214
215         /**
216          * @param {string} path
217          * @param {?string} content
218          * @this {WebInspector.SourcesSearchScope}
219          */
220         function contentLoaded(path, content)
221         {
222             /**
223              * @param {!WebInspector.ContentProvider.SearchMatch} a
224              * @param {!WebInspector.ContentProvider.SearchMatch} b
225              */
226             function matchesComparator(a, b)
227             {
228                 return a.lineNumber - b.lineNumber;
229             }
230
231             progress.worked(1);
232             var matches = [];
233             var queries = this._searchConfig.queries();
234             if (content !== null) {
235                 for (var i = 0; i < queries.length; ++i) {
236                     var nextMatches = WebInspector.ContentProvider.performSearchInContent(content, queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex())
237                     matches = matches.mergeOrdered(nextMatches, matchesComparator);
238                 }
239             }
240             var uiSourceCode = project.uiSourceCode(path);
241             if (matches && uiSourceCode) {
242                 var searchResult = new WebInspector.FileBasedSearchResult(uiSourceCode, matches);
243                 this._searchResultCallback(searchResult);
244             }
245
246             --callbacksLeft;
247             scheduleSearchInNextFileOrFinish.call(this);
248         }
249     },
250
251     stopSearch: function()
252     {
253         ++this._searchId;
254     },
255
256     /**
257      * @param {!WebInspector.ProjectSearchConfig} searchConfig
258      * @return {!WebInspector.FileBasedSearchResultsPane}
259      */
260     createSearchResultsPane: function(searchConfig)
261     {
262         return new WebInspector.FileBasedSearchResultsPane(searchConfig);
263     }
264 }