Changeset 138500 in webkit


Ignore:
Timestamp:
Dec 27, 2012 6:13:53 AM (11 years ago)
Author:
apavlov@chromium.org
Message:

Web Inspector: Refactor SourceMapParser to enable range-based sourcemap
https://bugs.webkit.org/show_bug.cgi?id=105764

Reviewed by Vsevolod Vlasov.

Source/WebCore:

Test: inspector/styles/range-based-mapping.html

  • WebCore.gypi:
  • WebCore.vcproj/WebCore.vcproj:
  • inspector/compile-front-end.py:
  • inspector/front-end/CompilerScriptMapping.js:

(WebInspector.CompilerScriptMapping):
(WebInspector.CompilerScriptMapping.prototype.loadSourceMapForScript):

  • inspector/front-end/SourceMap.js: Copied from Source/WebCore/inspector/front-end/CompilerScriptMapping.js.

(WebInspector.SourceMap):

Abstract. Renamed from SourceMapParser and supports only direct (generated -> originating source) mapping via findEntry()

(WebInspector.SourceMap.prototype.sources):
(WebInspector.SourceMap.prototype.sourceContent):
(WebInspector.SourceMap.prototype._parseMappingPayload):
(WebInspector.SourceMap.prototype._parseSections):
(WebInspector.SourceMap.prototype._parseMap):
(WebInspector.SourceMap.prototype._isSeparator):
(WebInspector.SourceMap.prototype._decodeVLQ):
(WebInspector.SourceMap.prototype._canonicalizeURL):
(WebInspector.SourceMap.StringCharIterator):
(WebInspector.SourceMap.StringCharIterator.prototype.next):
(WebInspector.SourceMap.StringCharIterator.prototype.peek):
(WebInspector.SourceMap.StringCharIterator.prototype.hasNext):
(WebInspector.PositionBasedSourceMap):

Extends SourceMap and provides direct (compiled -> original source)
and reverse (original -> compiled source) position mapping via findEntryReversed().

(WebInspector.PositionBasedSourceMap.prototype.findEntry):
(WebInspector.PositionBasedSourceMap.prototype.findEntryReversed):
(WebInspector.PositionBasedSourceMap.prototype._parseMap):
(WebInspector.RangeBasedSourceMap):

Extends SourceMap and provides direct range mapping
(compiled source position -> original source range) via findSourceRange().

(WebInspector.RangeBasedSourceMap.MappingComparator):
(WebInspector.RangeBasedSourceMap.prototype.findSourceRange):
(WebInspector.RangeBasedSourceMap.prototype._rangeForStartIndex):
(WebInspector.SourceRange):

  • inspector/front-end/WebKit.qrc:
  • inspector/front-end/inspector.html:

LayoutTests:

  • http/tests/inspector/compiler-script-mapping-expected.txt:
  • http/tests/inspector/compiler-script-mapping.html:
  • inspector/styles/range-based-mapping-expected.txt: Added.
  • inspector/styles/range-based-mapping.html: Added.
