2
* Copyright (C) 2012 Google Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are
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
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.
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.
33
* @implements {WebInspector.ScriptSourceMapping}
34
* @param {WebInspector.Workspace} workspace
35
* @param {WebInspector.NetworkWorkspaceProvider} networkWorkspaceProvider
37
WebInspector.CompilerScriptMapping = function(workspace, networkWorkspaceProvider)
39
this._workspace = workspace;
40
this._networkWorkspaceProvider = networkWorkspaceProvider;
41
/** @type {Object.<string, WebInspector.SourceMapParser>} */
42
this._sourceMapForSourceMapURL = {};
43
/** @type {Object.<string, WebInspector.SourceMapParser>} */
44
this._sourceMapForScriptId = {};
45
this._scriptForSourceMap = new Map();
46
/** @type {Object.<string, WebInspector.SourceMapParser>} */
47
this._sourceMapForURL = {};
48
/** @type {Object.<string, WebInspector.UISourceCode>} */
49
this._originalUISourceCodeForScriptId = {};
50
this._scriptForOriginalUISourceCode = new Map();
51
this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._reset, this);
54
WebInspector.CompilerScriptMapping.prototype = {
56
* @param {WebInspector.RawLocation} rawLocation
57
* @return {WebInspector.UILocation}
59
rawLocationToUILocation: function(rawLocation)
61
var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
62
var sourceMap = this._sourceMapForScriptId[debuggerModelLocation.scriptId];
63
var lineNumber = debuggerModelLocation.lineNumber;
64
var columnNumber = debuggerModelLocation.columnNumber || 0;
65
var entry = sourceMap.findEntry(lineNumber, columnNumber);
66
if (entry.length === 2) {
67
var temporaryUISourceCode = this._originalUISourceCodeForScriptId[debuggerModelLocation.scriptId];
68
return new WebInspector.UILocation(temporaryUISourceCode, lineNumber, columnNumber);
70
var uiSourceCode = this._workspace.uiSourceCodeForURL(entry[2]);
71
return new WebInspector.UILocation(uiSourceCode, entry[3], entry[4]);
75
* @param {WebInspector.UISourceCode} uiSourceCode
76
* @param {number} lineNumber
77
* @param {number} columnNumber
78
* @return {WebInspector.DebuggerModel.Location}
80
uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
82
var script = this._scriptForOriginalUISourceCode.get(uiSourceCode);
84
return WebInspector.debuggerModel.createRawLocation(script, lineNumber, columnNumber);
85
var sourceMap = this._sourceMapForURL[uiSourceCode.url];
86
var entry = sourceMap.findEntryReversed(uiSourceCode.url, lineNumber);
87
return WebInspector.debuggerModel.createRawLocation(this._scriptForSourceMap.get(sourceMap), entry[0], entry[1]);
91
* @param {WebInspector.Script} script
93
addScript: function(script)
95
// FIXME: We should only create temporary uiSourceCodes on demand and should set this as a mapping to
96
// relevant uiSourceCodes added by NetworkUISourceCodeProvider.
97
var originalUISourceCode = this._workspace.addTemporaryUISourceCode(script.sourceURL, script, false);
98
originalUISourceCode.setSourceMapping(this);
99
this._originalUISourceCodeForScriptId[script.scriptId] = originalUISourceCode;
100
this._scriptForOriginalUISourceCode.put(originalUISourceCode, script);
102
var sourceMap = this.loadSourceMapForScript(script);
104
if (this._scriptForSourceMap.get(sourceMap)) {
105
this._sourceMapForScriptId[script.scriptId] = sourceMap;
106
script.setSourceMapping(this);
110
var sourceURLs = sourceMap.sources();
111
for (var i = 0; i < sourceURLs.length; ++i) {
112
var sourceURL = sourceURLs[i];
113
if (this._workspace.uiSourceCodeForURL(sourceURL))
115
this._sourceMapForURL[sourceURL] = sourceMap;
116
var sourceContent = sourceMap.sourceContent(sourceURL);
119
contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Script, sourceContent);
121
contentProvider = new WebInspector.CompilerSourceMappingContentProvider(sourceURL);
122
this._networkWorkspaceProvider.addFile(sourceURL, contentProvider, true);
123
var uiSourceCode = this._workspace.uiSourceCodeForURL(sourceURL);
124
uiSourceCode.setSourceMapping(this);
125
uiSourceCode.isContentScript = script.isContentScript;
128
this._sourceMapForScriptId[script.scriptId] = sourceMap;
129
this._scriptForSourceMap.put(sourceMap, script);
130
script.setSourceMapping(this);
134
* @param {WebInspector.Script} script
135
* @return {WebInspector.SourceMapParser}
137
loadSourceMapForScript: function(script)
139
var sourceMapURL = WebInspector.SourceMapParser.prototype._canonicalizeURL(script.sourceMapURL, script.sourceURL);
140
var sourceMap = this._sourceMapForSourceMapURL[sourceMapURL];
145
// FIXME: make sendRequest async.
146
var response = InspectorFrontendHost.loadResourceSynchronously(sourceMapURL);
147
if (response.slice(0, 3) === ")]}")
148
response = response.substring(response.indexOf('\n'));
149
var payload = /** @type {WebInspector.SourceMapPayload} */ (JSON.parse(response));
150
sourceMap = new WebInspector.SourceMapParser(sourceMapURL, payload);
152
console.error(e.message);
155
this._sourceMapForSourceMapURL[sourceMapURL] = sourceMap;
161
this._sourceMapForSourceMapURL = {};
162
this._sourceMapForScriptId = {};
163
this._scriptForSourceMap = new Map();
164
this._sourceMapForURL = {};
165
this._originalUISourceCodeForScriptId = {};
166
this._scriptForOriginalUISourceCode = new Map();
173
WebInspector.SourceMapPayload = function()
177
this.sourceRoot = "";
182
* Implements Source Map V3 consumer. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
183
* for format description.
185
* @param {string} sourceMappingURL
186
* @param {WebInspector.SourceMapPayload} payload
188
WebInspector.SourceMapParser = function(sourceMappingURL, payload)
190
if (!WebInspector.SourceMapParser.prototype._base64Map) {
191
const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
192
WebInspector.SourceMapParser.prototype._base64Map = {};
193
for (var i = 0; i < base64Digits.length; ++i)
194
WebInspector.SourceMapParser.prototype._base64Map[base64Digits.charAt(i)] = i;
197
this._sourceMappingURL = sourceMappingURL;
199
this._reverseMappingsBySourceURL = {};
200
this._sourceContentByURL = {};
201
this._parseMappingPayload(payload);
204
WebInspector.SourceMapParser.prototype = {
206
* @return {Array.<string>}
211
for (var sourceURL in this._reverseMappingsBySourceURL)
212
sources.push(sourceURL);
216
sourceContent: function(sourceURL)
218
return this._sourceContentByURL[sourceURL];
221
findEntry: function(lineNumber, columnNumber)
224
var count = this._mappings.length;
226
var step = count >> 1;
227
var middle = first + step;
228
var mapping = this._mappings[middle];
229
if (lineNumber < mapping[0] || (lineNumber == mapping[0] && columnNumber < mapping[1]))
236
return this._mappings[first];
239
findEntryReversed: function(sourceURL, lineNumber)
241
var mappings = this._reverseMappingsBySourceURL[sourceURL];
242
for ( ; lineNumber < mappings.length; ++lineNumber) {
243
var mapping = mappings[lineNumber];
247
return this._mappings[0];
250
_parseMappingPayload: function(mappingPayload)
252
if (mappingPayload.sections)
253
this._parseSections(mappingPayload.sections);
255
this._parseMap(mappingPayload, 0, 0);
259
* @param {Array.<SourceMapV3.Section>} sections
261
_parseSections: function(sections)
263
for (var i = 0; i < sections.length; ++i) {
264
var section = sections[i];
265
this._parseMap(section.map, section.offset.line, section.offset.column)
270
* @param {SourceMapV3} map
271
* @param {number} lineNumber
272
* @param {number} columnNumber
274
_parseMap: function(map, lineNumber, columnNumber)
277
var sourceLineNumber = 0;
278
var sourceColumnNumber = 0;
282
for (var i = 0; i < map.sources.length; ++i) {
283
var sourceURL = map.sources[i];
285
sourceURL = map.sourceRoot + "/" + sourceURL;
286
var url = this._canonicalizeURL(sourceURL, this._sourceMappingURL);
288
if (!this._reverseMappingsBySourceURL[url])
289
this._reverseMappingsBySourceURL[url] = [];
290
if (map.sourcesContent && map.sourcesContent[i])
291
this._sourceContentByURL[url] = map.sourcesContent[i];
294
var stringCharIterator = new WebInspector.SourceMapParser.StringCharIterator(map.mappings);
295
var sourceURL = sources[sourceIndex];
296
var reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
299
if (stringCharIterator.peek() === ",")
300
stringCharIterator.next();
302
while (stringCharIterator.peek() === ";") {
305
stringCharIterator.next();
307
if (!stringCharIterator.hasNext())
311
columnNumber += this._decodeVLQ(stringCharIterator);
312
if (this._isSeparator(stringCharIterator.peek())) {
313
this._mappings.push([lineNumber, columnNumber]);
317
var sourceIndexDelta = this._decodeVLQ(stringCharIterator);
318
if (sourceIndexDelta) {
319
sourceIndex += sourceIndexDelta;
320
sourceURL = sources[sourceIndex];
321
reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
323
sourceLineNumber += this._decodeVLQ(stringCharIterator);
324
sourceColumnNumber += this._decodeVLQ(stringCharIterator);
325
if (!this._isSeparator(stringCharIterator.peek()))
326
nameIndex += this._decodeVLQ(stringCharIterator);
328
this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
329
if (!reverseMappings[sourceLineNumber])
330
reverseMappings[sourceLineNumber] = [lineNumber, columnNumber];
334
_isSeparator: function(char)
336
return char === "," || char === ";";
339
_decodeVLQ: function(stringCharIterator)
341
// Read unsigned value.
345
var digit = this._base64Map[stringCharIterator.next()];
346
result += (digit & this._VLQ_BASE_MASK) << shift;
347
shift += this._VLQ_BASE_SHIFT;
348
} while (digit & this._VLQ_CONTINUATION_MASK);
351
var negative = result & 1;
353
return negative ? -result : result;
356
_canonicalizeURL: function(url, baseURL)
358
if (!url || !baseURL || url.asParsedURL() || url.substring(0, 5) === "data:")
361
var base = baseURL.asParsedURL();
365
var baseHost = base.scheme + "://" + base.host + (base.port ? ":" + base.port : "");
367
return baseHost + url;
368
return baseHost + base.folderPathComponents + "/" + url;
372
_VLQ_BASE_MASK: (1 << 5) - 1,
373
_VLQ_CONTINUATION_MASK: 1 << 5
379
WebInspector.SourceMapParser.StringCharIterator = function(string)
381
this._string = string;
385
WebInspector.SourceMapParser.StringCharIterator.prototype = {
388
return this._string.charAt(this._position++);
393
return this._string.charAt(this._position);
398
return this._position < this._string.length;