~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/inspector/front-end/CompilerScriptMapping.js

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 * @implements {WebInspector.ScriptSourceMapping}
 
34
 * @param {WebInspector.Workspace} workspace
 
35
 * @param {WebInspector.NetworkWorkspaceProvider} networkWorkspaceProvider
 
36
 */
 
37
WebInspector.CompilerScriptMapping = function(workspace, networkWorkspaceProvider)
 
38
{
 
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);
 
52
}
 
53
 
 
54
WebInspector.CompilerScriptMapping.prototype = {
 
55
    /**
 
56
     * @param {WebInspector.RawLocation} rawLocation
 
57
     * @return {WebInspector.UILocation}
 
58
     */
 
59
    rawLocationToUILocation: function(rawLocation)
 
60
    {
 
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);
 
69
        }
 
70
        var uiSourceCode = this._workspace.uiSourceCodeForURL(entry[2]);
 
71
        return new WebInspector.UILocation(uiSourceCode, entry[3], entry[4]);
 
72
    },
 
73
 
 
74
    /**
 
75
     * @param {WebInspector.UISourceCode} uiSourceCode
 
76
     * @param {number} lineNumber
 
77
     * @param {number} columnNumber
 
78
     * @return {WebInspector.DebuggerModel.Location}
 
79
     */
 
80
    uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
 
81
    {
 
82
        var script = this._scriptForOriginalUISourceCode.get(uiSourceCode);
 
83
        if (script)
 
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]);
 
88
    },
 
89
 
 
90
    /**
 
91
     * @param {WebInspector.Script} script
 
92
     */
 
93
    addScript: function(script)
 
94
    {
 
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);
 
101
 
 
102
        var sourceMap = this.loadSourceMapForScript(script);
 
103
 
 
104
        if (this._scriptForSourceMap.get(sourceMap)) {
 
105
            this._sourceMapForScriptId[script.scriptId] = sourceMap;
 
106
            script.setSourceMapping(this);
 
107
            return;
 
108
        }
 
109
 
 
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))
 
114
                continue;
 
115
            this._sourceMapForURL[sourceURL] = sourceMap;
 
116
            var sourceContent = sourceMap.sourceContent(sourceURL);
 
117
            var contentProvider;
 
118
            if (sourceContent)
 
119
                contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Script, sourceContent);
 
120
            else
 
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;
 
126
        }
 
127
 
 
128
        this._sourceMapForScriptId[script.scriptId] = sourceMap;
 
129
        this._scriptForSourceMap.put(sourceMap, script);
 
130
        script.setSourceMapping(this);
 
131
    },
 
132
 
 
133
    /**
 
134
     * @param {WebInspector.Script} script
 
135
     * @return {WebInspector.SourceMapParser}
 
136
     */
 
137
    loadSourceMapForScript: function(script)
 
138
    {
 
139
        var sourceMapURL = WebInspector.SourceMapParser.prototype._canonicalizeURL(script.sourceMapURL, script.sourceURL);
 
140
        var sourceMap = this._sourceMapForSourceMapURL[sourceMapURL];
 
141
        if (sourceMap)
 
142
            return sourceMap;
 
143
 
 
144
        try {
 
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);
 
151
        } catch(e) {
 
152
            console.error(e.message);
 
153
            return null;
 
154
        }
 
155
        this._sourceMapForSourceMapURL[sourceMapURL] = sourceMap;
 
156
        return sourceMap;
 
157
    },
 
158
 
 
159
    _reset: function()
 
160
    {
 
161
        this._sourceMapForSourceMapURL = {};
 
162
        this._sourceMapForScriptId = {};
 
163
        this._scriptForSourceMap = new Map();
 
164
        this._sourceMapForURL = {};
 
165
        this._originalUISourceCodeForScriptId = {};
 
166
        this._scriptForOriginalUISourceCode = new Map();
 
167
    }
 
168
}
 
169
 
 
170
/**
 
171
 * @constructor
 
172
 */
 
