• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
40WebInspector.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
57WebInspector.Script.Events = {
58    ScriptEdited: "ScriptEdited",
59}
60
61WebInspector.Script.snippetSourceURLPrefix = "snippets:///";
62
63WebInspector.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 */
250WebInspector.Script.Location = function(script, rawLocation, updateDelegate)
251{
252    WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
253    this._script = script;
254}
255
256WebInspector.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