e1c3996f31e1cda3df56ac8c8a2c0b6e5ecb5985
[WebKit-https.git] / Source / WebCore / inspector / front-end / ContentProviders.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  *     * 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  * @implements {WebInspector.ContentProvider}
34  * @param {Array.<WebInspector.Script>} scripts
35  */
36 WebInspector.ConcatenatedScriptsContentProvider = function(scripts)
37 {
38     this._mimeType = "text/html";
39     this._scripts = scripts;
40 }
41
42 WebInspector.ConcatenatedScriptsContentProvider.scriptOpenTag = "<script>";
43 WebInspector.ConcatenatedScriptsContentProvider.scriptCloseTag = "</script>";
44
45 WebInspector.ConcatenatedScriptsContentProvider.prototype = {
46     /**
47      * @return {Array.<WebInspector.Script>}
48      */
49     _sortedScripts: function()
50     {
51         if (this._sortedScriptsArray)
52             return this._sortedScriptsArray;
53
54         this._sortedScriptsArray = [];
55         
56         var scripts = this._scripts.slice();
57         scripts.sort(function(x, y) { return x.lineOffset - y.lineOffset || x.columnOffset - y.columnOffset; });
58         
59         var scriptOpenTagLength = WebInspector.ConcatenatedScriptsContentProvider.scriptOpenTag.length;
60         var scriptCloseTagLength = WebInspector.ConcatenatedScriptsContentProvider.scriptCloseTag.length;
61         
62         this._sortedScriptsArray.push(scripts[0]);
63         for (var i = 1; i < scripts.length; ++i) {
64             var previousScript = this._sortedScriptsArray[this._sortedScriptsArray.length - 1];
65             
66             var lineNumber = previousScript.endLine;
67             var columnNumber = previousScript.endColumn + scriptCloseTagLength + scriptOpenTagLength;
68             
69             if (lineNumber < scripts[i].lineOffset || (lineNumber === scripts[i].lineOffset && columnNumber <= scripts[i].columnOffset))
70                 this._sortedScriptsArray.push(scripts[i]);
71         }
72         return this._sortedScriptsArray;
73     },
74
75     /**
76      * @return {?string}
77      */
78     contentURL: function()
79     {
80         return null;
81     },
82
83     /**
84      * @return {WebInspector.ResourceType}
85      */
86     contentType: function()
87     {
88         return WebInspector.resourceTypes.Document;
89     },
90     
91     /**
92      * @param {function(?string,boolean,string)} callback
93      */
94     requestContent: function(callback)
95     {
96         var scripts = this._sortedScripts();
97         var sources = [];
98         function didRequestSource(content, contentEncoded, mimeType)
99         {
100             sources.push(content);
101             if (sources.length == scripts.length)
102                 callback(this._concatenateScriptsContent(scripts, sources), false, this._mimeType);
103         }
104         for (var i = 0; i < scripts.length; ++i)
105             scripts[i].requestContent(didRequestSource.bind(this));
106     },
107
108     /**
109      * @param {string} query
110      * @param {boolean} caseSensitive
111      * @param {boolean} isRegex
112      * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
113      */
114     searchInContent: function(query, caseSensitive, isRegex, callback)
115     {
116         var results = {};
117         var scripts = this._sortedScripts();
118         var scriptsLeft = scripts.length;
119
120         function maybeCallback()
121         {
122             if (scriptsLeft)
123                 return;
124
125             var result = [];
126             for (var i = 0; i < scripts.length; ++i)
127                 result = result.concat(results[scripts[i].scriptId]);
128             callback(result);
129         }
130
131         /**
132          * @param {WebInspector.Script} script
133          * @param {Array.<PageAgent.SearchMatch>} searchMatches
134          */
135         function searchCallback(script, searchMatches)
136         {
137             results[script.scriptId] = [];
138             for (var i = 0; i < searchMatches.length; ++i) {
139                 var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber + script.lineOffset, searchMatches[i].lineContent);
140                 results[script.scriptId].push(searchMatch);
141             }
142             scriptsLeft--;
143             maybeCallback.call(this);
144         }
145
146         maybeCallback();
147         for (var i = 0; i < scripts.length; ++i)
148             scripts[i].searchInContent(query, caseSensitive, isRegex, searchCallback.bind(this, scripts[i]));
149     },
150
151     /**
152      * @return {string}
153      */
154     _concatenateScriptsContent: function(scripts, sources)
155     {
156         var content = "";
157         var lineNumber = 0;
158         var columnNumber = 0;
159
160         var scriptOpenTag = WebInspector.ConcatenatedScriptsContentProvider.scriptOpenTag;
161         var scriptCloseTag = WebInspector.ConcatenatedScriptsContentProvider.scriptCloseTag;
162         for (var i = 0; i < scripts.length; ++i) {
163             // Fill the gap with whitespace characters.
164             for (var newLinesCount = scripts[i].lineOffset - lineNumber; newLinesCount > 0; --newLinesCount) {
165                 columnNumber = 0;
166                 content += "\n";
167             }
168             for (var spacesCount = scripts[i].columnOffset - columnNumber - scriptOpenTag.length; spacesCount > 0; --spacesCount)
169                 content += " ";
170
171             // Add script tag.
172             content += scriptOpenTag;
173             content += sources[i];
174             content += scriptCloseTag;
175             lineNumber = scripts[i].endLine;
176             columnNumber = scripts[i].endColumn + scriptCloseTag.length;
177         }
178
179         return content;
180     }
181 }
182
183 WebInspector.ConcatenatedScriptsContentProvider.prototype.__proto__ = WebInspector.ContentProvider.prototype;
184
185 /**
186  * @constructor
187  * @implements {WebInspector.ContentProvider}
188  */
189 WebInspector.CompilerSourceMappingContentProvider = function(sourceURL)
190 {
191     this._sourceURL = sourceURL;
192 }
193
194 WebInspector.CompilerSourceMappingContentProvider.prototype = {
195     /**
196      * @return {?string}
197      */
198     contentURL: function()
199     {
200         return this._sourceURL;
201     },
202
203     /**
204      * @return {WebInspector.ResourceType}
205      */
206     contentType: function()
207     {
208         return WebInspector.resourceTypes.Script;
209     },
210     
211     /**
212      * @param {function(?string,boolean,string)} callback
213      */
214     requestContent: function(callback)
215     {
216         var sourceCode = "";
217         try {
218             // FIXME: make sendRequest async.
219             sourceCode = InspectorFrontendHost.loadResourceSynchronously(this._sourceURL);
220         } catch(e) {
221             console.error(e.message);
222         }
223         callback(sourceCode, false, "text/javascript");
224     },
225
226     /**
227      * @param {string} query
228      * @param {boolean} caseSensitive
229      * @param {boolean} isRegex
230      * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
231      */
232     searchInContent: function(query, caseSensitive, isRegex, callback)
233     {
234         callback([]);
235     }
236 }
237
238 WebInspector.CompilerSourceMappingContentProvider.prototype.__proto__ = WebInspector.ContentProvider.prototype;
239
240 /**
241  * @constructor
242  * @implements {WebInspector.ContentProvider}
243  * @param {WebInspector.ResourceType} contentType 
244  * @param {string} content 
245  */
246 WebInspector.StaticContentProvider = function(contentType, content)
247 {
248     this._content = content;
249     this._contentType = contentType;
250 }
251
252 WebInspector.StaticContentProvider.prototype = {
253     /**
254      * @return {?string}
255      */
256     contentURL: function()
257     {
258         return null;
259     },
260
261     /**
262      * @return {WebInspector.ResourceType}
263      */
264     contentType: function()
265     {
266         return this._contentType;
267     },
268
269     /**
270      * @param {function(?string,boolean,string)} callback
271      */
272     requestContent: function(callback)
273     {
274         callback(this._content, false, this._contentType.canonicalMimeType());
275     },
276
277     /**
278      * @param {string} query
279      * @param {boolean} caseSensitive
280      * @param {boolean} isRegex
281      * @param {function(Array.<WebInspector.ContentProvider.SearchMatch>)} callback
282      */
283     searchInContent: function(query, caseSensitive, isRegex, callback)
284     {
285         function performSearch()
286         {
287             var regex = createSearchRegex(query, caseSensitive, isRegex);
288             
289             var result = [];
290             var lineEndings = this._content.lineEndings();
291             for (var i = 0; i < lineEndings.length; ++i) {
292                 var lineStart = i > 0 ? lineEndings[i - 1] + 1 : 0;
293                 var lineEnd = lineEndings[i];
294                 var lineContent = this._content.substring(lineStart, lineEnd);
295                 if (lineContent.length > 0 && lineContent.charAt(lineContent.length - 1) === "\r")
296                     lineContent = lineContent.substring(0, lineContent.length - 1)
297                 
298                 if (regex.exec(lineContent))
299                     result.push(new WebInspector.ContentProvider.SearchMatch(i, lineContent));
300             }
301             callback(result);
302         }
303
304         // searchInContent should call back later.
305         window.setTimeout(performSearch.bind(this), 0);
306     }
307 }
308
309 WebInspector.StaticContentProvider.prototype.__proto__ = WebInspector.ContentProvider.prototype;