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