Upstream version 7.36.151.0
[platform/framework/web/crosswalk.git] / src / v8 / tools / SourceMap.js
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 // This is a copy from blink dev tools, see:
29 // http://src.chromium.org/viewvc/blink/trunk/Source/devtools/front_end/SourceMap.js
30 // revision: 153407
31
32 // Added to make the file work without dev tools
33 WebInspector = {};
34 WebInspector.ParsedURL = {};
35 WebInspector.ParsedURL.completeURL = function(){};
36 // start of original file content
37
38 /*
39  * Copyright (C) 2012 Google Inc. All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions are
43  * met:
44  *
45  *     * Redistributions of source code must retain the above copyright
46  * notice, this list of conditions and the following disclaimer.
47  *     * Redistributions in binary form must reproduce the above
48  * copyright notice, this list of conditions and the following disclaimer
49  * in the documentation and/or other materials provided with the
50  * distribution.
51  *     * Neither the name of Google Inc. nor the names of its
52  * contributors may be used to endorse or promote products derived from
53  * this software without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
56  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
57  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
58  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
59  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
60  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
61  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
62  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
63  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
64  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
65  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66  */
67
68 /**
69  * Implements Source Map V3 model. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
70  * for format description.
71  * @constructor
72  * @param {string} sourceMappingURL
73  * @param {SourceMapV3} payload
74  */
75 WebInspector.SourceMap = function(sourceMappingURL, payload)
76 {
77     if (!WebInspector.SourceMap.prototype._base64Map) {
78         const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
79         WebInspector.SourceMap.prototype._base64Map = {};
80         for (var i = 0; i < base64Digits.length; ++i)
81             WebInspector.SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i;
82     }
83
84     this._sourceMappingURL = sourceMappingURL;
85     this._reverseMappingsBySourceURL = {};
86     this._mappings = [];
87     this._sources = {};
88     this._sourceContentByURL = {};
89     this._parseMappingPayload(payload);
90 }
91
92 /**
93  * @param {string} sourceMapURL
94  * @param {string} compiledURL
95  * @param {function(WebInspector.SourceMap)} callback
96  */
97 WebInspector.SourceMap.load = function(sourceMapURL, compiledURL, callback)
98 {
99     NetworkAgent.loadResourceForFrontend(WebInspector.resourceTreeModel.mainFrame.id, sourceMapURL, undefined, contentLoaded.bind(this));
100
101     /**
102      * @param {?Protocol.Error} error
103      * @param {number} statusCode
104      * @param {NetworkAgent.Headers} headers
105      * @param {string} content
106      */
107     function contentLoaded(error, statusCode, headers, content)
108     {
109         if (error || !content || statusCode >= 400) {
110             console.error("Could not load content for " + sourceMapURL + " : " + (error || ("HTTP status code: " + statusCode)));
111             callback(null);
112             return;
113         }
114
115         if (content.slice(0, 3) === ")]}")
116             content = content.substring(content.indexOf('\n'));
117         try {
118             var payload = /** @type {SourceMapV3} */ (JSON.parse(content));
119             var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourceMapURL;
120             callback(new WebInspector.SourceMap(baseURL, payload));
121         } catch(e) {
122             console.error(e.message);
123             callback(null);
124         }
125     }
126 }
127
128 WebInspector.SourceMap.prototype = {
129     /**
130      * @return {Array.<string>}
131      */
132     sources: function()
133     {
134         return Object.keys(this._sources);
135     },
136
137     /**
138      * @param {string} sourceURL
139      * @return {string|undefined}
140      */
141     sourceContent: function(sourceURL)
142     {
143         return this._sourceContentByURL[sourceURL];
144     },
145
146     /**
147      * @param {string} sourceURL
148      * @param {WebInspector.ResourceType} contentType
149      * @return {WebInspector.ContentProvider}
150      */
151     sourceContentProvider: function(sourceURL, contentType)
152     {
153         var lastIndexOfDot = sourceURL.lastIndexOf(".");
154         var extension = lastIndexOfDot !== -1 ? sourceURL.substr(lastIndexOfDot + 1) : "";
155         var mimeType = WebInspector.ResourceType.mimeTypesForExtensions[extension.toLowerCase()];
156         var sourceContent = this.sourceContent(sourceURL);
157         if (sourceContent)
158             return new WebInspector.StaticContentProvider(contentType, sourceContent, mimeType);
159         return new WebInspector.CompilerSourceMappingContentProvider(sourceURL, contentType, mimeType);
160     },
161
162     /**
163      * @param {SourceMapV3} mappingPayload
164      */
165     _parseMappingPayload: function(mappingPayload)
166     {
167         if (mappingPayload.sections)
168             this._parseSections(mappingPayload.sections);
169         else
170             this._parseMap(mappingPayload, 0, 0);
171     },
172
173     /**
174      * @param {Array.<SourceMapV3.Section>} sections
175      */
176     _parseSections: function(sections)
177     {
178         for (var i = 0; i < sections.length; ++i) {
179             var section = sections[i];
180             this._parseMap(section.map, section.offset.line, section.offset.column);
181         }
182     },
183
184     /**
185      * @param {number} lineNumber in compiled resource
186      * @param {number} columnNumber in compiled resource
187      * @return {?Array}
188      */
189     findEntry: function(lineNumber, columnNumber)
190     {
191         var first = 0;
192         var count = this._mappings.length;
193         while (count > 1) {
194           var step = count >> 1;
195           var middle = first + step;
196           var mapping = this._mappings[middle];
197           if (lineNumber < mapping[0] || (lineNumber === mapping[0] && columnNumber < mapping[1]))
198               count = step;
199           else {
200               first = middle;
201               count -= step;
202           }
203         }
204         var entry = this._mappings[first];
205         if (!first && entry && (lineNumber < entry[0] || (lineNumber === entry[0] && columnNumber < entry[1])))
206             return null;
207         return entry;
208     },
209
210     /**
211      * @param {string} sourceURL of the originating resource
212      * @param {number} lineNumber in the originating resource
213      * @return {Array}
214      */
215     findEntryReversed: function(sourceURL, lineNumber)
216     {
217         var mappings = this._reverseMappingsBySourceURL[sourceURL];
218         for ( ; lineNumber < mappings.length; ++lineNumber) {
219             var mapping = mappings[lineNumber];
220             if (mapping)
221                 return mapping;
222         }
223         return this._mappings[0];
224     },
225
226     /**
227      * @override
228      */
229     _parseMap: function(map, lineNumber, columnNumber)
230     {
231         var sourceIndex = 0;
232         var sourceLineNumber = 0;
233         var sourceColumnNumber = 0;
234         var nameIndex = 0;
235
236         var sources = [];
237         var originalToCanonicalURLMap = {};
238         for (var i = 0; i < map.sources.length; ++i) {
239             var originalSourceURL = map.sources[i];
240             var sourceRoot = map.sourceRoot || "";
241             if (sourceRoot && !sourceRoot.endsWith("/"))
242                 sourceRoot += "/";
243             var href = sourceRoot + originalSourceURL;
244             var url = WebInspector.ParsedURL.completeURL(this._sourceMappingURL, href) || href;
245             originalToCanonicalURLMap[originalSourceURL] = url;
246             sources.push(url);
247             this._sources[url] = true;
248
249             if (map.sourcesContent && map.sourcesContent[i])
250                 this._sourceContentByURL[url] = map.sourcesContent[i];
251         }
252
253         var stringCharIterator = new WebInspector.SourceMap.StringCharIterator(map.mappings);
254         var sourceURL = sources[sourceIndex];
255
256         while (true) {
257             if (stringCharIterator.peek() === ",")
258                 stringCharIterator.next();
259             else {
260                 while (stringCharIterator.peek() === ";") {
261                     lineNumber += 1;
262                     columnNumber = 0;
263                     stringCharIterator.next();
264                 }
265                 if (!stringCharIterator.hasNext())
266                     break;
267             }
268
269             columnNumber += this._decodeVLQ(stringCharIterator);
270             if (this._isSeparator(stringCharIterator.peek())) {
271                 this._mappings.push([lineNumber, columnNumber]);
272                 continue;
273             }
274
275             var sourceIndexDelta = this._decodeVLQ(stringCharIterator);
276             if (sourceIndexDelta) {
277                 sourceIndex += sourceIndexDelta;
278                 sourceURL = sources[sourceIndex];
279             }
280             sourceLineNumber += this._decodeVLQ(stringCharIterator);
281             sourceColumnNumber += this._decodeVLQ(stringCharIterator);
282             if (!this._isSeparator(stringCharIterator.peek()))
283                 nameIndex += this._decodeVLQ(stringCharIterator);
284
285             this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
286         }
287
288         for (var i = 0; i < this._mappings.length; ++i) {
289             var mapping = this._mappings[i];
290             var url = mapping[2];
291             if (!url)
292                 continue;
293             if (!this._reverseMappingsBySourceURL[url])
294                 this._reverseMappingsBySourceURL[url] = [];
295             var reverseMappings = this._reverseMappingsBySourceURL[url];
296             var sourceLine = mapping[3];
297             if (!reverseMappings[sourceLine])
298                 reverseMappings[sourceLine] = [mapping[0], mapping[1]];
299         }
300     },
301
302     /**
303      * @param {string} char
304      * @return {boolean}
305      */
306     _isSeparator: function(char)
307     {
308         return char === "," || char === ";";
309     },
310
311     /**
312      * @param {WebInspector.SourceMap.StringCharIterator} stringCharIterator
313      * @return {number}
314      */
315     _decodeVLQ: function(stringCharIterator)
316     {
317         // Read unsigned value.
318         var result = 0;
319         var shift = 0;
320         do {
321             var digit = this._base64Map[stringCharIterator.next()];
322             result += (digit & this._VLQ_BASE_MASK) << shift;
323             shift += this._VLQ_BASE_SHIFT;
324         } while (digit & this._VLQ_CONTINUATION_MASK);
325
326         // Fix the sign.
327         var negative = result & 1;
328         result >>= 1;
329         return negative ? -result : result;
330     },
331
332     _VLQ_BASE_SHIFT: 5,
333     _VLQ_BASE_MASK: (1 << 5) - 1,
334     _VLQ_CONTINUATION_MASK: 1 << 5
335 }
336
337 /**
338  * @constructor
339  * @param {string} string
340  */
341 WebInspector.SourceMap.StringCharIterator = function(string)
342 {
343     this._string = string;
344     this._position = 0;
345 }
346
347 WebInspector.SourceMap.StringCharIterator.prototype = {
348     /**
349      * @return {string}
350      */
351     next: function()
352     {
353         return this._string.charAt(this._position++);
354     },
355
356     /**
357      * @return {string}
358      */
359     peek: function()
360     {
361         return this._string.charAt(this._position);
362     },
363
364     /**
365      * @return {boolean}
366      */
367     hasNext: function()
368     {
369         return this._position < this._string.length;
370     }
371 }