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.SDKObject} 29 * @implements {WebInspector.ContentProvider} 30 * @param {!WebInspector.Target} target 31 * @param {string} scriptId 32 * @param {string} sourceURL 33 * @param {number} startLine 34 * @param {number} startColumn 35 * @param {number} endLine 36 * @param {number} endColumn 37 * @param {boolean} isContentScript 38 * @param {string=} sourceMapURL 39 * @param {boolean=} hasSourceURL 40 */ 41 WebInspector.Script = function(target, scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL, hasSourceURL) 42 { 43 WebInspector.SDKObject.call(this, target); 44 this.scriptId = scriptId; 45 this.sourceURL = sourceURL; 46 this.lineOffset = startLine; 47 this.columnOffset = startColumn; 48 this.endLine = endLine; 49 this.endColumn = endColumn; 50 this._isContentScript = isContentScript; 51 this.sourceMapURL = sourceMapURL; 52 this.hasSourceURL = hasSourceURL; 53 } 54 55 WebInspector.Script.Events = { 56 ScriptEdited: "ScriptEdited", 57 SourceMapURLAdded: "SourceMapURLAdded", 58 } 59 60 WebInspector.Script.snippetSourceURLPrefix = "snippets:///"; 61 62 WebInspector.Script.sourceURLRegex = /\n[\040\t]*\/\/[@#]\ssourceURL=\s*(\S*?)\s*$/mg; 63 64 /** 65 * @param {string} source 66 * @return {string} 67 */ 68 WebInspector.Script._trimSourceURLComment = function(source) 69 { 70 return source.replace(WebInspector.Script.sourceURLRegex, ""); 71 } 72 73 74 WebInspector.Script.prototype = { 75 /** 76 * @return {boolean} 77 */ 78 isContentScript: function() 79 { 80 return this._isContentScript; 81 }, 82 83 /** 84 * @return {string} 85 */ 86 contentURL: function() 87 { 88 return this.sourceURL; 89 }, 90 91 /** 92 * @return {!WebInspector.ResourceType} 93 */ 94 contentType: function() 95 { 96 return WebInspector.resourceTypes.Script; 97 }, 98 99 /** 100 * @param {function(?string)} callback 101 */ 102 requestContent: function(callback) 103 { 104 if (this._source) { 105 callback(this._source); 106 return; 107 } 108 109 /** 110 * @this {WebInspector.Script} 111 * @param {?Protocol.Error} error 112 * @param {string} source 113 */ 114 function didGetScriptSource(error, source) 115 { 116 this._source = WebInspector.Script._trimSourceURLComment(error ? "" : source); 117 callback(this._source); 118 } 119 if (this.scriptId) { 120 // Script failed to parse. 121 this.target().debuggerAgent().getScriptSource(this.scriptId, didGetScriptSource.bind(this)); 122 } else 123 callback(""); 124 }, 125 126 /** 127 * @param {string} query 128 * @param {boolean} caseSensitive 129 * @param {boolean} isRegex 130 * @param {function(!Array.<!PageAgent.SearchMatch>)} callback 131 */ 132 searchInContent: function(query, caseSensitive, isRegex, callback) 133 { 134 /** 135 * @param {?Protocol.Error} error 136 * @param {!Array.<!PageAgent.SearchMatch>} searchMatches 137 */ 138 function innerCallback(error, searchMatches) 139 { 140 if (error) 141 console.error(error); 142 var result = []; 143 for (var i = 0; i < searchMatches.length; ++i) { 144 var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber, searchMatches[i].lineContent); 145 result.push(searchMatch); 146 } 147 callback(result || []); 148 } 149 150 if (this.scriptId) { 151 // Script failed to parse. 152 this.target().debuggerAgent().searchInContent(this.scriptId, query, caseSensitive, isRegex, innerCallback); 153 } else { 154 callback([]); 155 } 156 }, 157 158 /** 159 * @param {string} source 160 * @return {string} 161 */ 162 _appendSourceURLCommentIfNeeded: function(source) 163 { 164 if (!this.hasSourceURL) 165 return source; 166 return source + "\n //# sourceURL=" + this.sourceURL; 167 }, 168 169 /** 170 * @param {string} newSource 171 * @param {function(?Protocol.Error, !DebuggerAgent.SetScriptSourceError=, !Array.<!DebuggerAgent.CallFrame>=, !DebuggerAgent.StackTrace=, boolean=)} callback 172 */ 173 editSource: function(newSource, callback) 174 { 175 /** 176 * @this {WebInspector.Script} 177 * @param {?Protocol.Error} error 178 * @param {!DebuggerAgent.SetScriptSourceError=} errorData 179 * @param {!Array.<!DebuggerAgent.CallFrame>=} callFrames 180 * @param {!Object=} debugData 181 * @param {!DebuggerAgent.StackTrace=} asyncStackTrace 182 */ 183 function didEditScriptSource(error, errorData, callFrames, debugData, asyncStackTrace) 184 { 185 // FIXME: support debugData.stack_update_needs_step_in flag by calling WebInspector.debugger_model.callStackModified 186 if (!error) 187 this._source = newSource; 188 var needsStepIn = !!debugData && debugData["stack_update_needs_step_in"] === true; 189 callback(error, errorData, callFrames, asyncStackTrace, needsStepIn); 190 if (!error) 191 this.dispatchEventToListeners(WebInspector.Script.Events.ScriptEdited, newSource); 192 } 193 194 newSource = WebInspector.Script._trimSourceURLComment(newSource); 195 // We append correct sourceURL to script for consistency only. It's not actually needed for things to work correctly. 196 newSource = this._appendSourceURLCommentIfNeeded(newSource); 197 198 if (this.scriptId) 199 this.target().debuggerAgent().setScriptSource(this.scriptId, newSource, undefined, didEditScriptSource.bind(this)); 200 else 201 callback("Script failed to parse"); 202 }, 203 204 /** 205 * @param {number} lineNumber 206 * @param {number=} columnNumber 207 * @return {!WebInspector.DebuggerModel.Location} 208 */ 209 rawLocation: function(lineNumber, columnNumber) 210 { 211 return new WebInspector.DebuggerModel.Location(this.target(), this.scriptId, lineNumber, columnNumber || 0); 212 }, 213 214 /** 215 * @return {boolean} 216 */ 217 isInlineScript: function() 218 { 219 var startsAtZero = !this.lineOffset && !this.columnOffset; 220 return !!this.sourceURL && !startsAtZero; 221 }, 222 223 /** 224 * @param {string} sourceMapURL 225 */ 226 addSourceMapURL: function(sourceMapURL) 227 { 228 if (this.sourceMapURL) 229 return; 230 this.sourceMapURL = sourceMapURL; 231 this.dispatchEventToListeners(WebInspector.Script.Events.SourceMapURLAdded, this.sourceMapURL); 232 }, 233 234 /** 235 * @return {boolean} 236 */ 237 isAnonymousScript: function() 238 { 239 return !this.sourceURL; 240 }, 241 242 /** 243 * @return {boolean} 244 */ 245 isSnippet: function() 246 { 247 return !!this.sourceURL && this.sourceURL.startsWith(WebInspector.Script.snippetSourceURLPrefix); 248 }, 249 250 /** 251 * @return {boolean} 252 */ 253 isInlineScriptWithSourceURL: function() 254 { 255 return !!this.hasSourceURL && this.isInlineScript() 256 }, 257 258 __proto__: WebInspector.SDKObject.prototype 259 } 260