Changeset 142445 in webkit


Ignore:
Timestamp:
Feb 11, 2013 3:05:45 AM (11 years ago)
Author:
apavlov@chromium.org
Message:

Web Inspector: Implement position-based sourcemapping for stylesheets
https://bugs.webkit.org/show_bug.cgi?id=109168

Source/WebCore:

Reviewed by Vsevolod Vlasov.

This change introduces support for position-based source maps for CSS stylesheets.
Sourcemaps and originating resources (sass, scss, etc.) are loaded synchronously
upon the CSS UISourceCode addition. RangeBasedSourceMap is removed as it is not used.

Test: http/tests/inspector/stylesheet-source-mapping.html

  • inspector/front-end/CSSStyleModel.js:

(WebInspector.CSSStyleModel):
(WebInspector.CSSStyleModel.prototype.setSourceMapping):
(WebInspector.CSSStyleModel.prototype.rawLocationToUILocation):
(WebInspector.CSSStyleModel.LiveLocation.prototype.uiLocation):
(WebInspector.CSSLocation):
(WebInspector.CSSProperty):
(WebInspector.CSSProperty.parsePayload):

  • inspector/front-end/CompilerScriptMapping.js:

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

  • inspector/front-end/SASSSourceMapping.js:

(WebInspector.SASSSourceMapping):
(WebInspector.SASSSourceMapping.prototype._styleSheetChanged.callback):
(WebInspector.SASSSourceMapping.prototype._styleSheetChanged):
(WebInspector.SASSSourceMapping.prototype._reloadCSS):
(WebInspector.SASSSourceMapping.prototype._resourceAdded.didRequestContent):
(WebInspector.SASSSourceMapping.prototype._resourceAdded):
(WebInspector.SASSSourceMapping.prototype._loadAndProcessSourceMap):
(WebInspector.SASSSourceMapping.prototype.loadSourceMapForStyleSheet):
(WebInspector.SASSSourceMapping.prototype._bindUISourceCode):
(WebInspector.SASSSourceMapping.prototype.rawLocationToUILocation):
(WebInspector.SASSSourceMapping.prototype.uiLocationToRawLocation):
(WebInspector.SASSSourceMapping.prototype._reset):

  • inspector/front-end/SourceMap.js:

(WebInspector.SourceMap):
(WebInspector.SourceMap.load):
(WebInspector.SourceMap.prototype.findEntry):
(WebInspector.SourceMap.prototype.findEntryReversed):
(WebInspector.SourceMap.prototype._parseMap):

  • inspector/front-end/StylesSourceMapping.js:

(WebInspector.StylesSourceMapping):
(WebInspector.StylesSourceMapping.prototype._bindUISourceCode):

  • inspector/front-end/inspector.js:

LayoutTests:

Added test for the stylesheet source mappings, followed the API changes,
removed RangeBasedSourceMap tests as this type of sourcemap is gone.

Reviewed by Vsevolod Vlasov.

  • http/tests/inspector/compiler-script-mapping-expected.txt:
  • http/tests/inspector/compiler-script-mapping.html:
  • http/tests/inspector/resources/example.css.map: Added.
  • http/tests/inspector/resources/example.scss: Added.
  • http/tests/inspector/stylesheet-source-mapping-expected.txt: Added.
  • http/tests/inspector/stylesheet-source-mapping.html: Added.
  • inspector/styles/range-based-mapping-expected.txt: Removed.
  • inspector/styles/range-based-mapping.html: Removed.