173
WebInspector.SourceMapPayload = function()
 
174
{
 
175
    this.sections = [];
 
176
    this.mappings = "";
 
177
    this.sourceRoot = "";
 
178
    this.sources = [];
 
179
}
 
180
 
 
181
/**
 
182
 * Implements Source Map V3 consumer. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
 
183
 * for format description.
 
184
 * @constructor
 
185
 * @param {string} sourceMappingURL
 
186
 * @param {WebInspector.SourceMapPayload} payload
 
187
 */
 
188
WebInspector.SourceMapParser = function(sourceMappingURL, payload)
 
189
{
 
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;
 
195
    }
 
196
 
 
197
    this._sourceMappingURL = sourceMappingURL;
 
198
    this._mappings = [];
 
199
    this._reverseMappingsBySourceURL = {};
 
200
    this._sourceContentByURL = {};
 
201
    this._parseMappingPayload(payload);
 
202
}
 
203
 
 
204
WebInspector.SourceMapParser.prototype = {
 
205
    /**
 
206
     * @return {Array.<string>}
 
207
     */
 
208
    sources: function()
 
209
    {
 
210
        var sources = [];
 
211
        for (var sourceURL in this._reverseMappingsBySourceURL)
 
212
            sources.push(sourceURL);
 
213
        return sources;
 
214
    },
 
215
 
 
216
    sourceContent: function(sourceURL)
 
217
    {
 
218
        return this._sourceContentByURL[sourceURL];
 
219
    },
 
220
 
 
221
    findEntry: function(lineNumber, columnNumber)
 
222
    {
 
223
        var first = 0;
 
224
        var count = this._mappings.length;
 
225
        while (count > 1) {
 
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]))
 
230
              count = step;
 
231
          else {
 
232
              first = middle;
 
233
              count -= step;
 
234
          }
 
235
        }
 
236
        return this._mappings[first];
 
237
    },
 
238
 
 
239
    findEntryReversed: function(sourceURL, lineNumber)
 
240
    {
 
241
        var mappings = this._reverseMappingsBySourceURL[sourceURL];
 
242
        for ( ; lineNumber < mappings.length; ++lineNumber) {
 
243
            var mapping = mappings[lineNumber];
 
244
            if (mapping)
 
245
                return mapping;
 
246
        }
 
247
        return this._mappings[0];
 
248
    },
 
249
 
 
250
    _parseMappingPayload: function(mappingPayload)
 
251
    {
 
252
        if (mappingPayload.sections)
 
253
            this._parseSections(mappingPayload.sections);
 
254
        else
 
255
            this._parseMap(mappingPayload, 0, 0);
 
256
    },
 
257
 
 
258
    /**
 
259
     * @param {Array.<SourceMapV3.Section>} sections
 
260
     */
 
261
    _parseSections: function(sections)
 
262
    {
 
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)
 
266
        }
 
267
    },
 
268
 
 
269
    /**
 
270
     * @param {SourceMapV3} map
 
271
     * @param {number} lineNumber
 
272
     * @param {number} columnNumber
 
273
     */
 
274
    _parseMap: function(map, lineNumber, columnNumber)
 
