Unreviewed, rolling out r103405.
[WebKit-https.git] / Source / WebCore / inspector / front-end / RawSourceCode.js
1 /*
2  * Copyright (C) 2011 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 // RawSourceCode represents JavaScript resource or HTML resource with inlined scripts
32 // as it came from network.
33
34 /**
35  * @constructor
36  * @extends {WebInspector.Object}
37  * @param {string} id
38  * @param {WebInspector.Script} script
39  * @param {WebInspector.Resource} resource
40  * @param {WebInspector.ScriptFormatter} formatter
41  * @param {boolean} formatted
42  */
43 WebInspector.RawSourceCode = function(id, script, resource, formatter, formatted)
44 {
45     this.id = id;
46     this.url = script.sourceURL;
47     this.isContentScript = script.isContentScript;
48     this._scripts = [script];
49     this._formatter = formatter;
50     this._formatted = formatted;
51     this._resource = resource;
52     this.messages = [];
53
54     this._useTemporaryContent = this._resource && !this._resource.finished;
55     this._hasNewScripts = true;
56     if (!this._useTemporaryContent)
57         this._updateSourceMapping();
58     else if (this._resource)
59         this._resource.addEventListener("finished", this._resourceFinished.bind(this));
60 }
61
62 WebInspector.RawSourceCode.Events = {
63     SourceMappingUpdated: "source-mapping-updated"
64 }
65
66 WebInspector.RawSourceCode.prototype = {
67     /**
68      * @param {WebInspector.Script} script
69      */
70     addScript: function(script)
71     {
72         this._scripts.push(script);
73         this._hasNewScripts = true;
74     },
75
76     /**
77      * @return {WebInspector.RawSourceCode.SourceMapping}
78      */
79     get sourceMapping()
80     {
81         return this._sourceMapping;
82     },
83
84     /**
85      * @param {boolean} formatted
86      */
87     setFormatted: function(formatted)
88     {
89         if (this._formatted === formatted)
90             return;
91         this._formatted = formatted;
92         this._updateSourceMapping();
93     },
94
95     /**
96      * @param {WebInspector.CompilerSourceMapping} compilerSourceMapping
97      */
98     setCompilerSourceMapping: function(compilerSourceMapping)
99     {
100         if (compilerSourceMapping)
101             this._useTemporaryContent = false;
102         this._compilerSourceMapping = compilerSourceMapping;
103         this._updateSourceMapping();
104     },
105
106     _resourceFinished: function()
107     {
108         if (this._compilerSourceMapping)
109             return;
110         this._useTemporaryContent = false;
111         this._updateSourceMapping();
112     },
113
114     /**
115      * @param {number} lineNumber
116      * @param {number} columnNumber
117      * @return {WebInspector.Script}
118      */
119     _scriptForRawLocation: function(lineNumber, columnNumber)
120     {
121         var closestScript = this._scripts[0];
122         for (var i = 1; i < this._scripts.length; ++i) {
123             var script = this._scripts[i];
124             if (script.lineOffset > lineNumber || (script.lineOffset === lineNumber && script.columnOffset > columnNumber))
125                 continue;
126             if (script.lineOffset > closestScript.lineOffset ||
127                 (script.lineOffset === closestScript.lineOffset && script.columnOffset > closestScript.columnOffset))
128                 closestScript = script;
129         }
130         return closestScript;
131     },
132
133     /**
134      * @param {WebInspector.Script} script
135      */
136     forceUpdateSourceMapping: function(script)
137     {
138         if (!this._useTemporaryContent || !this._hasNewScripts)
139             return;
140         this._hasNewScripts = false;
141         this._updateSourceMapping();
142     },
143
144     _updateSourceMapping: function()
145     {
146         if (this._updatingSourceMapping) {
147             this._updateNeeded = true;
148             return;
149         }
150         this._updatingSourceMapping = true;
151         this._updateNeeded = false;
152
153         this._createSourceMapping(didCreateSourceMapping.bind(this));
154
155         /**
156          * @this {WebInspector.RawSourceCode}
157          * @param {WebInspector.RawSourceCode.SourceMapping} sourceMapping
158          */
159         function didCreateSourceMapping(sourceMapping)
160         {
161             this._updatingSourceMapping = false;
162             if (!sourceMapping)
163                 return;
164             if (!this._updateNeeded)
165                 this._saveSourceMapping(sourceMapping);
166             else
167                 this._updateSourceMapping();
168         }
169     },
170
171     _createContentProvider: function()
172     {
173         if (this._resource && this._resource.finished)
174             return new WebInspector.ResourceContentProvider(this._resource);
175         if (this._scripts.length === 1 && !this._scripts[0].lineOffset && !this._scripts[0].columnOffset)
176             return new WebInspector.ScriptContentProvider(this._scripts[0]);
177         return new WebInspector.ConcatenatedScriptsContentProvider(this._scripts);
178     },
179
180     /**
181      * @param {function(WebInspector.RawSourceCode.SourceMapping)} callback
182      */
183     _createSourceMapping: function(callback)
184     {
185         if (this._compilerSourceMapping) {
186             var success = this._compilerSourceMapping.load();
187             if (!success) {
188                 delete this._compilerSourceMapping;
189                 callback(null);
190                 return;
191             }
192             var uiSourceCodeList = [];
193             var sourceURLs = this._compilerSourceMapping.sources();
194             for (var i = 0; i < sourceURLs.length; ++i) {
195                 var sourceURL = sourceURLs[i];
196                 var contentProvider = new WebInspector.CompilerSourceMappingContentProvider(sourceURL, this._compilerSourceMapping);
197                 var uiSourceCode = new WebInspector.UISourceCode(sourceURL, sourceURL, this.isContentScript, this, contentProvider);
198                 uiSourceCodeList.push(uiSourceCode);
199             }
200             var sourceMapping = new WebInspector.RawSourceCode.CompilerSourceMapping(this, uiSourceCodeList, this._compilerSourceMapping);
201             callback(sourceMapping);
202             return;
203         }
204
205         var originalContentProvider = this._createContentProvider();
206         if (!this._formatted) {
207             var uiSourceCode = new WebInspector.UISourceCode(this.url, this.url, this.isContentScript, this, originalContentProvider);
208             var sourceMapping = new WebInspector.RawSourceCode.PlainSourceMapping(this, uiSourceCode);
209             callback(sourceMapping);
210             return;
211         }
212
213         /**
214          * @this {WebInspector.RawSourceCode}
215          * @param {string} mimeType
216          * @param {string} content
217          */
218         function didRequestContent(mimeType, content)
219         {
220             /**
221              * @this {WebInspector.RawSourceCode}
222              * @param {string} formattedContent
223              * @param {WebInspector.FormattedSourceMapping} mapping
224              */
225             function didFormatContent(formattedContent, mapping)
226             {
227                 var contentProvider = new WebInspector.StaticContentProvider(mimeType, formattedContent)
228                 var uiSourceCode = new WebInspector.UISourceCode("deobfuscated:" + this.url, this.url, this.isContentScript, this, contentProvider);
229                 var sourceMapping = new WebInspector.RawSourceCode.FormattedSourceMapping(this, uiSourceCode, mapping);
230                 callback(sourceMapping);
231             }
232             this._formatter.formatContent(mimeType, content, didFormatContent.bind(this));
233         }
234         originalContentProvider.requestContent(didRequestContent.bind(this));
235     },
236
237     /**
238      * @param {WebInspector.RawSourceCode.SourceMapping} sourceMapping
239      */
240     _saveSourceMapping: function(sourceMapping)
241     {
242         var oldSourceMapping;
243         if (this._sourceMapping)
244             oldSourceMapping = this._sourceMapping;
245         this._sourceMapping = sourceMapping;
246         this.dispatchEventToListeners(WebInspector.RawSourceCode.Events.SourceMappingUpdated, { oldSourceMapping: oldSourceMapping });
247     }
248 }
249
250 WebInspector.RawSourceCode.prototype.__proto__ = WebInspector.Object.prototype;
251
252 /**
253  * @interface
254  */
255 WebInspector.RawSourceCode.SourceMapping = function()
256 {
257 }
258
259 WebInspector.RawSourceCode.SourceMapping.prototype = {
260     /**
261      * @param {DebuggerAgent.Location} rawLocation
262      * @return {WebInspector.UILocation}
263      */
264     rawLocationToUILocation: function(rawLocation) { },
265
266     /**
267      * @param {WebInspector.UISourceCode} uiSourceCode
268      * @param {number} lineNumber
269      * @param {number} columnNumber
270      * @return {DebuggerAgent.Location}
271      */
272     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber) { }
273 }
274
275 /**
276  * @constructor
277  * @implements {WebInspector.RawSourceCode.SourceMapping}
278  * @param {WebInspector.RawSourceCode} rawSourceCode
279  * @param {WebInspector.UISourceCode} uiSourceCode
280  */
281 WebInspector.RawSourceCode.PlainSourceMapping = function(rawSourceCode, uiSourceCode)
282 {
283     this._rawSourceCode = rawSourceCode;
284     this._uiSourceCodeList = [uiSourceCode];
285 }
286
287 WebInspector.RawSourceCode.PlainSourceMapping.prototype = {
288     /**
289      * @param {DebuggerAgent.Location} rawLocation
290      * @return {WebInspector.UILocation}
291      */
292     rawLocationToUILocation: function(rawLocation)
293     {
294         return new WebInspector.UILocation(this._uiSourceCodeList[0], rawLocation.lineNumber, rawLocation.columnNumber || 0);
295     },
296
297     /**
298      * @param {WebInspector.UISourceCode} uiSourceCode
299      * @param {number} lineNumber
300      * @param {number} columnNumber
301      * @return {DebuggerAgent.Location}
302      */
303     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
304     {
305         console.assert(uiSourceCode === this._uiSourceCodeList[0]);
306         var rawLocation = { lineNumber: lineNumber, columnNumber: columnNumber };
307         rawLocation.scriptId = this._rawSourceCode._scriptForRawLocation(rawLocation.lineNumber, rawLocation.columnNumber).scriptId;
308         return /** @type {DebuggerAgent.Location} */ rawLocation;
309     },
310
311     /**
312      * @return {Array.<WebInspector.UISourceCode>}
313      */
314     uiSourceCodeList: function()
315     {
316         return this._uiSourceCodeList;
317     }
318 }
319
320 /**
321  * @constructor
322  * @implements {WebInspector.RawSourceCode.SourceMapping}
323  * @param {WebInspector.RawSourceCode} rawSourceCode
324  * @param {WebInspector.UISourceCode} uiSourceCode
325  * @param {WebInspector.FormattedSourceMapping} mapping
326  */
327 WebInspector.RawSourceCode.FormattedSourceMapping = function(rawSourceCode, uiSourceCode, mapping)
328 {
329     this._rawSourceCode = rawSourceCode;
330     this._uiSourceCodeList = [uiSourceCode];
331     this._mapping = mapping;
332 }
333
334 WebInspector.RawSourceCode.FormattedSourceMapping.prototype = {
335     /**
336      * @param {DebuggerAgent.Location} rawLocation
337      */
338     rawLocationToUILocation: function(rawLocation)
339     {
340         var location = this._mapping.originalToFormatted(rawLocation);
341         return new WebInspector.UILocation(this._uiSourceCodeList[0], location.lineNumber, location.columnNumber || 0);
342     },
343
344     /**
345      * @param {WebInspector.UISourceCode} uiSourceCode
346      * @param {number} lineNumber
347      * @param {number} columnNumber
348      * @return {DebuggerAgent.Location}
349      */
350     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
351     {
352         console.assert(uiSourceCode === this._uiSourceCodeList[0]);
353         var rawLocation = this._mapping.formattedToOriginal({ lineNumber: lineNumber, columnNumber: columnNumber });
354         rawLocation.scriptId = this._rawSourceCode._scriptForRawLocation(rawLocation.lineNumber, rawLocation.columnNumber).scriptId;
355         return rawLocation;
356     },
357
358     /**
359      * @return {Array.<WebInspector.UISourceCode>}
360      */
361     uiSourceCodeList: function()
362     {
363         return this._uiSourceCodeList;
364     }
365 }
366
367 /**
368  * @constructor
369  * @implements {WebInspector.RawSourceCode.SourceMapping}
370  * @param {WebInspector.RawSourceCode} rawSourceCode
371  * @param {Array.<WebInspector.UISourceCode>} uiSourceCodeList
372  * @param {WebInspector.CompilerSourceMapping} mapping
373  */
374 WebInspector.RawSourceCode.CompilerSourceMapping = function(rawSourceCode, uiSourceCodeList, mapping)
375 {
376     this._rawSourceCode = rawSourceCode;
377     this._uiSourceCodeList = uiSourceCodeList;
378     this._mapping = mapping;
379     this._uiSourceCodeByURL = {};
380     for (var i = 0; i < uiSourceCodeList.length; ++i)
381         this._uiSourceCodeByURL[uiSourceCodeList[i].url] = uiSourceCodeList[i];
382 }
383
384 WebInspector.RawSourceCode.CompilerSourceMapping.prototype = {
385     /**
386      * @param {DebuggerAgent.Location} rawLocation
387      */
388     rawLocationToUILocation: function(rawLocation)
389     {
390         var location = this._mapping.compiledLocationToSourceLocation(rawLocation.lineNumber, rawLocation.columnNumber || 0);
391         var uiSourceCode = this._uiSourceCodeByURL[location.sourceURL];
392         return new WebInspector.UILocation(uiSourceCode, location.lineNumber, location.columnNumber);
393     },
394
395     /**
396      * @param {WebInspector.UISourceCode} uiSourceCode
397      * @param {number} lineNumber
398      * @param {number} columnNumber
399      * @return {DebuggerAgent.Location}
400      */
401     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
402     {
403         var rawLocation = this._mapping.sourceLocationToCompiledLocation(uiSourceCode.url, lineNumber);
404         rawLocation.scriptId = this._rawSourceCode._scriptForRawLocation(rawLocation.lineNumber, rawLocation.columnNumber).scriptId;
405         return /** @type {DebuggerAgent.Location} */ rawLocation;
406     },
407
408     /**
409      * @return {Array.<WebInspector.UISourceCode>}
410      */
411     uiSourceCodeList: function()
412     {
413         return this._uiSourceCodeList;
414     }
415 }
416
417 /**
418  * @constructor
419  * @param {WebInspector.UISourceCode} uiSourceCode
420  * @param {number} lineNumber
421  * @param {number} columnNumber
422  */
423 WebInspector.UILocation = function(uiSourceCode, lineNumber, columnNumber)
424 {
425     this.uiSourceCode = uiSourceCode;
426     this.lineNumber = lineNumber;
427     this.columnNumber = columnNumber;
428 }