Location:
trunk
Files:
4 added
2 deleted
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r142444 r142445  
     12013-02-11  Alexander Pavlov  <apavlov@chromium.org>
     2
     3        Web Inspector: Implement position-based sourcemapping for stylesheets
     4        https://bugs.webkit.org/show_bug.cgi?id=109168
     5
     6        Added test for the stylesheet source mappings, followed the API changes,
     7        removed RangeBasedSourceMap tests as this type of sourcemap is gone.
     8
     9        Reviewed by Vsevolod Vlasov.
     10
     11        * http/tests/inspector/compiler-script-mapping-expected.txt:
     12        * http/tests/inspector/compiler-script-mapping.html:
     13        * http/tests/inspector/resources/example.css.map: Added.
     14        * http/tests/inspector/resources/example.scss: Added.
     15        * http/tests/inspector/stylesheet-source-mapping-expected.txt: Added.
     16        * http/tests/inspector/stylesheet-source-mapping.html: Added.
     17        * inspector/styles/range-based-mapping-expected.txt: Removed.
     18        * inspector/styles/range-based-mapping.html: Removed.
     19
    1202013-02-11  Alexander Shalamov  <alexander.shalamov@intel.com>
    221
  • trunk/LayoutTests/http/tests/inspector/compiler-script-mapping-expected.txt

    r140965 r142445  
    1 Tests PositionBasedSourceMap and CompilerScriptMapping.
     1Tests SourceMap and CompilerScriptMapping.
    22
    33
  • trunk/LayoutTests/http/tests/inspector/compiler-script-mapping.html

    r142269 r142445  
    5959                "sources":["example.js"]
    6060            };
    61             var mapping = new WebInspector.PositionBasedSourceMap("source-map.json", mappingPayload);
     61            var mapping = new WebInspector.SourceMap("source-map.json", mappingPayload);
    6262
    6363            checkMapping(0, 9, "example.js", 0, 9, mapping);
     
    8383                "sources":["example.js"]
    8484            };
    85             var mapping = new WebInspector.PositionBasedSourceMap("source-map.json", mappingPayload);
     85            var mapping = new WebInspector.SourceMap("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.PositionBasedSourceMap("source-map.json", mappingPayload);
     99            var mapping = new WebInspector.SourceMap("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.PositionBasedSourceMap("source-map.json", mappingPayload);
     122            var mapping = new WebInspector.SourceMap("source-map.json", mappingPayload);
    123123            InspectorTest.assertEquals(2, mapping.sources().length);
    124124            checkMapping(0, 0, "source1.js", 0, 0, mapping);
     
    330330
    331331<body onload="runTest()">
    332 <p>Tests PositionBasedSourceMap and CompilerScriptMapping.</p>
     332<p>Tests SourceMap and CompilerScriptMapping.</p>
    333333</body>
    334334</html>
  • trunk/Source/WebCore/ChangeLog

    r142444 r142445  
     12013-02-11  Alexander Pavlov  <apavlov@chromium.org>
     2
     3        Web Inspector: Implement position-based sourcemapping for stylesheets
     4        https://bugs.webkit.org/show_bug.cgi?id=109168
     5
     6        Reviewed by Vsevolod Vlasov.
     7
     8        This change introduces support for position-based source maps for CSS stylesheets.
     9        Sourcemaps and originating resources (sass, scss, etc.) are loaded synchronously
     10        upon the CSS UISourceCode addition. RangeBasedSourceMap is removed as it is not used.
     11
     12        Test: http/tests/inspector/stylesheet-source-mapping.html
     13
     14        * inspector/front-end/CSSStyleModel.js:
     15        (WebInspector.CSSStyleModel):
     16        (WebInspector.CSSStyleModel.prototype.setSourceMapping):
     17        (WebInspector.CSSStyleModel.prototype.rawLocationToUILocation):
     18        (WebInspector.CSSStyleModel.LiveLocation.prototype.uiLocation):
     19        (WebInspector.CSSLocation):
     20        (WebInspector.CSSProperty):
     21        (WebInspector.CSSProperty.parsePayload):
     22        * inspector/front-end/CompilerScriptMapping.js:
     23        (WebInspector.CompilerScriptMapping):
     24        (WebInspector.CompilerScriptMapping.prototype.rawLocationToUILocation):
     25        (WebInspector.CompilerScriptMapping.prototype.loadSourceMapForScript):
     26        * inspector/front-end/SASSSourceMapping.js:
     27        (WebInspector.SASSSourceMapping):
     28        (WebInspector.SASSSourceMapping.prototype._styleSheetChanged.callback):
     29        (WebInspector.SASSSourceMapping.prototype._styleSheetChanged):
     30        (WebInspector.SASSSourceMapping.prototype._reloadCSS):
     31        (WebInspector.SASSSourceMapping.prototype._resourceAdded.didRequestContent):
     32        (WebInspector.SASSSourceMapping.prototype._resourceAdded):
     33        (WebInspector.SASSSourceMapping.prototype._loadAndProcessSourceMap):
     34        (WebInspector.SASSSourceMapping.prototype.loadSourceMapForStyleSheet):
     35        (WebInspector.SASSSourceMapping.prototype._bindUISourceCode):
     36        (WebInspector.SASSSourceMapping.prototype.rawLocationToUILocation):
     37        (WebInspector.SASSSourceMapping.prototype.uiLocationToRawLocation):
     38        (WebInspector.SASSSourceMapping.prototype._reset):
     39        * inspector/front-end/SourceMap.js:
     40        (WebInspector.SourceMap):
     41        (WebInspector.SourceMap.load):
     42        (WebInspector.SourceMap.prototype.findEntry):
     43        (WebInspector.SourceMap.prototype.findEntryReversed):
     44        (WebInspector.SourceMap.prototype._parseMap):
     45        * inspector/front-end/StylesSourceMapping.js:
     46        (WebInspector.StylesSourceMapping):
     47        (WebInspector.StylesSourceMapping.prototype._bindUISourceCode):
     48        * inspector/front-end/inspector.js:
     49
    1502013-02-11  Alexander Shalamov  <alexander.shalamov@intel.com>
    251
  • trunk/Source/WebCore/inspector/front-end/CSSStyleModel.js

    r139885 r142445  
    3232 * @constructor
    3333 * @extends {WebInspector.Object}
    34  */
    35 WebInspector.CSSStyleModel = function()
    36 {
     34 * @param {WebInspector.Workspace} workspace
     35 */
     36WebInspector.CSSStyleModel = function(workspace)
     37{
     38    this._workspace = workspace;
    3739    this._pendingCommandsMajorState = [];
    3840    /** @type {Array.<WebInspector.CSSStyleModel.LiveLocation>} */
     
    495497    setSourceMapping: function(url, sourceMapping)
    496498    {
    497         this._sourceMappings[url] = sourceMapping;
     499        if (sourceMapping)
     500            this._sourceMappings[url] = sourceMapping;
     501        else
     502            delete this._sourceMappings[url];
    498503        this._updateLocations();
    499504    },
     
    516521
    517522    /**
     523     * @param {WebInspector.CSSRule} cssRule
    518524     * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
    519525     * @return {?WebInspector.LiveLocation}
     
    535541     * @return {?WebInspector.UILocation}
    536542     */
    537     _rawLocationToUILocation: function(rawLocation)
     543    rawLocationToUILocation: function(rawLocation)
    538544    {
    539545        var sourceMapping = this._sourceMappings[rawLocation.url];
    540         return sourceMapping ? sourceMapping.rawLocationToUILocation(rawLocation) : null;
     546        if (sourceMapping) {
     547            var uiLocation = sourceMapping.rawLocationToUILocation(rawLocation);
     548            if (uiLocation)
     549                return uiLocation;
     550        }
     551        var uri = WebInspector.fileMapping.uriForURL(rawLocation.url);
     552        var uiSourceCode = this._workspace.uiSourceCodeForURI(uri);
     553        if (!uiSourceCode)
     554            return null;
     555        return new WebInspector.UILocation(uiSourceCode, rawLocation.lineNumber, rawLocation.columnNumber);
    541556    },
    542557
     
    585600    {
    586601        var cssLocation = /** @type WebInspector.CSSLocation */ (this.rawLocation());
    587         return WebInspector.cssModel._rawLocationToUILocation(cssLocation);
     602        return WebInspector.cssModel.rawLocationToUILocation(cssLocation);
    588603    },
    589604
     
    604619 * @param {string} url
    605620 * @param {number} lineNumber
    606  */
    607 WebInspector.CSSLocation = function(url, lineNumber)
     621 * @param {number=} columnNumber
     622 */
     623WebInspector.CSSLocation = function(url, lineNumber, columnNumber)
    608624{
    609625    this.url = url;
    610626    this.lineNumber = lineNumber;
     627    this.columnNumber = columnNumber || 0;
    611628}
    612629
     
    891908 * @param {boolean} implicit
    892909 * @param {?string=} text
    893  */
    894 WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, text)
     910 * @param {CSSAgent.SourceRange=} range
     911 */
     912WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, text, range)
    895913{
    896914    this.ownerStyle = ownerStyle;
     
    903921    this.implicit = implicit;
    904922    this.text = text;
     923    this.range = range;
    905924}
    906925
     
    919938    // status: "style"
    920939    var result = new WebInspector.CSSProperty(
    921         ownerStyle, index, payload.name, payload.value, payload.priority || "", payload.status || "style", ("parsedOk" in payload) ? !!payload.parsedOk : true, !!payload.implicit, payload.text);
     940        ownerStyle, index, payload.name, payload.value, payload.priority || "", payload.status || "style", ("parsedOk" in payload) ? !!payload.parsedOk : true, !!payload.implicit, payload.text, payload.range);
    922941    return result;
    923942}
  • trunk/Source/WebCore/inspector/front-end/CompilerScriptMapping.js

    r140965 r142445  
    4040    this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAddedToWorkspace, this);
    4141    this._networkWorkspaceProvider = networkWorkspaceProvider;
    42     /** @type {Object.<string, WebInspector.PositionBasedSourceMap>} */
     42    /** @type {Object.<string, WebInspector.SourceMap>} */
    4343    this._sourceMapForSourceMapURL = {};
    44     /** @type {Object.<string, WebInspector.PositionBasedSourceMap>} */
     44    /** @type {Object.<string, WebInspector.SourceMap>} */
    4545    this._sourceMapForScriptId = {};
    4646    this._scriptForSourceMap = new Map();
    47     /** @type {Object.<string, WebInspector.PositionBasedSourceMap>} */
     47    /** @type {Object.<string, WebInspector.SourceMap>} */
    4848    this._sourceMapForURL = {};
    4949    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
     
    6262        var columnNumber = debuggerModelLocation.columnNumber || 0;
    6363        var entry = sourceMap.findEntry(lineNumber, columnNumber);
    64         if (entry.length === 2)
     64        if (!entry || entry.length === 2)
    6565            return null;
    6666        var url = entry[2];
     
    153153    /**
    154154     * @param {WebInspector.Script} script
    155      * @return {?WebInspector.PositionBasedSourceMap}
     155     * @return {?WebInspector.SourceMap}
    156156     */
    157157    loadSourceMapForScript: function(script)
     
    167167        if (!sourceMapURL)
    168168            return null;
    169 
    170169        var sourceMap = this._sourceMapForSourceMapURL[sourceMapURL];
    171170        if (sourceMap)
    172171            return sourceMap;
    173172
    174         try {
    175             // FIXME: make sendRequest async.
    176             var response = InspectorFrontendHost.loadResourceSynchronously(sourceMapURL);
    177             if (response.slice(0, 3) === ")]}")
    178                 response = response.substring(response.indexOf('\n'));
    179             var payload = /** @type {SourceMapV3} */ (JSON.parse(response));
    180             var baseURL = sourceMapURL.startsWith("data:") ? scriptURL : sourceMapURL;
    181             sourceMap = new WebInspector.PositionBasedSourceMap(baseURL, payload);
    182         } catch(e) {
    183             console.error(e.message);
     173        sourceMap = WebInspector.SourceMap.load(sourceMapURL, scriptURL);
     174        if (!sourceMap)
    184175            return null;
    185         }
    186176        this._sourceMapForSourceMapURL[sourceMapURL] = sourceMap;
    187177        return sourceMap;
  • trunk/Source/WebCore/inspector/front-end/SASSSourceMapping.js

    r140405 r142445  
    3232 * @constructor
    3333 * @implements {WebInspector.SourceMapping}
     34 * @param {WebInspector.CSSStyleModel} cssModel
    3435 * @param {WebInspector.Workspace} workspace
    3536 * @param {WebInspector.SimpleWorkspaceProvider} networkWorkspaceProvider
    3637 */
    37 WebInspector.SASSSourceMapping = function(workspace, networkWorkspaceProvider)
     38WebInspector.SASSSourceMapping = function(cssModel, workspace, networkWorkspaceProvider)
    3839{
     40    this._cssModel = cssModel;
    3941    this._workspace = workspace;
    4042    this._networkWorkspaceProvider = networkWorkspaceProvider;
    41     this._mappingEntries = {};
     43    this._sourceMapByURL = {};
     44    this._sourceMapByStyleSheetURL = {};
    4245    this._cssURLsForSASSURL = {};
    4346    this._timeoutForURL = {};
    4447    WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ResourceAdded, this._resourceAdded, this);
    4548    WebInspector.fileManager.addEventListener(WebInspector.FileManager.EventTypes.SavedURL, this._fileSaveFinished, this);
     49    this._cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this);
    4650    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._reset, this);
    4751}
     
    6165
    6266        populateFrame.call(this, WebInspector.resourceTreeModel.mainFrame);
     67    },
     68
     69    /**
     70     * @param {WebInspector.Event} event
     71     */
     72    _styleSheetChanged: function(event)
     73    {
     74        var isAddingRevision = this._isAddingRevision;
     75        delete this._isAddingRevision;
     76
     77        if (isAddingRevision)
     78            return;
     79        this._cssModel.resourceBinding().requestResourceURLForStyleSheetId(event.data.styleSheetId, callback.bind(this));
     80
     81        function callback(url)
     82        {
     83            if (!url)
     84                return;
     85            this._cssModel.setSourceMapping(url, null);
     86        }
    6387    },
    6488
     
    99123            return;
    100124        var newContent = InspectorFrontendHost.loadResourceSynchronously(url);
     125        this._isAddingRevision = true;
    101126        uiSourceCode.addRevision(newContent);
     127        // this._isAddingRevision will be deleted in this._styleSheetChanged().
     128        this._loadAndProcessSourceMap(newContent, url, true);
    102129    },
    103130
     
    118145        function didRequestContent(content, contentEncoded, mimeType)
    119146        {
    120             if (!content)
    121                 return;
    122             var lines = content.split(/\r?\n/);
    123             var debugInfoRegex = /@media\s\-sass\-debug\-info{filename{font-family:([^}]+)}line{font-family:\\0000(\d\d)([^}]*)}}/i;
    124             var lineNumbersRegex = /\/\*\s+line\s+([0-9]+),\s+([^*\/]+)/;
    125             for (var lineNumber = 0; lineNumber < lines.length; ++lineNumber) {
    126                 var match = debugInfoRegex.exec(lines[lineNumber]);
    127                 if (match) {
    128                     var url = match[1].replace(/\\(.)/g, "$1");
    129                     var line = parseInt(decodeURI("%" + match[2]) + match[3], 10);
    130                     this._addURLMapping(url, line, resource.url, lineNumber);
    131                     continue;
    132                 }
    133                 match = lineNumbersRegex.exec(lines[lineNumber]);
    134                 if (match) {
    135                     var fileName = match[2].trim();
    136                     var line = parseInt(match[1], 10);
    137                     var url = resource.url;
    138                     if (url.endsWith("/" + resource.parsedURL.lastPathComponent))
    139                         url = url.substring(0, url.length - resource.parsedURL.lastPathComponent.length) + fileName;
    140                     else
    141                         url = fileName;
    142                     this._addURLMapping(url, line, resource.url, lineNumber);
    143                     continue;
    144                 }
    145             }
     147            this._loadAndProcessSourceMap(content, resource.url);
    146148        }
    147149        resource.requestContent(didRequestContent.bind(this));
     
    149151
    150152    /**
    151      * @param {string} url
    152      * @param {number} line
    153      * @param {string} rawURL
    154      * @param {number} rawLine
    155      */
    156     _addURLMapping: function(url, line, rawURL, rawLine)
    157     {
    158         var uri = WebInspector.fileMapping.uriForURL(url);
    159         if (!WebInspector.fileMapping.hasMappingForURL(url) && !this._workspace.uiSourceCodeForURI(uri)) {
    160             var content = InspectorFrontendHost.loadResourceSynchronously(url);
    161             var contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Stylesheet, content, "text/x-scss");
    162             this._networkWorkspaceProvider.addFileForURL(url, contentProvider, true);
    163         }
    164         var rawLocationString = rawURL + ":" + (rawLine + 1);  // Next line after mapping metainfo
    165         this._mappingEntries[rawLocationString] = new WebInspector.SASSSourceMapping.MappingEntry(uri, line - 1, 0);
    166         this._addCSSURLforSASSURL(rawURL, url);
    167         WebInspector.cssModel.setSourceMapping(rawURL, this);
     153     * @param {?string} content
     154     * @param {string} cssURL
     155     * @param {boolean=} forceRebind
     156     */
     157    _loadAndProcessSourceMap: function(content, cssURL, forceRebind)
     158    {
     159        if (!content)
     160            return;
     161        var lines = content.split(/\r?\n/);
     162        if (!lines.length)
     163            return;
     164
     165        const sourceMapRegex = /^\/\*@ sourceMappingURL=([^\s]+)\s*\*\/$/;
     166        var lastLine = lines[lines.length - 1];
     167        var match = lastLine.match(sourceMapRegex);
     168        if (!match)
     169            return;
     170
     171        if (!forceRebind && this._sourceMapByStyleSheetURL[cssURL])
     172            return;
     173        var sourceMap = this.loadSourceMapForStyleSheet(match[1], cssURL, forceRebind);
     174
     175        if (!sourceMap)
     176            return;
     177        this._sourceMapByStyleSheetURL[cssURL] = sourceMap;
     178        this._bindUISourceCode(cssURL, sourceMap);
    168179    },
    169180
     
    186197
    187198    /**
     199     * @param {string} sourceMapURL
     200     * @param {string} styleSheetURL
     201     * @param {boolean=} forceReload
     202     * @return {WebInspector.SourceMap}
     203     */
     204    loadSourceMapForStyleSheet: function(sourceMapURL, styleSheetURL, forceReload)
     205    {
     206        var completeStyleSheetURL = WebInspector.ParsedURL.completeURL(WebInspector.inspectedPageURL, styleSheetURL);
     207        if (!completeStyleSheetURL)
     208            return null;
     209        var completeSourceMapURL = WebInspector.ParsedURL.completeURL(completeStyleSheetURL, sourceMapURL);
     210        if (!completeSourceMapURL)
     211            return null;
     212        var sourceMap = this._sourceMapByURL[completeSourceMapURL];
     213        if (sourceMap && !forceReload)
     214            return sourceMap;
     215        sourceMap = WebInspector.SourceMap.load(completeSourceMapURL, completeStyleSheetURL);
     216        if (!sourceMap) {
     217            delete this._sourceMapByURL[completeSourceMapURL];
     218            return null;
     219        }
     220        this._sourceMapByURL[completeSourceMapURL] = sourceMap;
     221        return sourceMap;
     222    },
     223
     224    /**
     225     * @param {string} rawURL
     226     * @param {WebInspector.SourceMap} sourceMap
     227     */
     228    _bindUISourceCode: function(rawURL, sourceMap)
     229    {
     230        var sources = sourceMap.sources();
     231        for (var i = 0; i < sources.length; ++i) {
     232            var url = sources[i];
     233            var uri = WebInspector.fileMapping.uriForURL(url);
     234            if (!WebInspector.fileMapping.hasMappingForURL(url) && !this._workspace.uiSourceCodeForURI(uri)) {
     235                var content = InspectorFrontendHost.loadResourceSynchronously(url);
     236                var contentProvider = new WebInspector.StaticContentProvider(WebInspector.resourceTypes.Stylesheet, content, "text/x-scss");
     237                var uiSourceCode = this._networkWorkspaceProvider.addFileForURL(url, contentProvider, true);
     238                uiSourceCode.setSourceMapping(this);
     239                this._addCSSURLforSASSURL(rawURL, url);
     240            }
     241        }
     242
     243        this._cssModel.setSourceMapping(rawURL, this);
     244    },
     245
     246    /**
    188247     * @param {WebInspector.RawLocation} rawLocation
    189248     * @return {WebInspector.UILocation}
     
    192251    {
    193252        var location = /** @type WebInspector.CSSLocation */ (rawLocation);
    194         var mappingEntry = this._mappingEntries[location.url + ":" + location.lineNumber];
    195         var uiLocation = null;
    196         if (mappingEntry) {
    197             var uiSourceCode = this._workspace.uiSourceCodeForURI(mappingEntry.uri);
    198             if (uiSourceCode)
    199                 uiLocation = new WebInspector.UILocation(uiSourceCode, mappingEntry.lineNumber, mappingEntry.columnNumber);
    200         }
    201         if (!uiLocation) {
    202             var uri = WebInspector.fileMapping.uriForURL(location.url);
    203             var uiSourceCode = this._workspace.uiSourceCodeForURI(uri);
    204             if (uiSourceCode)
    205                 uiLocation = new WebInspector.UILocation(uiSourceCode, location.lineNumber, 0);
    206         }
    207         return uiLocation;
     253        var entry;
     254        var uiSourceCode;
     255        var sourceMap = this._sourceMapByStyleSheetURL[location.url];
     256        if (!sourceMap)
     257            return null;
     258        entry = sourceMap.findEntry(location.lineNumber, location.columnNumber);
     259        if (!entry || entry.length === 2)
     260            return null;
     261        var uri = WebInspector.fileMapping.uriForURL(entry[2]);
     262        uiSourceCode = this._workspace.uiSourceCodeForURI(uri);
     263        if (!uiSourceCode)
     264            return null;
     265        return new WebInspector.UILocation(uiSourceCode, entry[3], entry[4]);
    208266    },
    209267
     
    217275    {
    218276        // FIXME: Implement this when ui -> raw mapping has clients.
    219         return new WebInspector.CSSLocation(uiSourceCode.url || "", lineNumber);
     277        return new WebInspector.CSSLocation(uiSourceCode.url || "", lineNumber, columnNumber);
    220278    },
    221279
    222280    _reset: function()
    223281    {
    224         this._mappingEntries = {};
     282        this._sourceMapByURL = {};
     283        this._sourceMapByStyleSheetURL = {};
    225284        this._populate();
    226285    }
    227286}
    228 
    229 /**
    230  * @constructor
    231  * @param {string} uri
    232  * @param {number} lineNumber
    233  */
    234 WebInspector.SASSSourceMapping.MappingEntry = function(uri, lineNumber, columnNumber)
    235 {
    236     this.uri = uri;
    237     this.lineNumber = lineNumber;
    238     this.columnNumber = columnNumber;
    239 }
    240 
  • trunk/Source/WebCore/inspector/front-end/SourceMap.js

    r140965 r142445  
    4646
    4747    this._sourceMappingURL = sourceMappingURL;
     48    this._reverseMappingsBySourceURL = {};
    4849    this._mappings = [];
    4950    this._sources = {};
     
    5253}
    5354
     55/**
     56 * @param {string} sourceMapURL
     57 * @param {string} compiledURL
     58 * @return {WebInspector.SourceMap}
     59 */
     60WebInspector.SourceMap.load = function(sourceMapURL, compiledURL)
     61{
     62    try {
     63        // FIXME: make sendRequest async.
     64        var response = InspectorFrontendHost.loadResourceSynchronously(sourceMapURL);
     65        if (!response)
     66            return null;
     67        if (response.slice(0, 3) === ")]}")
     68            response = response.substring(response.indexOf('\n'));
     69        var payload = /** @type {SourceMapV3} */ (JSON.parse(response));
     70        var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourceMapURL;
     71        return new WebInspector.SourceMap(baseURL, payload);
     72    } catch(e) {
     73        console.error(e.message);
     74        return null;
     75    }
     76}
     77
    5478WebInspector.SourceMap.prototype = {
    5579    /**
     
    93117
    94118    /**
    95      * @param {SourceMapV3} map
    96      * @param {number} lineNumber
    97      * @param {number} columnNumber
     119     * @param {number} lineNumber in compiled resource
     120     * @param {number} columnNumber in compiled resource
     121     * @return {?Array}
     122     */
     123    findEntry: function(lineNumber, columnNumber)
     124    {
     125        var first = 0;
     126        var count = this._mappings.length;
     127        while (count > 1) {
     128          var step = count >> 1;
     129          var middle = first + step;
     130          var mapping = this._mappings[middle];
     131          if (lineNumber < mapping[0] || (lineNumber === mapping[0] && columnNumber < mapping[1]))
     132              count = step;
     133          else {
     134              first = middle;
     135              count -= step;
     136          }
     137        }
     138        var entry = this._mappings[first];
     139        if (!first && entry && (lineNumber < entry[0] || (lineNumber === entry[0] && columnNumber < entry[1])))
     140            return null;
     141        return entry;
     142    },
     143
     144    /**
     145     * @param {string} sourceURL of the originating resource
     146     * @param {number} lineNumber in the originating resource
     147     * @return {Array}
     148     */
     149    findEntryReversed: function(sourceURL, lineNumber)
     150    {
     151        var mappings = this._reverseMappingsBySourceURL[sourceURL];
     152        for ( ; lineNumber < mappings.length; ++lineNumber) {
     153            var mapping = mappings[lineNumber];
     154            if (mapping)
     155                return mapping;
     156        }
     157        return this._mappings[0];
     158    },
     159
     160    /**
     161     * @override
    98162     */
    99163    _parseMap: function(map, lineNumber, columnNumber)
     
    152216            this._mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]);
    153217        }
    154     },
    155 
    156     /**
    157      * @param {string} char
    158      * @return {boolean}
    159      */
    160     _isSeparator: function(char)
    161     {
    162         return char === "," || char === ";";
    163     },
    164 
    165     /**
    166      * @param {WebInspector.SourceMap.StringCharIterator} stringCharIterator
    167      * @return {number}
    168      */
    169     _decodeVLQ: function(stringCharIterator)
    170     {
    171         // Read unsigned value.
    172         var result = 0;
    173         var shift = 0;
    174         do {
    175             var digit = this._base64Map[stringCharIterator.next()];
    176             result += (digit & this._VLQ_BASE_MASK) << shift;
    177             shift += this._VLQ_BASE_SHIFT;
    178         } while (digit & this._VLQ_CONTINUATION_MASK);
    179 
    180         // Fix the sign.
    181         var negative = result & 1;
    182         result >>= 1;
    183         return negative ? -result : result;
    184     },
    185 
    186     _VLQ_BASE_SHIFT: 5,
    187     _VLQ_BASE_MASK: (1 << 5) - 1,
    188     _VLQ_CONTINUATION_MASK: 1 << 5
    189 }
    190 
    191 /**
    192  * @constructor
    193  * @param {string} string
    194  */
    195 WebInspector.SourceMap.StringCharIterator = function(string)
    196 {
    197     this._string = string;
    198     this._position = 0;
    199 }
    200 
    201 WebInspector.SourceMap.StringCharIterator.prototype = {
    202     /**
    203      * @return {string}
    204      */
    205     next: function()
    206     {
    207         return this._string.charAt(this._position++);
    208     },
    209 
    210     /**
    211      * @return {string}
    212      */
    213     peek: function()
    214     {
    215         return this._string.charAt(this._position);
    216     },
    217 
    218     /**
    219      * @return {boolean}
    220      */
    221     hasNext: function()
    222     {
    223         return this._position < this._string.length;
    224     }
    225 }
    226 
    227 /**
    228  * @constructor
    229  * @extends WebInspector.SourceMap
    230  * @param {string} sourceMappingURL
    231  * @param {SourceMapV3} payload
    232  */
    233 WebInspector.PositionBasedSourceMap = function(sourceMappingURL, payload)
    234 {
    235     this._reverseMappingsBySourceURL = {};
    236     WebInspector.SourceMap.call(this, sourceMappingURL, payload);
    237 }
    238 
    239 WebInspector.PositionBasedSourceMap.prototype = {
    240     /**
    241      * @param {number} lineNumber in compiled resource
    242      * @param {number} columnNumber in compiled resource
    243      */
    244     findEntry: function(lineNumber, columnNumber)
    245     {
    246         var first = 0;
    247         var count = this._mappings.length;
    248         while (count > 1) {
    249           var step = count >> 1;
    250           var middle = first + step;
    251           var mapping = this._mappings[middle];
    252           if (lineNumber < mapping[0] || (lineNumber == mapping[0] && columnNumber < mapping[1]))
    253               count = step;
    254           else {
    255               first = middle;
    256               count -= step;
    257           }
    258         }
    259         return this._mappings[first];
    260     },
    261 
    262     /**
    263      * @param {string} sourceURL of the originating resource
    264      * @param {number} lineNumber in the originating resource
    265      * @return {Array}
    266      */
    267     findEntryReversed: function(sourceURL, lineNumber)
    268     {
    269         var mappings = this._reverseMappingsBySourceURL[sourceURL];
    270         for ( ; lineNumber < mappings.length; ++lineNumber) {
    271             var mapping = mappings[lineNumber];
    272             if (mapping)
    273                 return mapping;
    274         }
    275         return this._mappings[0];
    276     },
    277 
    278     /**
    279      * @override
    280      */
    281     _parseMap: function(map, lineNumber, columnNumber)
    282     {
    283         WebInspector.SourceMap.prototype._parseMap.call(this, map, lineNumber, columnNumber);
    284218
    285219        for (var i = 0; i < this._mappings.length; ++i) {
     
    297231    },
    298232
    299     __proto__: WebInspector.SourceMap.prototype
     233    /**
     234     * @param {string} char
     235     * @return {boolean}
     236     */
     237    _isSeparator: function(char)
     238    {
     239        return char === "," || char === ";";
     240    },
     241
     242    /**
     243     * @param {WebInspector.SourceMap.StringCharIterator} stringCharIterator
     244     * @return {number}
     245     */
     246    _decodeVLQ: function(stringCharIterator)
     247    {
     248        // Read unsigned value.
     249        var result = 0;
     250        var shift = 0;
     251        do {
     252            var digit = this._base64Map[stringCharIterator.next()];
     253            result += (digit & this._VLQ_BASE_MASK) << shift;
     254            shift += this._VLQ_BASE_SHIFT;
     255        } while (digit & this._VLQ_CONTINUATION_MASK);
     256
     257        // Fix the sign.
     258        var negative = result & 1;
     259        result >>= 1;
     260        return negative ? -result : result;
     261    },
     262
     263    _VLQ_BASE_SHIFT: 5,
     264    _VLQ_BASE_MASK: (1 << 5) - 1,
     265    _VLQ_CONTINUATION_MASK: 1 << 5
    300266}
    301267
    302268/**
    303269 * @constructor
    304  * @extends WebInspector.SourceMap
    305  * @param {string} sourceMappingURL
    306  * @param {SourceMapV3} payload
     270 * @param {string} string
    307271 */
    308 WebInspector.RangeBasedSourceMap = function(sourceMappingURL, payload)
     272WebInspector.SourceMap.StringCharIterator = function(string)
    309273{
    310     WebInspector.SourceMap.call(this, sourceMappingURL, payload);
    311 
    312     // Empty mappings should not normally be found in a range-based map.
    313     function callback(value)
    314     {
    315         return !!value[2];
     274    this._string = string;
     275    this._position = 0;
     276}
     277
     278WebInspector.SourceMap.StringCharIterator.prototype = {
     279    /**
     280     * @return {string}
     281     */
     282    next: function()
     283    {
     284        return this._string.charAt(this._position++);
     285    },
     286
     287    /**
     288     * @return {string}
     289     */
     290    peek: function()
     291    {
     292        return this._string.charAt(this._position);
     293    },
     294
     295    /**
     296     * @return {boolean}
     297     */
     298    hasNext: function()
     299    {
     300        return this._position < this._string.length;
    316301    }
    317     this._mappings = this._mappings.filter(callback);
    318 }
    319 
    320 WebInspector.RangeBasedSourceMap.MappingComparator = function(a, b)
    321 {
    322     if (a[0] !== b[0])
    323         return a[0] - b[0];
    324     return a[1] - b[1];
    325 }
    326 
    327 WebInspector.RangeBasedSourceMap.prototype = {
    328     /**
    329      * @param {number} lineNumber in the compiled resource
    330      * @param {number} columnNumber in the compiled resource
    331      * @return {?WebInspector.RangeBasedSourceMap.SourceRange}
    332      */
    333     findSourceRange: function(lineNumber, columnNumber)
    334     {
    335         var comparator = WebInspector.RangeBasedSourceMap.MappingComparator;
    336         var lookupEntry = [lineNumber, columnNumber];
    337         var index = binarySearch(lookupEntry, this._mappings, comparator);
    338         if (index >= 0) {
    339             if (index % 2) {
    340                 // Range end hit. Check if there's a following range that starts from the same position.
    341                 if (index + 1 >= this._mappings.length || comparator(lookupEntry, this._mappings[index + 1]))
    342                     return null;
    343                 return this._rangeForStartIndex(index + 1);
    344             }
    345 
    346             return this._rangeForStartIndex(index);
    347         }
    348 
    349         index = -(index + 1);
    350         if ((index % 2) && comparator(lookupEntry, this._mappings[index - 1]) >= 0 && comparator(lookupEntry, this._mappings[index]) <= 0)
    351             return this._rangeForStartIndex(index - 1);
    352 
    353         return null;
    354     },
    355 
    356     /**
    357      * @param {number} index
    358      * @return {?WebInspector.RangeBasedSourceMap.SourceRange}
    359      */
    360     _rangeForStartIndex: function(index)
    361     {
    362         var startEntry = this._mappings[index];
    363         var endEntry = this._mappings[index + 1];
    364         if (startEntry[2] !== endEntry[2]) {
    365             console.error("Mismatched source URLs in adjacent range-based sourcemap entries: %s vs %s", JSON.stringify(startEntry), JSON.stringify(endEntry));
    366             return null;
    367         }
    368 
    369         return new WebInspector.RangeBasedSourceMap.SourceRange(startEntry[2], startEntry[3], startEntry[4], endEntry[3], endEntry[4]);
    370     },
    371 
    372     __proto__: WebInspector.SourceMap.prototype
    373 }
    374 
    375 /**
    376  * @constructor
    377  * @param {string} url
    378  * @param {number} startLine
    379  * @param {number} startColumn
    380  * @param {number} endLine
    381  * @param {number} endColumn
    382  */
    383 WebInspector.RangeBasedSourceMap.SourceRange = function(url, startLine, startColumn, endLine, endColumn)
    384 {
    385     this.url = url;
    386     this.startLine = startLine;
    387     this.endLine = endLine;
    388     this.startColumn = startColumn;
    389     this.endColumn = endColumn;
    390 }
     302}
  • trunk/Source/WebCore/inspector/front-end/StylesSourceMapping.js

    r139885 r142445  
    3232 * @constructor
    3333 * @implements {WebInspector.SourceMapping}
     34 * @param {WebInspector.CSSStyleModel} cssModel
    3435 * @param {WebInspector.Workspace} workspace
    3536 */
    36 WebInspector.StylesSourceMapping = function(workspace)
     37WebInspector.StylesSourceMapping = function(cssModel, workspace)
    3738{
     39    this._cssModel = cssModel;
    3840    this._workspace = workspace;
    3941    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset, this);
     
    101103        var styleFile = new WebInspector.StyleFile(uiSourceCode);
    102104        uiSourceCode.setStyleFile(styleFile);
    103         WebInspector.cssModel.setSourceMapping(uiSourceCode.url, this);
     105        this._cssModel.setSourceMapping(uiSourceCode.url, this);
    104106    },
    105107
  • trunk/Source/WebCore/inspector/front-end/inspector.js

    r142269 r142445  
    414414    InspectorBackend.registerInspectorDispatcher(this);
    415415
    416     this.cssModel = new WebInspector.CSSStyleModel();
     416    this.workspace = new WebInspector.Workspace();
     417
     418    this.cssModel = new WebInspector.CSSStyleModel(this.workspace);
    417419    this.timelineManager = new WebInspector.TimelineManager();
    418420    this.userAgentSupport = new WebInspector.UserAgentSupport();
     
    433435    this.openAnchorLocationRegistry.registerHandler(autoselectPanel, function() { return false; });
    434436
    435     this.workspace = new WebInspector.Workspace();
    436437    this.workspaceController = new WebInspector.WorkspaceController(this.workspace);
    437438
     
    450451    this.liveEditSupport = new WebInspector.LiveEditSupport(this.workspace);
    451452    this.styleContentBinding = new WebInspector.StyleContentBinding(this.cssModel);
    452     new WebInspector.StylesSourceMapping(this.workspace);
     453    new WebInspector.StylesSourceMapping(this.cssModel, this.workspace);
    453454    if (WebInspector.experimentsSettings.sass.isEnabled())
    454         new WebInspector.SASSSourceMapping(this.workspace, this.networkWorkspaceProvider);
     455        new WebInspector.SASSSourceMapping(this.cssModel, this.workspace, this.networkWorkspaceProvider);
    455456
    456457    new WebInspector.PresentationConsoleMessageHelper(this.workspace);
Note: See TracChangeset for help on using the changeset viewer.