1 /* 2 * Copyright (C) 2008 Apple 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 /** 27 * @constructor 28 * @extends {WebInspector.Object} 29 * @implements {WebInspector.ContentProvider} 30 * @param {string} scriptId 31 * @param {string} sourceURL 32 * @param {number} startLine 33 * @param {number} startColumn 34 * @param {number} endLine 35 * @param {number} endColumn 36 * @param {boolean} isContentScript 37 * @param {string=} sourceMapURL 38 * @param {boolean=} hasSourceURL 39 */ 40 WebInspector.Script = function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL) 41 { 42 this.scriptId = scriptId; 43 this.sourceURL = sourceURL; 44 this.lineOffset = startLine; 45 this.columnOffset = startColumn; 46 this.endLine = endLine; 47 this.endColumn = endColumn; 48 this.isContentScript = isContentScript; 49 this.sourceMapURL = sourceMapURL; 50 this.hasSourceURL = hasSourceURL; 51 /** @type {!Set.<!WebInspector.Script.Location>} */ 52 this._locations = new Set(); 53 /** @type {!Array.<!WebInspector.SourceMapping>} */ 54 this._sourceMappings = []; 55 } 56 57 WebInspector.Script.Events = { 58 ScriptEdited: "ScriptEdited", 59 } 60 61 WebInspector.Script.snippetSourceURLPrefix = "snippets:///"; 62 63 WebInspector.Script.prototype = { 64 /** 65 * @return {string} 66 */ 67 contentURL: function() 68 { 69 return this.sourceURL; 70 }, 71 72 /** 73 * @return {!WebInspector.ResourceType} 74 */ 75 contentType: function() 76 { 77 return WebInspector.resourceTypes.Script; 78 }, 79 80 /** 81 * @param {function(?string)} callback 82 */ 83 requestContent: function(callback) 84 { 85 if (this._source) { 86 callback(this._source); 87 return; 88 } 89 90 /** 91 * @this {WebInspector.Script} 92 * @param {?Protocol.Error} error 93 * @param {string} source 94 */ 95 function didGetScriptSource(error, source) 96 { 97 this._source = error ? "" : source; 98 callback(this._source); 99 } 100 if (this.scriptId) { 101 // Script failed to parse. 102 DebuggerAgent.getScriptSource(this.scriptId, didGetScriptSource.bind(this)); 103 } else 104 callback(""); 105 }, 106 107 /** 108 * @param {string} query 109 * @param {boolean} caseSensitive 110 * @param {boolean} isRegex 111 * @param {function(!Array.<!PageAgent.SearchMatch>)} callback 112 */ 113 searchInContent: function(query, caseSensitive, isRegex, callback) 114 { 115 /** 116 * @this {WebInspector.Script} 117 * @param {?Protocol.Error} error 118 * @param {!Array.<!PageAgent.SearchMatch>} searchMatches 119 */ 120 function innerCallback(error, searchMatches) 121 { 122 if (error) 123 console.error(error); 124 var result = []; 125 for (var i = 0; i < searchMatches.length; ++i) { 126 var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber, searchMatches[i].lineContent); 127 result.push(searchMatch); 128 } 129 callback(result || []); 130 } 131 132 if (this.scriptId) { 133 // Script failed to parse. 134 DebuggerAgent.searchInContent(this.scriptId, query, caseSensitive, isRegex, innerCallback.bind(this)); 135 } else 136 callback([]); 137 }, 138 139 /** 140 * @param {string} newSource 141 * @param {function(?Protocol.Error, !DebuggerAgent.SetScriptSourceError=, !Array.<!DebuggerAgent.CallFrame>=, !DebuggerAgent.StackTrace=, boolean=)} callback 142 */ 143 editSource: function(newSource, callback) 144 { 145 /** 146 * @this {WebInspector.Script} 147 * @param {?Protocol.Error} error 148 * @param {!DebuggerAgent.SetScriptSourceError=} errorData 149 * @param {!Array.<!DebuggerAgent.CallFrame>=} callFrames 150 * @param {!Object=} debugData 151 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 152 */ 153 function didEditScriptSource(error, errorData, callFrames, debugData, asyncStackTrace) 154 { 155 // FIXME: support debugData.stack_update_needs_step_in flag by calling WebInspector.debugger_model.callStackModified 156 if (!error) 157 this._source = newSource; 158 var needsStepIn = !!debugData && debugData["stack_update_needs_step_in"] === true; 159 callback(error, errorData, callFrames, asyncStackTrace, needsStepIn); 160 if (!error) 161 this.dispatchEventToListeners(WebInspector.Script.Events.ScriptEdited, newSource); 162 } 163 164 if (this.scriptId) 165 DebuggerAgent.setScriptSource(this.scriptId, newSource, undefined, didEditScriptSource.bind(this)); 166 else 167 callback("Script failed to parse"); 168 }, 169 170 /** 171 * @return {boolean} 172 */ 173 isInlineScript: function() 174 { 175 var startsAtZero = !this.lineOffset && !this.columnOffset; 176 return !!this.sourceURL && !startsAtZero; 177 }, 178 179 /** 180 * @return {boolean} 181 */ 182 isAnonymousScript: function() 183 { 184 return !this.sourceURL; 185 }, 186 187 /** 188 * @return {boolean} 189 */ 190 isSnippet: function() 191 { 192 return !!this.sourceURL && this.sourceURL.startsWith(WebInspector.Script.snippetSourceURLPrefix); 193 }, 194 195 /** 196 * @param {number} lineNumber 197 * @param {number=} columnNumber 198 * @return {!WebInspector.UILocation} 199 */ 200 rawLocationToUILocation: function(lineNumber, columnNumber) 201 { 202 var uiLocation; 203 var rawLocation = new WebInspector.DebuggerModel.Location(this.scriptId, lineNumber, columnNumber || 0); 204 for (var i = this._sourceMappings.length - 1; !uiLocation && i >= 0; --i) 205 uiLocation = this._sourceMappings[i].rawLocationToUILocation(rawLocation); 206 console.assert(uiLocation, "Script raw location can not be mapped to any ui location."); 207 return uiLocation.uiSourceCode.overrideLocation(uiLocation); 208 }, 209 210 /** 211 * @param {!WebInspector.SourceMapping} sourceMapping 212 */ 213 pushSourceMapping: function(sourceMapping) 214 { 215 this._sourceMappings.push(sourceMapping); 216 this.updateLocations(); 217 }, 218 219 updateLocations: function() 220 { 221 var items = this._locations.items(); 222 for (var i = 0; i < items.length; ++i) 223 items[i].update(); 224 }, 225 226 /** 227 * @param {!WebInspector.DebuggerModel.Location} rawLocation 228 * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate 229 * @return {!WebInspector.Script.Location} 230 */ 231 createLiveLocation: function(rawLocation, updateDelegate) 232 { 233 console.assert(rawLocation.scriptId === this.scriptId); 234 var location = new WebInspector.Script.Location(this, rawLocation, updateDelegate); 235 this._locations.add(location); 236 location.update(); 237 return location; 238 }, 239 240 __proto__: WebInspector.Object.prototype 241 } 242 243 /** 244 * @constructor 245 * @extends {WebInspector.LiveLocation} 246 * @param {!WebInspector.Script} script 247 * @param {!WebInspector.DebuggerModel.Location} rawLocation 248 * @param {function(!WebInspector.UILocation):(boolean|undefined)} updateDelegate 249 */ 250 WebInspector.Script.Location = function(script, rawLocation, updateDelegate) 251 { 252 WebInspector.LiveLocation.call(this, rawLocation, updateDelegate); 253 this._script = script; 254 } 255 256 WebInspector.Script.Location.prototype = { 257 /** 258 * @return {!WebInspector.UILocation} 259 */ 260 uiLocation: function() 261 { 262 var debuggerModelLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (this.rawLocation()); 263 return this._script.rawLocationToUILocation(debuggerModelLocation.lineNumber, debuggerModelLocation.columnNumber); 264 }, 265 266 dispose: function() 267 { 268 WebInspector.LiveLocation.prototype.dispose.call(this); 269 this._script._locations.remove(this); 270 }, 271 272 __proto__: WebInspector.LiveLocation.prototype 273 } 274