• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
33WebInspector.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
40WebInspector.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}
265