f8ba4d5a27c89a1778ae1360269cbdd2a4bc9738
[WebKit-https.git] / Source / WebCore / inspector / front-end / Script.js
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  * @implements {WebInspector.ContentProvider}
29  * @param {string} scriptId
30  * @param {string} sourceURL
31  * @param {number} startLine
32  * @param {number} startColumn
33  * @param {number} endLine
34  * @param {number} endColumn
35  * @param {boolean} isContentScript
36  * @param {string=} sourceMapURL
37  */
38 WebInspector.Script = function(scriptId, sourceURL, startLine, startColumn, endLine, endColumn, isContentScript, sourceMapURL)
39 {
40     this.scriptId = scriptId;
41     this.sourceURL = sourceURL;
42     this.lineOffset = startLine;
43     this.columnOffset = startColumn;
44     this.endLine = endLine;
45     this.endColumn = endColumn;
46     this.isContentScript = isContentScript;
47     this.sourceMapURL = sourceMapURL;
48     this._locations = [];
49 }
50
51 WebInspector.Script.prototype = {
52     /**
53      * @return {?string}
54      */
55     contentURL: function()
56     {
57         return this.sourceURL;
58     },
59
60     /**
61      * @return {WebInspector.ResourceType}
62      */
63     contentType: function()
64     {
65         return WebInspector.resourceTypes.Script;
66     },
67
68     /**
69      * @param {function(?string,boolean,string)} callback
70      */
71     requestContent: function(callback)
72     {
73         if (this._source) {
74             callback(this._source, false, "text/javascript");
75             return;
76         }
77
78         /**
79          * @this {WebInspector.Script}
80          * @param {?Protocol.Error} error
81          * @param {string} source
82          */
83         function didGetScriptSource(error, source)
84         {
85             this._source = error ? "" : source;
86             callback(this._source, false, "text/javascript");
87         }
88         if (this.scriptId) {
89             // Script failed to parse.
90             DebuggerAgent.getScriptSource(this.scriptId, didGetScriptSource.bind(this));
91         } else
92             callback("", false, "text/javascript");
93     },
94
95     /**
96      * @param {string} query
97      * @param {boolean} caseSensitive
98      * @param {boolean} isRegex
99      * @param {function(Array.<PageAgent.SearchMatch>)} callback
100      */
101     searchInContent: function(query, caseSensitive, isRegex, callback)
102     {
103         /**
104          * @this {WebInspector.Script}
105          * @param {?Protocol.Error} error
106          * @param {Array.<PageAgent.SearchMatch>} searchMatches
107          */
108         function innerCallback(error, searchMatches)
109         {
110             if (error)
111                 console.error(error);
112             var result = [];
113             for (var i = 0; i < searchMatches.length; ++i) {
114                 var searchMatch = new WebInspector.ContentProvider.SearchMatch(searchMatches[i].lineNumber, searchMatches[i].lineContent);
115                 result.push(searchMatch);
116             }
117             callback(result || []);
118         }
119
120         if (this.scriptId) {
121             // Script failed to parse.
122             DebuggerAgent.searchInContent(this.scriptId, query, caseSensitive, isRegex, innerCallback.bind(this));
123         } else
124             callback([]);
125     },
126
127     /**
128      * @param {string} newSource
129      * @param {function(?Protocol.Error, Array.<DebuggerAgent.CallFrame>=)} callback
130      */
131     editSource: function(newSource, callback)
132     {
133         /**
134          * @this {WebInspector.Script}
135          * @param {?Protocol.Error} error
136          * @param {Array.<DebuggerAgent.CallFrame>|undefined} callFrames
137          * @param {Object=} debugData
138          */
139         function didEditScriptSource(error, callFrames, debugData)
140         {
141             if (!error)
142                 this._source = newSource;
143             callback(error, callFrames);
144         }
145         if (this.scriptId) {
146             // Script failed to parse.
147             DebuggerAgent.setScriptSource(this.scriptId, newSource, undefined, didEditScriptSource.bind(this));
148         } else
149             callback("Script failed to parse");
150     },
151
152     /**
153      * @return {boolean}
154      */
155     isInlineScript: function()
156     {
157         var startsAtZero = !this.lineOffset && !this.columnOffset;
158         return !!this.sourceURL && !startsAtZero;
159     },
160
161     /**
162      * @return {boolean}
163      */
164     isAnonymousScript: function()
165     {
166         return !this.sourceURL;
167     },
168
169     /**
170      * @param {DebuggerAgent.Location} rawLocation
171      * @return {WebInspector.UILocation}
172      */
173     rawLocationToUILocation: function(rawLocation)
174     {
175         console.assert(rawLocation.scriptId === this.scriptId);
176         var uiLocation = this._sourceMapping.rawLocationToUILocation(rawLocation);
177         return uiLocation.uiSourceCode.overrideLocation(uiLocation);
178     },
179
180     /**
181      * @param {WebInspector.SourceMapping} sourceMapping
182      */
183     setSourceMapping: function(sourceMapping)
184     {
185         this._sourceMapping = sourceMapping;
186         for (var i = 0; i < this._locations.length; ++i)
187             this._locations[i].update();
188     },
189
190     /**
191      * @param {DebuggerAgent.Location} rawLocation
192      * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
193      * @return {WebInspector.Script.Location}
194      */
195     createLiveLocation: function(rawLocation, updateDelegate)
196     {
197         console.assert(rawLocation.scriptId === this.scriptId);
198         var location = new WebInspector.Script.Location(this, rawLocation, updateDelegate);
199         this._locations.push(location);
200         location.update();
201         return location;
202     }
203 }
204
205 /**
206  * @constructor
207  * @param {WebInspector.Script} script
208  * @param {DebuggerAgent.Location} rawLocation
209  * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
210  */
211 WebInspector.Script.Location = function(script, rawLocation, updateDelegate)
212 {
213     this._script = script;
214     this._rawLocation = rawLocation;
215     this._updateDelegate = updateDelegate;
216     this._uiSourceCodes = [];
217 }
218
219 WebInspector.Script.Location.prototype = {
220     update: function()
221     {
222         var uiLocation = this._script.rawLocationToUILocation(this._rawLocation);
223         if (uiLocation) {
224             var uiSourceCode = uiLocation.uiSourceCode;
225             if (this._uiSourceCodes.indexOf(uiSourceCode) === -1) {
226                 uiSourceCode.addLiveLocation(this);
227                 this._uiSourceCodes.push(uiSourceCode);
228             }
229             var oneTime = this._updateDelegate(uiLocation);
230             if (oneTime)
231                 this.dispose();
232         }
233     },
234
235     dispose: function()
236     {
237         for (var i = 0; i < this._uiSourceCodes.length; ++i)
238             this._uiSourceCodes[i].removeLiveLocation(this);
239         this._uiSourceCodes = [];
240         this._script._locations.remove(this);
241     }
242 }