275
    {
 
276
        var sourceIndex = 0;
 
277
        var sourceLineNumber = 0;
 
278
        var sourceColumnNumber = 0;
 
279
        var nameIndex = 0;
 
280
 
 
281
        var sources = [];
 
282
        for (var i = 0; i < map.sources.length; ++i) {
 
283
            var sourceURL = map.sources[i];
 
284
            if (map.sourceRoot)
 
285
                sourceURL = map.sourceRoot + "/" + sourceURL;
 
286
            var url = this._canonicalizeURL(sourceURL, this._sourceMappingURL);
 
287
            sources.push(url);
 
288
            if (!this._reverseMappingsBySourceURL[url])
 
289
                this._reverseMappingsBySourceURL[url] = [];
 
290
            if (map.sourcesContent && map.sourcesContent[i])
 
291
                this._sourceContentByURL[url] = map.sourcesContent[i];
 
292
        }
 
293
 
 
294
        var stringCharIterator = new WebInspector.SourceMapParser.StringCharIterator(map.mappings);
 
295
        var sourceURL = sources[sourceIndex];
 
296
        var reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
 
297
 
 
298
        while (true) {
 
299
            if (stringCharIterator.peek() === ",")
 
300
                stringCharIterator.next();
 
301
            else {
 
302
                while (stringCharIterator.peek() === ";") {
 
303
                    lineNumber += 1;
 
304
                    columnNumber = 0;
 
305
                    stringCharIterator.next();
 
306
                }
 
307
                if (!stringCharIterator.hasNext())
 
308
                    break;
 
309
            }
 
310
 
 
311
            columnNumber += this._decodeVLQ(stringCharIterator);
 
312
            if (this._isSeparator(stringCharIterator.peek())) {
 
313
                this._mappings.push([lineNumber, columnNumber]);
 
314
                continue;
 
315
            }
 
316
 
 
317
            var sourceIndexDelta = this._decodeVLQ(stringCharIterator);
 
318
            if (sourceIndexDelta) {
 
319
                sourceIndex += sourceIndexDelta;
 
320
                sourceURL = sources[sourceIndex];
 
321
                reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
 
322
            }
 
323
            sourceLineNumber += this._decodeVLQ(stringCharIterator);
 
324
            sourceColumnNumber += this._decodeVLQ(stringCharIterator);
 
325
            if (!this._isSeparator(stringCharIterator.peek()))
 
326
                nameIndex += this._decodeVLQ(stringCharIterator);
 
327
 
 
328
            this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
 
329
            if (!reverseMappings[sourceLineNumber])
 
330
                reverseMappings[sourceLineNumber] = [lineNumber, columnNumber];
 
331
        }
 
332
    },
 
333
 
 
334
    _isSeparator: function(char)
 
335
    {
 
336
        return char === "," || char === ";";
 
337
    },
 
338
 
 
339
    _decodeVLQ: function(stringCharIterator)
 
340
    {
 
341
        // Read unsigned value.
 
342
        var result = 0;
 
343
        var shift = 0;
 
344
        do {
 
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);
 
349
 
 
350
        // Fix the sign.
 
351
        var negative = result & 1;
 
352
        result >>= 1;
 
353
        return negative ? -result : result;
 
354
    },
 
355
 
 
356
    _canonicalizeURL: function(url, baseURL)
 
357
    {
 
358
        if (!url || !baseURL || url.asParsedURL() || url.substring(0, 5) === "data:")
 
359
            return url;
 
360
 
 
361
        var base = baseURL.asParsedURL();
 
362
        if (!base)
 
363
            return url;
 
364
 
 
365
        var baseHost = base.scheme + "://" + base.host + (base.port ? ":" + base.port : "");
 
366
        if (url[0] === "/")
 
367
            return baseHost + url;
 
368
        return baseHost + base.folderPathComponents + "/" + url;
 
369
    },
 
370
 
 
371
    _VLQ_BASE_SHIFT: 5,
 
372
    _VLQ_BASE_MASK: (1 << 5) - 1,
 
373
    _VLQ_CONTINUATION_MASK: 1 << 5
 
374
}
 
375
 
 
376
/**
 
377
 * @constructor
 
378
 */
 
379
WebInspector.SourceMapParser.StringCharIterator = function(string)
 
380
{
 
381
    this._string = string;
 
382
    this._position = 0;
 
383
}
 
384
 
 
385
WebInspector.SourceMapParser.StringCharIterator.prototype = {
 
386
    next: function()
 
387
    {
 
388
        return this._string.charAt(this._position++);
 
389
    },
 
390
 
 
391
    peek: function()
 
392
    {
 
393
        return this._string.charAt(this._position);
 
394
    },
 
395
 
 
396
    hasNext: function()
 
397
    {
 
398
        return this._position < this._string.length;
 
399
    }
 
400
}