Location:
trunk
Files:
2 added
10 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r138499 r138500  
     12012-12-27  Alexander Pavlov  <apavlov@chromium.org>
     2
     3        Web Inspector: Refactor SourceMapParser to enable range-based sourcemap
     4        https://bugs.webkit.org/show_bug.cgi?id=105764
     5
     6        Reviewed by Vsevolod Vlasov.
     7
     8        * http/tests/inspector/compiler-script-mapping-expected.txt:
     9        * http/tests/inspector/compiler-script-mapping.html:
     10        * inspector/styles/range-based-mapping-expected.txt: Added.
     11        * inspector/styles/range-based-mapping.html: Added.
     12
    1132012-12-27  Andrey Adaikin  <aandrey@chromium.org>
    214
  • trunk/LayoutTests/http/tests/inspector/compiler-script-mapping-expected.txt

    r116843 r138500  
    1 Tests SourceMapParser and CompilerScriptMapping.
     1Tests PositionBasedSourceMap and CompilerScriptMapping.
    22
    33
  • trunk/LayoutTests/http/tests/inspector/compiler-script-mapping.html

    r138496 r138500  
    5959                "sources":["example.js"]
    6060            };
    61             var mapping = new WebInspector.SourceMapParser("source-map.json", mappingPayload);
     61            var mapping = new WebInspector.PositionBasedSourceMap("source-map.json", mappingPayload);
    6262
    6363            checkMapping(0, 9, "example.js", 0, 9, mapping);
     
    8383                "sources":["example.js"]
    8484            };
    85             var mapping = new WebInspector.SourceMapParser("source-map.json", mappingPayload);
     85            var mapping = new WebInspector.PositionBasedSourceMap("source-map.json", mappingPayload);
    8686            checkMapping(0, 0, "example.js", 0, 0, mapping);
    8787            var entry = mapping.findEntry(0, 1);
     
    9797                "sources":["example.js"]
    9898            };
    99             var mapping = new WebInspector.SourceMapParser("source-map.json", mappingPayload);
     99            var mapping = new WebInspector.PositionBasedSourceMap("source-map.json", mappingPayload);
    100100            checkMapping(0, 0, "example.js", 0, 0, mapping);
    101101            checkReverseMapping(3, 1, "example.js", 1, mapping);
     
    120120                }
    121121            ]};
    122             var mapping = new WebInspector.SourceMapParser("source-map.json", mappingPayload);
     122            var mapping = new WebInspector.PositionBasedSourceMap("source-map.json", mappingPayload);
    123123            InspectorTest.assertEquals(2, mapping.sources().length);
    124124            checkMapping(0, 0, "source1.js", 0, 0, mapping);
     
    131131        function testResolveSourceMapURL(next)
    132132        {
    133             var func = WebInspector.SourceMapParser.prototype._canonicalizeURL;
     133            var func = WebInspector.PositionBasedSourceMap.prototype._canonicalizeURL;
    134134            InspectorTest.assertEquals("http://example.com/map.json", func("http://example.com/map.json", "http://example.com/script.js"));
    135135            InspectorTest.assertEquals("http://example.com/map.json", func("/map.json", "http://example.com/script.js"));
     
    271271
    272272<body onload="runTest()">
    273 <p>Tests SourceMapParser and CompilerScriptMapping.</p>
     273<p>Tests PositionBasedSourceMap and CompilerScriptMapping.</p>
    274274</body>
    275275</html>
  • trunk/Source/WebCore/ChangeLog

    r138497 r138500  
     12012-12-27  Alexander Pavlov  <apavlov@chromium.org>
     2
     3        Web Inspector: Refactor SourceMapParser to enable range-based sourcemap
     4        https://bugs.webkit.org/show_bug.cgi?id=105764
     5
     6        Reviewed by Vsevolod Vlasov.
     7
     8        Test: inspector/styles/range-based-mapping.html
     9
     10        * WebCore.gypi:
     11        * WebCore.vcproj/WebCore.vcproj:
     12        * inspector/compile-front-end.py:
     13        * inspector/front-end/CompilerScriptMapping.js:
     14        (WebInspector.CompilerScriptMapping):
     15        (WebInspector.CompilerScriptMapping.prototype.loadSourceMapForScript):
     16        * inspector/front-end/SourceMap.js: Copied from Source/WebCore/inspector/front-end/CompilerScriptMapping.js.
     17        (WebInspector.SourceMap):
     18          Abstract. Renamed from SourceMapParser and supports only direct (generated -> originating source) mapping via findEntry()
     19        (WebInspector.SourceMap.prototype.sources):
     20        (WebInspector.SourceMap.prototype.sourceContent):
     21        (WebInspector.SourceMap.prototype._parseMappingPayload):
     22        (WebInspector.SourceMap.prototype._parseSections):
     23        (WebInspector.SourceMap.prototype._parseMap):
     24        (WebInspector.SourceMap.prototype._isSeparator):
     25        (WebInspector.SourceMap.prototype._decodeVLQ):
     26        (WebInspector.SourceMap.prototype._canonicalizeURL):
     27        (WebInspector.SourceMap.StringCharIterator):
     28        (WebInspector.SourceMap.StringCharIterator.prototype.next):
     29        (WebInspector.SourceMap.StringCharIterator.prototype.peek):
     30        (WebInspector.SourceMap.StringCharIterator.prototype.hasNext):
     31        (WebInspector.PositionBasedSourceMap):
     32          Extends SourceMap and provides direct (compiled -> original source)
     33          and reverse (original -> compiled source) position mapping via findEntryReversed().
     34        (WebInspector.PositionBasedSourceMap.prototype.findEntry):
     35        (WebInspector.PositionBasedSourceMap.prototype.findEntryReversed):
     36        (WebInspector.PositionBasedSourceMap.prototype._parseMap):
     37        (WebInspector.RangeBasedSourceMap):
     38          Extends SourceMap and provides direct range mapping
     39          (compiled source position -> original source range) via findSourceRange().
     40        (WebInspector.RangeBasedSourceMap.MappingComparator):
     41        (WebInspector.RangeBasedSourceMap.prototype.findSourceRange):
     42        (WebInspector.RangeBasedSourceMap.prototype._rangeForStartIndex):
     43        (WebInspector.SourceRange):
     44        * inspector/front-end/WebKit.qrc:
     45        * inspector/front-end/inspector.html:
     46
    1472012-12-25  Andrey Adaikin  <aandrey@chromium.org>
    248
  • trunk/Source/WebCore/WebCore.gypi

    r138496 r138500  
    51925192            'inspector/front-end/SourceHTMLTokenizer.js',
    51935193            'inspector/front-end/SourceJavaScriptTokenizer.js',
     5194            'inspector/front-end/SourceMap.js',
    51945195            'inspector/front-end/SourceMapping.js',
    51955196            'inspector/front-end/SourceTokenizer.js',
  • trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj

    r138496 r138500  
    7711477114                                </File>
    7711577115                                <File
     77116                                        RelativePath="..\inspector\front-end\SourceMap.js"
     77117                                        >
     77118                                </File>
     77119                                <File
    7711677120                                        RelativePath="..\inspector\front-end\SourceMapping.js"
    7711777121                                        >
  • trunk/Source/WebCore/inspector/compile-front-end.py

    r138496 r138500  
    104104            "ResourceType.js",
    105105            "ResourceUtils.js",
     106            "SourceMap.js",
    106107            "NetworkManager.js",
    107108            "NetworkRequest.js",
  • trunk/Source/WebCore/inspector/front-end/CompilerScriptMapping.js

    r138496 r138500  
    3939    this._workspace = workspace;
    4040    this._networkWorkspaceProvider = networkWorkspaceProvider;
    41     /** @type {Object.<string, WebInspector.SourceMapParser>} */
     41    /** @type {Object.<string, WebInspector.PositionBasedSourceMap>} */
    4242    this._sourceMapForSourceMapURL = {};
    43     /** @type {Object.<string, WebInspector.SourceMapParser>} */
     43    /** @type {Object.<string, WebInspector.PositionBasedSourceMap>} */
    4444    this._sourceMapForScriptId = {};
    4545    this._scriptForSourceMap = new Map();
    46     /** @type {Object.<string, WebInspector.SourceMapParser>} */
     46    /** @type {Object.<string, WebInspector.PositionBasedSourceMap>} */
    4747    this._sourceMapForURL = {};
    4848    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._reset, this);
     
    118118    /**
    119119     * @param {WebInspector.Script} script
    120      * @return {WebInspector.SourceMapParser}
     120     * @return {WebInspector.PositionBasedSourceMap}
    121121     */
    122122    loadSourceMapForScript: function(script)
    123123    {
    124         var sourceMapURL = WebInspector.SourceMapParser.prototype._canonicalizeURL(script.sourceMapURL, script.sourceURL);
     124        var sourceMapURL = WebInspector.PositionBasedSourceMap.prototype._canonicalizeURL(script.sourceMapURL, script.sourceURL);
    125125        var sourceMap = this._sourceMapForSourceMapURL[sourceMapURL];
    126126        if (sourceMap)
     
    132132            if (response.slice(0, 3) === ")]}")
    133133                response = response.substring(response.indexOf('\n'));
    134             var payload = /** @type {WebInspector.SourceMapPayload} */ (JSON.parse(response));
    135             sourceMap = new WebInspector.SourceMapParser(sourceMapURL, payload);
     134            var payload = /** @type {SourceMapV3} */ (JSON.parse(response));
     135            sourceMap = new WebInspector.PositionBasedSourceMap(sourceMapURL, payload);
    136136        } catch(e) {
    137137            console.error(e.message);
     
    150150    }
    151151}
    152 
    153 /**
    154  * @constructor
    155  */
    156 WebInspector.SourceMapPayload = function()
    157 {
    158     this.sections = [];
    159     this.mappings = "";
    160     this.sourceRoot = "";
    161     this.sources = [];
    162 }
    163 
    164 /**
    165  * Implements Source Map V3 consumer. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
    166  * for format description.
    167  * @constructor
    168  * @param {string} sourceMappingURL
    169  * @param {WebInspector.SourceMapPayload} payload
    170  */
    171 WebInspector.SourceMapParser = function(sourceMappingURL, payload)
    172 {
    173     if (!WebInspector.SourceMapParser.prototype._base64Map) {
    174         const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    175         WebInspector.SourceMapParser.prototype._base64Map = {};
    176         for (var i = 0; i < base64Digits.length; ++i)
    177             WebInspector.SourceMapParser.prototype._base64Map[base64Digits.charAt(i)] = i;
    178     }
    179 
    180     this._sourceMappingURL = sourceMappingURL;
    181     this._mappings = [];
    182     this._reverseMappingsBySourceURL = {};
    183     this._sourceContentByURL = {};
    184     this._parseMappingPayload(payload);
    185 }
    186 
    187 WebInspector.SourceMapParser.prototype = {
    188     /**
    189      * @return {Array.<string>}
    190      */
    191     sources: function()
    192     {
    193         var sources = [];
    194         for (var sourceURL in this._reverseMappingsBySourceURL)
    195             sources.push(sourceURL);
    196         return sources;
    197     },
    198 
    199     sourceContent: function(sourceURL)
    200     {
    201         return this._sourceContentByURL[sourceURL];
    202     },
    203 
    204     findEntry: function(lineNumber, columnNumber)
    205     {
    206         var first = 0;
    207         var count = this._mappings.length;
    208         while (count > 1) {
    209           var step = count >> 1;
    210           var middle = first + step;
    211           var mapping = this._mappings[middle];
    212           if (lineNumber < mapping[0] || (lineNumber == mapping[0] && columnNumber < mapping[1]))
    213               count = step;
    214           else {
    215               first = middle;
    216               count -= step;
    217           }
    218         }
    219         return this._mappings[first];
    220     },
    221 
    222     findEntryReversed: function(sourceURL, lineNumber)
    223     {
    224         var mappings = this._reverseMappingsBySourceURL[sourceURL];
    225         for ( ; lineNumber < mappings.length; ++lineNumber) {
    226             var mapping = mappings[lineNumber];
    227             if (mapping)
    228                 return mapping;
    229         }
    230         return this._mappings[0];
    231     },
    232 
    233     _parseMappingPayload: function(mappingPayload)
    234     {
    235         if (mappingPayload.sections)
    236             this._parseSections(mappingPayload.sections);
    237         else
    238             this._parseMap(mappingPayload, 0, 0);
    239     },
    240 
    241     /**
    242      * @param {Array.<SourceMapV3.Section>} sections
    243      */
    244     _parseSections: function(sections)
    245     {
    246         for (var i = 0; i < sections.length; ++i) {
    247             var section = sections[i];
    248             this._parseMap(section.map, section.offset.line, section.offset.column)
    249         }
    250     },
    251 
    252     /**
    253      * @param {SourceMapV3} map
    254      * @param {number} lineNumber
    255      * @param {number} columnNumber
    256      */
    257     _parseMap: function(map, lineNumber, columnNumber)
    258     {
    259         var sourceIndex = 0;
    260         var sourceLineNumber = 0;
    261         var sourceColumnNumber = 0;
    262         var nameIndex = 0;
    263 
    264         var sources = [];
    265         for (var i = 0; i < map.sources.length; ++i) {
    266             var sourceURL = map.sources[i];
    267             if (map.sourceRoot)
    268                 sourceURL = map.sourceRoot + "/" + sourceURL;
    269             var url = this._canonicalizeURL(sourceURL, this._sourceMappingURL);
    270             sources.push(url);
    271             if (!this._reverseMappingsBySourceURL[url])
    272                 this._reverseMappingsBySourceURL[url] = [];
    273             if (map.sourcesContent && map.sourcesContent[i])
    274                 this._sourceContentByURL[url] = map.sourcesContent[i];
    275         }
    276 
    277         var stringCharIterator = new WebInspector.SourceMapParser.StringCharIterator(map.mappings);
    278         var sourceURL = sources[sourceIndex];
    279         var reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
    280 
    281         while (true) {
    282             if (stringCharIterator.peek() === ",")
    283                 stringCharIterator.next();
    284             else {
    285                 while (stringCharIterator.peek() === ";") {
    286                     lineNumber += 1;
    287                     columnNumber = 0;
    288                     stringCharIterator.next();
    289                 }
    290                 if (!stringCharIterator.hasNext())
    291                     break;
    292             }
    293 
    294             columnNumber += this._decodeVLQ(stringCharIterator);
    295             if (this._isSeparator(stringCharIterator.peek())) {
    296                 this._mappings.push([lineNumber, columnNumber]);
    297                 continue;
    298             }
    299 
    300             var sourceIndexDelta = this._decodeVLQ(stringCharIterator);
    301             if (sourceIndexDelta) {
    302                 sourceIndex += sourceIndexDelta;
    303                 sourceURL = sources[sourceIndex];
    304                 reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
    305             }
    306             sourceLineNumber += this._decodeVLQ(stringCharIterator);
    307             sourceColumnNumber += this._decodeVLQ(stringCharIterator);
    308             if (!this._isSeparator(stringCharIterator.peek()))
    309                 nameIndex += this._decodeVLQ(stringCharIterator);
    310 
    311             this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
    312             if (!reverseMappings[sourceLineNumber])
    313                 reverseMappings[sourceLineNumber] = [lineNumber, columnNumber];
    314         }
    315     },
    316 
    317     _isSeparator: function(char)
    318     {
    319         return char === "," || char === ";";
    320     },
    321 
    322     _decodeVLQ: function(stringCharIterator)
    323     {
    324         // Read unsigned value.
    325         var result = 0;
    326         var shift = 0;
    327         do {
    328             var digit = this._base64Map[stringCharIterator.next()];
    329             result += (digit & this._VLQ_BASE_MASK) << shift;
    330             shift += this._VLQ_BASE_SHIFT;
    331         } while (digit & this._VLQ_CONTINUATION_MASK);
    332 
    333         // Fix the sign.
    334         var negative = result & 1;
    335         result >>= 1;
    336         return negative ? -result : result;
    337     },
    338 
    339     _canonicalizeURL: function(url, baseURL)
    340     {
    341         if (!url || !baseURL || url.asParsedURL() || url.substring(0, 5) === "data:")
    342             return url;
    343 
    344         var base = baseURL.asParsedURL();
    345         if (!base)
    346             return url;
    347 
    348         var baseHost = base.scheme + "://" + base.host + (base.port ? ":" + base.port : "");
    349         if (url[0] === "/")
    350             return baseHost + url;
    351         return baseHost + base.folderPathComponents + "/" + url;
    352     },
    353 
    354     _VLQ_BASE_SHIFT: 5,
    355     _VLQ_BASE_MASK: (1 << 5) - 1,
    356     _VLQ_CONTINUATION_MASK: 1 << 5
    357 }
    358 
    359 /**
    360  * @constructor
    361  */
    362 WebInspector.SourceMapParser.StringCharIterator = function(string)
    363 {
    364     this._string = string;
    365     this._position = 0;
    366 }
    367 
    368 WebInspector.SourceMapParser.StringCharIterator.prototype = {
    369     next: function()
    370     {
    371         return this._string.charAt(this._position++);
    372     },
    373 
    374     peek: function()
    375     {
    376         return this._string.charAt(this._position);
    377     },
    378 
    379     hasNext: function()
    380     {
    381         return this._position < this._string.length;
    382     }
    383 }
  • trunk/Source/WebCore/inspector/front-end/SourceMap.js

    r138499 r138500  
    3030
    3131/**
    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     this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._reset, this);
    49 }
    50 
    51 WebInspector.CompilerScriptMapping.prototype = {
    52     /**
    53      * @param {WebInspector.RawLocation} rawLocation
    54      * @return {WebInspector.UILocation}
    55      */
    56     rawLocationToUILocation: function(rawLocation)
    57     {
    58         var debuggerModelLocation = /** @type {WebInspector.DebuggerModel.Location} */ (rawLocation);
    59         var sourceMap = this._sourceMapForScriptId[debuggerModelLocation.scriptId];
    60         var lineNumber = debuggerModelLocation.lineNumber;
    61         var columnNumber = debuggerModelLocation.columnNumber || 0;
    62         var entry = sourceMap.findEntry(lineNumber, columnNumber);
    63         if (entry.length === 2)
    64             return null;
    65         var uiSourceCode = this._workspace.uiSourceCodeForURL(entry[2]);
    66         return new WebInspector.UILocation(uiSourceCode, entry[3], entry[4]);
    67     },
    68 
    69     /**
    70      * @param {WebInspector.UISourceCode} uiSourceCode
    71      * @param {number} lineNumber
    72      * @param {number} columnNumber
    73      * @return {WebInspector.DebuggerModel.Location}
    74      */
    75     uiLocationToRawLocation: function(uiSourceCode, lineNumber, columnNumber)
    76     {
    77         var sourceMap = this._sourceMapForURL[uiSourceCode.url];
    78         var entry = sourceMap.findEntryReversed(uiSourceCode.url, lineNumber);
    79         return WebInspector.debuggerModel.createRawLocation(this._scriptForSourceMap.get(sourceMap), entry[0], entry[1]);
    80     },
    81 
    82     /**
    83      * @param {WebInspector.Script} script
    84      */
    85     addScript: function(script)
    86     {
    87         var sourceMap = this.loadSourceMapForScript(script);
    88 
    89         if (this._scriptForSourceMap.get(sourceMap)) {
    90             this._sourceMapForScriptId[script.scriptId] = sourceMap;
    91             script.pushSourceMapping(this);
    92             return;
    93         }
    94 
    95         var sourceURLs = sourceMap.sources();
    96         for (var i = 0; i < sourceURLs.length; ++i) {
    97             var sourceURL = sourceURLs[i];
    98             if (this._workspace.uiSourceCodeForURL(sourceURL))
    99                 continue;
    100             this._sourceMapForURL[sourceURL] = sourceMap;
    101             var sourceContent = sourceMap.sourceContent(sourceURL);
    102             var contentProvider;
    103             if (sourceContent)
    104                 contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Script, sourceContent);
    105             else
    106                 contentProvider = new WebInspector.CompilerSourceMappingContentProvider(sourceURL);
    107             this._networkWorkspaceProvider.addFile(sourceURL, contentProvider, true);
    108             var uiSourceCode = this._workspace.uiSourceCodeForURL(sourceURL);
    109             uiSourceCode.setSourceMapping(this);
    110             uiSourceCode.isContentScript = script.isContentScript;
    111         }
    112 
    113         this._sourceMapForScriptId[script.scriptId] = sourceMap;
    114         this._scriptForSourceMap.put(sourceMap, script);
    115         script.pushSourceMapping(this);
    116     },
    117 
    118     /**
    119      * @param {WebInspector.Script} script
    120      * @return {WebInspector.SourceMapParser}
    121      */
    122     loadSourceMapForScript: function(script)
    123     {
    124         var sourceMapURL = WebInspector.SourceMapParser.prototype._canonicalizeURL(script.sourceMapURL, script.sourceURL);
    125         var sourceMap = this._sourceMapForSourceMapURL[sourceMapURL];
    126         if (sourceMap)
    127             return sourceMap;
    128 
    129         try {
    130             // FIXME: make sendRequest async.
    131             var response = InspectorFrontendHost.loadResourceSynchronously(sourceMapURL);
    132             if (response.slice(0, 3) === ")]}")
    133                 response = response.substring(response.indexOf('\n'));
    134             var payload = /** @type {WebInspector.SourceMapPayload} */ (JSON.parse(response));
    135             sourceMap = new WebInspector.SourceMapParser(sourceMapURL, payload);
    136         } catch(e) {
    137             console.error(e.message);
    138             return null;
    139         }
    140         this._sourceMapForSourceMapURL[sourceMapURL] = sourceMap;
    141         return sourceMap;
    142     },
    143 
    144     _reset: function()
    145     {
    146         this._sourceMapForSourceMapURL = {};
    147         this._sourceMapForScriptId = {};
    148         this._scriptForSourceMap = new Map();
    149         this._sourceMapForURL = {};
    150     }
    151 }
    152 
    153 /**
    154  * @constructor
    155  */
    156 WebInspector.SourceMapPayload = function()
    157 {
    158     this.sections = [];
    159     this.mappings = "";
    160     this.sourceRoot = "";
    161     this.sources = [];
    162 }
    163 
    164 /**
    165  * Implements Source Map V3 consumer. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
     32 * Implements Source Map V3 model. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
    16633 * for format description.
    16734 * @constructor
    16835 * @param {string} sourceMappingURL
    169  * @param {WebInspector.SourceMapPayload} payload
    170  */
    171 WebInspector.SourceMapParser = function(sourceMappingURL, payload)
    172 {
    173     if (!WebInspector.SourceMapParser.prototype._base64Map) {
     36 * @param {SourceMapV3} payload
     37 */
     38WebInspector.SourceMap = function(sourceMappingURL, payload)
     39{
     40    if (!WebInspector.SourceMap.prototype._base64Map) {
    17441        const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    175         WebInspector.SourceMapParser.prototype._base64Map = {};
     42        WebInspector.SourceMap.prototype._base64Map = {};
    17643        for (var i = 0; i < base64Digits.length; ++i)
    177             WebInspector.SourceMapParser.prototype._base64Map[base64Digits.charAt(i)] = i;
     44            WebInspector.SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i;
    17845    }
    17946
    18047    this._sourceMappingURL = sourceMappingURL;
    18148    this._mappings = [];
    182     this._reverseMappingsBySourceURL = {};
     49    this._sources = {};
    18350    this._sourceContentByURL = {};
    18451    this._parseMappingPayload(payload);
    18552}
    18653
    187 WebInspector.SourceMapParser.prototype = {
     54WebInspector.SourceMap.prototype = {
    18855    /**
    18956     * @return {Array.<string>}
     
    19158    sources: function()
    19259    {
     60        return Object.keys(this._sources);
     61    },
     62
     63    /**
     64     * @param {string} sourceURL
     65     * @return {string|undefined}
     66     */
     67    sourceContent: function(sourceURL)
     68    {
     69        return this._sourceContentByURL[sourceURL];
     70    },
     71
     72    /**
     73     * @param {SourceMapV3} mappingPayload
     74     */
     75    _parseMappingPayload: function(mappingPayload)
     76    {
     77        if (mappingPayload.sections)
     78            this._parseSections(mappingPayload.sections);
     79        else
     80            this._parseMap(mappingPayload, 0, 0);
     81    },
     82
     83    /**
     84     * @param {Array.<SourceMapV3.Section>} sections
     85     */
     86    _parseSections: function(sections)
     87    {
     88        for (var i = 0; i < sections.length; ++i) {
     89            var section = sections[i];
     90            this._parseMap(section.map, section.offset.line, section.offset.column);
     91        }
     92    },
     93
     94    /**
     95     * @param {SourceMapV3} map
     96     * @param {number} lineNumber
     97     * @param {number} columnNumber
     98     */
     99    _parseMap: function(map, lineNumber, columnNumber)
     100    {
     101        var sourceIndex = 0;
     102        var sourceLineNumber = 0;
     103        var sourceColumnNumber = 0;
     104        var nameIndex = 0;
     105
    193106        var sources = [];
    194         for (var sourceURL in this._reverseMappingsBySourceURL)
    195             sources.push(sourceURL);
    196         return sources;
    197     },
    198 
    199     sourceContent: function(sourceURL)
    200     {
    201         return this._sourceContentByURL[sourceURL];
    202     },
    203 
     107        var originalToCanonicalURLMap = {};
     108        for (var i = 0; i < map.sources.length; ++i) {
     109            var originalSourceURL = map.sources[i];
     110            var url = this._canonicalizeURL((map.sourceRoot ? map.sourceRoot + "/" : "") + originalSourceURL, this._sourceMappingURL);
     111            originalToCanonicalURLMap[originalSourceURL] = url;
     112            sources.push(url);
     113            this._sources[url] = true;
     114
     115            if (map.sourcesContent && map.sourcesContent[i])
     116                this._sourceContentByURL[url] = map.sourcesContent[i];
     117        }
     118
     119        var stringCharIterator = new WebInspector.SourceMap.StringCharIterator(map.mappings);
     120        var sourceURL = sources[sourceIndex];
     121
     122        while (true) {
     123            if (stringCharIterator.peek() === ",")
     124                stringCharIterator.next();
     125            else {
     126                while (stringCharIterator.peek() === ";") {
     127                    lineNumber += 1;
     128                    columnNumber = 0;
     129                    stringCharIterator.next();
     130                }
     131                if (!stringCharIterator.hasNext())
     132                    break;
     133            }
     134
     135            columnNumber += this._decodeVLQ(stringCharIterator);
     136            if (this._isSeparator(stringCharIterator.peek())) {
     137                this._mappings.push([lineNumber, columnNumber]);
     138                continue;
     139            }
     140
     141            var sourceIndexDelta = this._decodeVLQ(stringCharIterator);
     142            if (sourceIndexDelta) {
     143                sourceIndex += sourceIndexDelta;
     144                sourceURL = sources[sourceIndex];
     145            }
     146            sourceLineNumber += this._decodeVLQ(stringCharIterator);
     147            sourceColumnNumber += this._decodeVLQ(stringCharIterator);
     148            if (!this._isSeparator(stringCharIterator.peek()))
     149                nameIndex += this._decodeVLQ(stringCharIterator);
     150
     151            this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
     152        }
     153    },
     154
     155    /**
     156     * @param {string} char
     157     * @return {boolean}
     158     */
     159    _isSeparator: function(char)
     160    {
     161        return char === "," || char === ";";
     162    },
     163
     164    /**
     165     * @param {WebInspector.SourceMap.StringCharIterator} stringCharIterator
     166     * @return {number}
     167     */
     168    _decodeVLQ: function(stringCharIterator)
     169    {
     170        // Read unsigned value.
     171        var result = 0;
     172        var shift = 0;
     173        do {
     174            var digit = this._base64Map[stringCharIterator.next()];
     175            result += (digit & this._VLQ_BASE_MASK) << shift;
     176            shift += this._VLQ_BASE_SHIFT;
     177        } while (digit & this._VLQ_CONTINUATION_MASK);
     178
     179        // Fix the sign.
     180        var negative = result & 1;
     181        result >>= 1;
     182        return negative ? -result : result;
     183    },
     184
     185    /**
     186     * @param {string} url
     187     * @param {string} baseURL
     188     */
     189    _canonicalizeURL: function(url, baseURL)
     190    {
     191        if (!url || !baseURL || url.asParsedURL() || url.substring(0, 5) === "data:")
     192            return url;
     193
     194        var base = baseURL.asParsedURL();
     195        if (!base)
     196            return url;
     197
     198        var baseHost = base.scheme + "://" + base.host + (base.port ? ":" + base.port : "");
     199        if (url[0] === "/")
     200            return baseHost + url;
     201        return baseHost + base.folderPathComponents + "/" + url;
     202    },
     203
     204    _VLQ_BASE_SHIFT: 5,
     205    _VLQ_BASE_MASK: (1 << 5) - 1,
     206    _VLQ_CONTINUATION_MASK: 1 << 5
     207}
     208
     209/**
     210 * @constructor
     211 * @param {string} string
     212 */
     213WebInspector.SourceMap.StringCharIterator = function(string)
     214{
     215    this._string = string;
     216    this._position = 0;
     217}
     218
     219WebInspector.SourceMap.StringCharIterator.prototype = {
     220    /**
     221     * @return {string}
     222     */
     223    next: function()
     224    {
     225        return this._string.charAt(this._position++);
     226    },
     227
     228    /**
     229     * @return {string}
     230     */
     231    peek: function()
     232    {
     233        return this._string.charAt(this._position);
     234    },
     235
     236    /**
     237     * @return {boolean}
     238     */
     239    hasNext: function()
     240    {
     241        return this._position < this._string.length;
     242    }
     243}
     244
     245/**
     246 * @constructor
     247 * @extends WebInspector.SourceMap
     248 * @param {string} sourceMappingURL
     249 * @param {SourceMapV3} payload
     250 */
     251WebInspector.PositionBasedSourceMap = function(sourceMappingURL, payload)
     252{
     253    this._reverseMappingsBySourceURL = {};
     254    WebInspector.SourceMap.call(this, sourceMappingURL, payload);
     255}
     256
     257WebInspector.PositionBasedSourceMap.prototype = {
     258    /**
     259     * @param {number} lineNumber in compiled resource
     260     * @param {number} columnNumber in compiled resource
     261     */
    204262    findEntry: function(lineNumber, columnNumber)
    205263    {
     
    220278    },
    221279
     280    /**
     281     * @param {string} sourceURL of the originating resource
     282     * @param {number} lineNumber in the originating resource
     283     * @return {Array}
     284     */
    222285    findEntryReversed: function(sourceURL, lineNumber)
    223286    {
     
    231294    },
    232295
    233     _parseMappingPayload: function(mappingPayload)
    234     {
    235         if (mappingPayload.sections)
    236             this._parseSections(mappingPayload.sections);
    237         else
    238             this._parseMap(mappingPayload, 0, 0);
    239     },
    240 
    241     /**
    242      * @param {Array.<SourceMapV3.Section>} sections
    243      */
    244     _parseSections: function(sections)
    245     {
    246         for (var i = 0; i < sections.length; ++i) {
    247             var section = sections[i];
    248             this._parseMap(section.map, section.offset.line, section.offset.column)
    249         }
    250     },
    251 
    252     /**
    253      * @param {SourceMapV3} map
    254      * @param {number} lineNumber
    255      * @param {number} columnNumber
     296    /**
     297     * @override
    256298     */
    257299    _parseMap: function(map, lineNumber, columnNumber)
    258300    {
    259         var sourceIndex = 0;
    260         var sourceLineNumber = 0;
    261         var sourceColumnNumber = 0;
    262         var nameIndex = 0;
    263 
    264         var sources = [];
    265         for (var i = 0; i < map.sources.length; ++i) {
    266             var sourceURL = map.sources[i];
    267             if (map.sourceRoot)
    268                 sourceURL = map.sourceRoot + "/" + sourceURL;
    269             var url = this._canonicalizeURL(sourceURL, this._sourceMappingURL);
    270             sources.push(url);
     301        WebInspector.SourceMap.prototype._parseMap.call(this, map, lineNumber, columnNumber);
     302
     303        for (var i = 0; i < this._mappings.length; ++i) {
     304            var mapping = this._mappings[i];
     305            var url = mapping[2];
     306            if (!url)
     307                continue;
    271308            if (!this._reverseMappingsBySourceURL[url])
    272309                this._reverseMappingsBySourceURL[url] = [];
    273             if (map.sourcesContent && map.sourcesContent[i])
    274                 this._sourceContentByURL[url] = map.sourcesContent[i];
    275         }
    276 
    277         var stringCharIterator = new WebInspector.SourceMapParser.StringCharIterator(map.mappings);
    278         var sourceURL = sources[sourceIndex];
    279         var reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
    280 
    281         while (true) {
    282             if (stringCharIterator.peek() === ",")
    283                 stringCharIterator.next();
    284             else {
    285                 while (stringCharIterator.peek() === ";") {
    286                     lineNumber += 1;
    287                     columnNumber = 0;
    288                     stringCharIterator.next();
    289                 }
    290                 if (!stringCharIterator.hasNext())
    291                     break;
    292             }
    293 
    294             columnNumber += this._decodeVLQ(stringCharIterator);
    295             if (this._isSeparator(stringCharIterator.peek())) {
    296                 this._mappings.push([lineNumber, columnNumber]);
    297                 continue;
    298             }
    299 
    300             var sourceIndexDelta = this._decodeVLQ(stringCharIterator);
    301             if (sourceIndexDelta) {
    302                 sourceIndex += sourceIndexDelta;
    303                 sourceURL = sources[sourceIndex];
    304                 reverseMappings = this._reverseMappingsBySourceURL[sourceURL];
    305             }
    306             sourceLineNumber += this._decodeVLQ(stringCharIterator);
    307             sourceColumnNumber += this._decodeVLQ(stringCharIterator);
    308             if (!this._isSeparator(stringCharIterator.peek()))
    309                 nameIndex += this._decodeVLQ(stringCharIterator);
    310 
    311             this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
    312             if (!reverseMappings[sourceLineNumber])
    313                 reverseMappings[sourceLineNumber] = [lineNumber, columnNumber];
    314         }
    315     },
    316 
    317     _isSeparator: function(char)
    318     {
    319         return char === "," || char === ";";
    320     },
    321 
    322     _decodeVLQ: function(stringCharIterator)
    323     {
    324         // Read unsigned value.
    325         var result = 0;
    326         var shift = 0;
    327         do {
    328             var digit = this._base64Map[stringCharIterator.next()];
    329             result += (digit & this._VLQ_BASE_MASK) << shift;
    330             shift += this._VLQ_BASE_SHIFT;
    331         } while (digit & this._VLQ_CONTINUATION_MASK);
    332 
    333         // Fix the sign.
    334         var negative = result & 1;
    335         result >>= 1;
    336         return negative ? -result : result;
    337     },
    338 
    339     _canonicalizeURL: function(url, baseURL)
    340     {
    341         if (!url || !baseURL || url.asParsedURL() || url.substring(0, 5) === "data:")
    342             return url;
    343 
    344         var base = baseURL.asParsedURL();
    345         if (!base)
    346             return url;
    347 
    348         var baseHost = base.scheme + "://" + base.host + (base.port ? ":" + base.port : "");
    349         if (url[0] === "/")
    350             return baseHost + url;
    351         return baseHost + base.folderPathComponents + "/" + url;
    352     },
    353 
    354     _VLQ_BASE_SHIFT: 5,
    355     _VLQ_BASE_MASK: (1 << 5) - 1,
    356     _VLQ_CONTINUATION_MASK: 1 << 5
     310            var reverseMappings = this._reverseMappingsBySourceURL[url];
     311            var sourceLine = mapping[3];
     312            if (!reverseMappings[sourceLine])
     313                reverseMappings[sourceLine] = [mapping[0], mapping[1]];
     314        }
     315    },
     316
     317    __proto__: WebInspector.SourceMap.prototype
    357318}
    358319
    359320/**
    360321 * @constructor
    361  */
    362 WebInspector.SourceMapParser.StringCharIterator = function(string)
    363 {
    364     this._string = string;
    365     this._position = 0;
    366 }
    367 
    368 WebInspector.SourceMapParser.StringCharIterator.prototype = {
    369     next: function()
    370     {
    371         return this._string.charAt(this._position++);
    372     },
    373 
    374     peek: function()
    375     {
    376         return this._string.charAt(this._position);
    377     },
    378 
    379     hasNext: function()
    380     {
    381         return this._position < this._string.length;
     322 * @extends WebInspector.SourceMap
     323 * @param {string} sourceMappingURL
     324 * @param {SourceMapV3} payload
     325 */
     326WebInspector.RangeBasedSourceMap = function(sourceMappingURL, payload)
     327{
     328    WebInspector.SourceMap.call(this, sourceMappingURL, payload);
     329
     330    // Empty mappings should not normally be found in a range-based map.
     331    function callback(value)
     332    {
     333        return !!value[2];
    382334    }
    383 }
     335    this._mappings = this._mappings.filter(callback);
     336}
     337
     338WebInspector.RangeBasedSourceMap.MappingComparator = function(a, b)
     339{
     340    if (a[0] !== b[0])
     341        return a[0] - b[0];
     342    return a[1] - b[1];
     343}
     344
     345WebInspector.RangeBasedSourceMap.prototype = {
     346    /**
     347     * @param {number} lineNumber in the compiled resource
     348     * @param {number} columnNumber in the compiled resource
     349     * @return {?WebInspector.RangeBasedSourceMap.SourceRange}
     350     */
     351    findSourceRange: function(lineNumber, columnNumber)
     352    {
     353        var comparator = WebInspector.RangeBasedSourceMap.MappingComparator;
     354        var lookupEntry = [lineNumber, columnNumber];
     355        var index = binarySearch(lookupEntry, this._mappings, comparator);
     356        if (index >= 0) {
     357            if (index % 2) {
     358                // Range end hit. Check if there's a following range that starts from the same position.
     359                if (index + 1 >= this._mappings.length || comparator(lookupEntry, this._mappings[index + 1]))
     360                    return null;
     361                return this._rangeForStartIndex(index + 1);
     362            }
     363
     364            return this._rangeForStartIndex(index);
     365        }
     366
     367        index = -(index + 1);
     368        if ((index % 2) && comparator(lookupEntry, this._mappings[index - 1]) >= 0 && comparator(lookupEntry, this._mappings[index]) <= 0)
     369            return this._rangeForStartIndex(index - 1);
     370
     371        return null;
     372    },
     373
     374    /**
     375     * @param {number} index
     376     * @return {?WebInspector.RangeBasedSourceMap.SourceRange}
     377     */
     378    _rangeForStartIndex: function(index)
     379    {
     380        var startEntry = this._mappings[index];
     381        var endEntry = this._mappings[index + 1];
     382        if (startEntry[2] !== endEntry[2]) {
     383            console.error("Mismatched source URLs in adjacent range-based sourcemap entries: %s vs %s", JSON.stringify(startEntry), JSON.stringify(endEntry));
     384            return null;
     385        }
     386
     387        return new WebInspector.RangeBasedSourceMap.SourceRange(startEntry[2], startEntry[3], startEntry[4], endEntry[3], endEntry[4]);
     388    },
     389
     390    __proto__: WebInspector.SourceMap.prototype
     391}
     392
     393/**
     394 * @constructor
     395 * @param {string} url
     396 * @param {number} startLine
     397 * @param {number} startColumn
     398 * @param {number} endLine
     399 * @param {number} endColumn
     400 */
     401WebInspector.RangeBasedSourceMap.SourceRange = function(url, startLine, startColumn, endLine, endColumn)
     402{
     403    this.url = url;
     404    this.startLine = startLine;
     405    this.endLine = endLine;
     406    this.startColumn = startColumn;
     407    this.endColumn = endColumn;
     408}
  • trunk/Source/WebCore/inspector/front-end/WebKit.qrc

    r138496 r138500  
    185185    <file>SourceHTMLTokenizer.js</file>
    186186    <file>SourceJavaScriptTokenizer.js</file>
     187    <file>SourceMap.js</file>
    187188    <file>SourceMapping.js</file>
    188189    <file>SourceTokenizer.js</file>
  • trunk/Source/WebCore/inspector/front-end/inspector.html

    r138496 r138500  
    132132    <script type="text/javascript" src="FileUtils.js"></script>
    133133    <script type="text/javascript" src="DebuggerModel.js"></script>
     134    <script type="text/javascript" src="SourceMap.js"></script>
    134135    <script type="text/javascript" src="SourceMapping.js"></script>
    135136    <script type="text/javascript" src="Script.js"></script>
Note: See TracChangeset for help on using the changeset viewer.