• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
33WebInspector = {};
34WebInspector.ParsedURL = {};
35WebInspector.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 */
75WebInspector.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 */
97WebInspector.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
128WebInspector.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 */
341WebInspector.SourceMap.StringCharIterator = function(string)
342{
343    this._string = string;
344    this._position = 0;
345}
346
347WebInspector.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}
372