Web Inspector: live locations could be disposed twice.
[WebKit-https.git] / Source / WebCore / inspector / front-end / ScriptMapping.js
1 /*
2  * Copyright (C) 2012 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  * @extends {WebInspector.Object}
34  */
35 WebInspector.ScriptMapping = function()
36 {
37 }
38
39 WebInspector.ScriptMapping.Events = {
40     UISourceCodeListChanged: "us-source-code-list-changed"
41 }
42
43 WebInspector.ScriptMapping.prototype = {
44     /**
45      * @param {DebuggerAgent.Location} rawLocation
46      * @return {WebInspector.UILocation}
47      */
48     rawLocationToUILocation: function(rawLocation) {},
49
50     /**
51      * @param {WebInspector.UISourceCode} uiSourceCode
52      * @param {number} lineNumber
53      * @param {number} columnNumber
54      * @return {DebuggerAgent.Location}
55      */
56     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber) {},
57
58     /**
59      * @return {Array.<WebInspector.UISourceCode>}
60      */
61     uiSourceCodeList: function() {}
62 }
63
64 WebInspector.ScriptMapping.prototype.__proto__ = WebInspector.Object.prototype;
65
66 /**
67  * @constructor
68  * @extends {WebInspector.Object}
69  */
70 WebInspector.MainScriptMapping = function()
71 {
72     this._mappings = [];
73
74     this._resourceMapping = new WebInspector.ResourceScriptMapping();
75     this._mappings.push(this._resourceMapping);
76     this._compilerMapping = new WebInspector.CompilerScriptMapping();
77     this._mappings.push(this._compilerMapping);
78
79     for (var i = 0; i < this._mappings.length; ++i)
80         this._mappings[i].addEventListener(WebInspector.ScriptMapping.Events.UISourceCodeListChanged, this._handleUISourceCodeListChanged, this);
81
82     if (WebInspector.experimentsSettings.snippetsSupport.isEnabled()) {
83         this._snippetsMapping = new WebInspector.SnippetsScriptMapping();
84         this._mappings.push(this._snippetsMapping);
85     }
86
87     for (var i = 0; i < this._mappings.length; ++i)
88         this._mappings[i].addEventListener(WebInspector.ScriptMapping.Events.UISourceCodeListChanged, this._handleUISourceCodeListChanged, this);
89
90     this._mappingForScriptId = {};
91     this._mappingForUISourceCode = new Map();
92     this._liveLocationsForScriptId = {};
93 }
94
95 WebInspector.MainScriptMapping.Events = {
96     UISourceCodeListChanged: "us-source-code-list-changed"
97 }
98
99 WebInspector.MainScriptMapping.prototype = {
100     /**
101      * @param {DebuggerAgent.Location} rawLocation
102      * @return {WebInspector.UILocation}
103      */
104     rawLocationToUILocation: function(rawLocation)
105     {
106         return this._mappingForScriptId[rawLocation.scriptId].rawLocationToUILocation(rawLocation);
107     },
108
109     /**
110      * @param {WebInspector.UISourceCode} uiSourceCode
111      * @param {number} lineNumber
112      * @param {number} columnNumber
113      * @return {DebuggerAgent.Location}
114      */
115     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
116     {
117         return this._mappingForUISourceCode.get(uiSourceCode).uiLocationToRawLocation(uiSourceCode, lineNumber, columnNumber);
118     },
119
120     /**
121      * @param {DebuggerAgent.Location} rawLocation
122      * @param {function(WebInspector.UILocation)} updateDelegate
123      * @return {WebInspector.LiveLocation}
124      */
125     createLiveLocation: function(rawLocation, updateDelegate)
126     {
127         return new WebInspector.LiveLocation(this, rawLocation, updateDelegate);
128     },
129
130     _registerLiveLocation: function(scriptId, liveLocation)
131     {
132         this._liveLocationsForScriptId[scriptId].push(liveLocation)
133         liveLocation._update();
134     },
135
136     _unregisterLiveLocation: function(scriptId, liveLocation)
137     {
138         if (this._liveLocationsForScriptId[scriptId])
139             this._liveLocationsForScriptId[scriptId].remove(liveLocation);
140     },
141
142     _updateLiveLocations: function(scriptIds)
143     {
144         for (var i = 0; i < scriptIds.length; ++i) {
145             var liveLocations = this._liveLocationsForScriptId[scriptIds[i]];
146             for (var j = 0; j < liveLocations.length; ++j)
147                 liveLocations[j]._update();
148         }
149     },
150
151     /**
152      * @return {Array.<WebInspector.UISourceCode>}
153      */
154     uiSourceCodeList: function()
155     {
156         var result = [];
157         for (var i = 0; i < this._mappings.length; ++i) {
158             var uiSourceCodeList = this._mappings[i].uiSourceCodeList();
159             for (var j = 0; j < uiSourceCodeList.length; ++j)
160                 result.push(uiSourceCodeList[j]);
161         }
162         return result;
163     },
164
165     /**
166      * @param {WebInspector.Script} script
167      */
168     addScript: function(script)
169     {
170         this._liveLocationsForScriptId[script.scriptId] = [];
171
172         var mapping = this._mappingForScript(script);
173         this._mappingForScriptId[script.scriptId] = mapping;
174         mapping.addScript(script);
175     },
176
177     /**
178      * @param {WebInspector.Script} script
179      * @return {WebInspector.ScriptMapping}
180      */
181     _mappingForScript: function(script)
182     {
183         if (WebInspector.experimentsSettings.snippetsSupport.isEnabled()) {
184             if (WebInspector.snippetsModel.snippetIdForSourceURL(script.sourceURL))
185                 return this._snippetsMapping;
186         }
187
188         if (WebInspector.settings.sourceMapsEnabled.get() && script.sourceMapURL) {
189             if (this._compilerMapping.loadSourceMapForScript(script))
190                 return this._compilerMapping;
191         }
192
193         return this._resourceMapping;
194     },
195
196     /**
197      * @param {WebInspector.Event} event
198      */
199     _handleUISourceCodeListChanged: function(event)
200     {
201         var scriptMapping = /** @type {WebInspector.ScriptMapping} */ event.target;
202         var removedItems = /** @type {Array.<WebInspector.UISourceCode>} */ event.data["removedItems"];
203         var addedItems = /** @type {Array.<WebInspector.UISourceCode>} */ event.data["addedItems"];
204         var scriptIds = /** @type {Array.<number>} */ event.data["scriptIds"];
205
206         for (var i = 0; i < removedItems.length; ++i)
207             this._mappingForUISourceCode.remove(removedItems[i]);
208         for (var i = 0; i < addedItems.length; ++i)
209             this._mappingForUISourceCode.put(addedItems[i], scriptMapping);
210         this._updateLiveLocations(scriptIds);
211         this.dispatchEventToListeners(WebInspector.MainScriptMapping.Events.UISourceCodeListChanged, event.data);
212     },
213
214     /**
215      * @param {boolean} formatSource
216      */
217     setFormatSource: function(formatSource)
218     {
219         this._resourceMapping.setFormatSource(formatSource);
220     },
221
222     /**
223      * @param {DebuggerAgent.Location} rawLocation
224      */
225     forceUpdateSourceMapping: function(rawLocation)
226     {
227         if (this._mappingForScriptId[rawLocation.scriptId] === this._resourceMapping)
228             this._resourceMapping.forceUpdateSourceMapping(rawLocation);
229     },
230
231     reset: function()
232     {
233         for (var i = 0; i < this._mappings.length; ++i)
234             this._mappings[i].reset();
235         this._mappingForScriptId = {};
236         this._mappingForUISourceCode = new Map();
237         this._liveLocationsForScriptId = {};
238     }
239 }
240
241 WebInspector.MainScriptMapping.prototype.__proto__ = WebInspector.Object.prototype;
242
243 /**
244  * @constructor
245  * @param {WebInspector.MainScriptMapping} scriptMapping
246  * @param {DebuggerAgent.Location} rawLocation
247  * @param {function(WebInspector.UILocation)} updateDelegate
248  */
249 WebInspector.LiveLocation = function(scriptMapping, rawLocation, updateDelegate)
250 {
251     this._scriptMapping = scriptMapping;
252     this._rawLocation = rawLocation;
253     this._updateDelegate = updateDelegate;
254 }
255
256 WebInspector.LiveLocation.prototype = {
257     init: function()
258     {
259         this._scriptMapping._registerLiveLocation(this._rawLocation.scriptId, this);
260     },
261
262     dispose: function()
263     {
264         this._scriptMapping._unregisterLiveLocation(this._rawLocation.scriptId, this);
265     },
266
267     _update: function()
268     {
269         var uiLocation = this._scriptMapping.rawLocationToUILocation(this._rawLocation);
270         if (uiLocation)
271             this._updateDelegate(uiLocation);
272     }
273 }