| Index: third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
 | 
| diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js b/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
 | 
| index 5449ee591c729826df8c31ed855bf6eb613141e9..3eca7c8aecee812240d91218459c44a42f44cd1f 100644
 | 
| --- a/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
 | 
| +++ b/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
 | 
| @@ -5,53 +5,49 @@
 | 
|  WebInspector.SourceMapNamesResolver = {};
 | 
|  
 | 
|  WebInspector.SourceMapNamesResolver._cachedMapSymbol = Symbol("cache");
 | 
| -WebInspector.SourceMapNamesResolver._cachedPromiseSymbol = Symbol("cachePromise");
 | 
| +WebInspector.SourceMapNamesResolver._cachedIdentifiersSymbol = Symbol("cachedIdentifiers");
 | 
|  
 | 
|  /**
 | 
| - * @param {!WebInspector.DebuggerModel.Scope} scope
 | 
| - * @return {!Promise.<!Map<string, string>>}
 | 
| + * @constructor
 | 
| + * @param {string} name
 | 
| + * @param {number} lineNumber
 | 
| + * @param {number} columnNumber
 | 
|   */
 | 
| -WebInspector.SourceMapNamesResolver._resolveScope = function(scope)
 | 
| +WebInspector.SourceMapNamesResolver.Identifier = function(name, lineNumber, columnNumber)
 | 
|  {
 | 
| -    var cachedMap = scope[WebInspector.SourceMapNamesResolver._cachedMapSymbol];
 | 
| -    if (cachedMap)
 | 
| -        return Promise.resolve(cachedMap);
 | 
| -
 | 
| -    var cachedPromise = scope[WebInspector.SourceMapNamesResolver._cachedPromiseSymbol];
 | 
| -    if (cachedPromise)
 | 
| -        return cachedPromise;
 | 
| +    this.name = name;
 | 
| +    this.lineNumber = lineNumber;
 | 
| +    this.columnNumber = columnNumber;
 | 
| +}
 | 
|  
 | 
| +/**
 | 
| + * @param {!WebInspector.DebuggerModel.Scope} scope
 | 
| + * @return {!Promise<!Array<!WebInspector.SourceMapNamesResolver.Identifier>>}
 | 
| + */
 | 
| +WebInspector.SourceMapNamesResolver._scopeIdentifiers = function(scope)
 | 
| +{
 | 
|      var startLocation = scope.startLocation();
 | 
|      var endLocation = scope.endLocation();
 | 
|  
 | 
|      if (scope.type() === DebuggerAgent.ScopeType.Global || !startLocation || !endLocation || !startLocation.script().sourceMapURL || (startLocation.script() !== endLocation.script()))
 | 
| -        return Promise.resolve(new Map());
 | 
| +        return Promise.resolve(/** @type {!Array<!WebInspector.SourceMapNamesResolver.Identifier>}*/([]));
 | 
|  
 | 
|      var script = startLocation.script();
 | 
| -    var sourceMap = WebInspector.debuggerWorkspaceBinding.sourceMapForScript(script);
 | 
| -    if (!sourceMap)
 | 
| -        return Promise.resolve(new Map());
 | 
| -
 | 
| -    var promise = script.requestContent().then(onContent);
 | 
| -    scope[WebInspector.SourceMapNamesResolver._cachedPromiseSymbol] = promise;
 | 
| -    return promise;
 | 
| +    return script.requestContent().then(onContent);
 | 
|  
 | 
|      /**
 | 
|       * @param {?string} content
 | 
| -     * @return {!Promise<!Map<string, string>>}
 | 
| +     * @return {!Promise<!Array<!WebInspector.SourceMapNamesResolver.Identifier>>}
 | 
|       */
 | 
|      function onContent(content)
 | 
|      {
 | 
|          if (!content)
 | 
| -            return Promise.resolve(new Map());
 | 
| -
 | 
| -        var startLocation = scope.startLocation();
 | 
| -        var endLocation = scope.endLocation();
 | 
| -        var textRange = new WebInspector.TextRange(startLocation.lineNumber, startLocation.columnNumber, endLocation.lineNumber, endLocation.columnNumber);
 | 
| +            return Promise.resolve(/** @type {!Array<!WebInspector.SourceMapNamesResolver.Identifier>}*/([]));
 | 
|  
 | 
|          var text = new WebInspector.Text(content);
 | 
| -        var scopeText = text.extract(textRange);
 | 
| -        var scopeStart = text.toSourceRange(textRange).offset;
 | 
| +        var scopeRange = new WebInspector.TextRange(startLocation.lineNumber, startLocation.columnNumber, endLocation.lineNumber, endLocation.columnNumber)
 | 
| +        var scopeText = text.extract(scopeRange);
 | 
| +        var scopeStart = text.toSourceRange(scopeRange).offset;
 | 
|          var prefix = "function fui";
 | 
|  
 | 
|          return WebInspector.SourceMapNamesResolverWorker._instance().javaScriptIdentifiers(prefix + scopeText)
 | 
| @@ -63,28 +59,124 @@ WebInspector.SourceMapNamesResolver._resolveScope = function(scope)
 | 
|       * @param {number} scopeStart
 | 
|       * @param {string} prefix
 | 
|       * @param {!Array<!{name: string, offset: number}>} identifiers
 | 
| -     * @return {!Map<string, string>}
 | 
| +     * @return {!Array<!WebInspector.SourceMapNamesResolver.Identifier>}
 | 
|       */
 | 
|      function onIdentifiers(text, scopeStart, prefix, identifiers)
 | 
|      {
 | 
| -        var namesMapping = new Map();
 | 
| -        var lineEndings = text.lineEndings();
 | 
| -
 | 
| +        var result = [];
 | 
| +        var cursor = new WebInspector.TextCursor(text.lineEndings());
 | 
| +        var promises = [];
 | 
|          for (var i = 0; i < identifiers.length; ++i) {
 | 
|              var id = identifiers[i];
 | 
| +            if (id.offset < prefix.length)
 | 
| +                continue;
 | 
|              var start = scopeStart + id.offset - prefix.length;
 | 
| +            cursor.resetTo(start);
 | 
| +            result.push(new WebInspector.SourceMapNamesResolver.Identifier(id.name, cursor.lineNumber(), cursor.columnNumber()));
 | 
| +        }
 | 
| +        return result;
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +/**
 | 
| + * @param {!WebInspector.DebuggerModel.Scope} scope
 | 
| + * @return {!Promise.<!Map<string, string>>}
 | 
| + */
 | 
| +WebInspector.SourceMapNamesResolver._resolveScope = function(scope)
 | 
| +{
 | 
| +    var identifiersPromise = scope[WebInspector.SourceMapNamesResolver._cachedIdentifiersSymbol];
 | 
| +    if (identifiersPromise)
 | 
| +        return identifiersPromise;
 | 
| +
 | 
| +    var script = scope.callFrame().script;
 | 
| +    var sourceMap = WebInspector.debuggerWorkspaceBinding.sourceMapForScript(script);
 | 
| +    if (!sourceMap)
 | 
| +        return Promise.resolve(new Map());
 | 
|  
 | 
| -            var lineNumber = lineEndings.lowerBound(start);
 | 
| -            var columnNumber = start - (lineNumber === 0 ? 0 : (lineEndings[lineNumber - 1] + 1));
 | 
| -            var entry = sourceMap.findEntry(lineNumber, columnNumber);
 | 
| -            if (entry)
 | 
| +    /** @type {!Map<string, !WebInspector.Text>} */
 | 
| +    var textCache = new Map();
 | 
| +    identifiersPromise = WebInspector.SourceMapNamesResolver._scopeIdentifiers(scope).then(onIdentifiers);
 | 
| +    scope[WebInspector.SourceMapNamesResolver._cachedIdentifiersSymbol] = identifiersPromise;
 | 
| +    return identifiersPromise;
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!Array<!WebInspector.SourceMapNamesResolver.Identifier>} identifiers
 | 
| +     * @return {!Promise<!Map<string, string>>}
 | 
| +     */
 | 
| +    function onIdentifiers(identifiers)
 | 
| +    {
 | 
| +        var namesMapping = new Map();
 | 
| +        // Extract as much as possible from SourceMap.
 | 
| +        for (var i = 0; i < identifiers.length; ++i) {
 | 
| +            var id = identifiers[i];
 | 
| +            var entry = sourceMap.findEntry(id.lineNumber, id.columnNumber);
 | 
| +            if (entry && entry.name)
 | 
|                  namesMapping.set(id.name, entry.name);
 | 
|          }
 | 
|  
 | 
| -        scope[WebInspector.SourceMapNamesResolver._cachedMapSymbol] = namesMapping;
 | 
| -        delete scope[WebInspector.SourceMapNamesResolver._cachedPromiseSymbol];
 | 
| -        WebInspector.SourceMapNamesResolver._scopeResolvedForTest();
 | 
| -        return namesMapping;
 | 
| +        // Resolve missing identifier names from sourcemap ranges.
 | 
| +        var promises = [];
 | 
| +        for (var i = 0; i < identifiers.length; ++i) {
 | 
| +            var id = identifiers[i];
 | 
| +            if (namesMapping.has(id.name))
 | 
| +                continue;
 | 
| +            var promise = resolveSourceName(id).then(onSourceNameResolved.bind(null, namesMapping, id));
 | 
| +            promises.push(promise);
 | 
| +        }
 | 
| +        return Promise.all(promises)
 | 
| +            .then(() => WebInspector.SourceMapNamesResolver._scopeResolvedForTest())
 | 
| +            .then(() => namesMapping)
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!Map<string, string>} namesMapping
 | 
| +     * @param {!WebInspector.SourceMapNamesResolver.Identifier} id
 | 
| +     * @param {?string} sourceName
 | 
| +     */
 | 
| +    function onSourceNameResolved(namesMapping, id, sourceName)
 | 
| +    {
 | 
| +        if (!sourceName)
 | 
| +            return;
 | 
| +        namesMapping.set(id.name, sourceName);
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!WebInspector.SourceMapNamesResolver.Identifier} id
 | 
| +     * @return {!Promise<?string>}
 | 
| +     */
 | 
| +    function resolveSourceName(id)
 | 
| +    {
 | 
| +        var startEntry = sourceMap.findEntry(id.lineNumber, id.columnNumber);
 | 
| +        var endEntry = sourceMap.findEntry(id.lineNumber, id.columnNumber + id.name.length);
 | 
| +        if (!startEntry || !endEntry || !startEntry.sourceURL || startEntry.sourceURL !== endEntry.sourceURL
 | 
| +            || !startEntry.sourceLineNumber || !startEntry.sourceColumnNumber
 | 
| +            || !endEntry.sourceLineNumber || !endEntry.sourceColumnNumber)
 | 
| +            return Promise.resolve(/** @type {?string} */(null));
 | 
| +        var sourceTextRange = new WebInspector.TextRange(startEntry.sourceLineNumber, startEntry.sourceColumnNumber, endEntry.sourceLineNumber, endEntry.sourceColumnNumber);
 | 
| +        var uiSourceCode = WebInspector.networkMapping.uiSourceCodeForScriptURL(startEntry.sourceURL, script);
 | 
| +        if (!uiSourceCode)
 | 
| +            return Promise.resolve(/** @type {?string} */(null));
 | 
| +
 | 
| +        return uiSourceCode.requestContent()
 | 
| +            .then(onSourceContent.bind(null, sourceTextRange));
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!WebInspector.TextRange} sourceTextRange
 | 
| +     * @param {?string} content
 | 
| +     * @return {?string}
 | 
| +     */
 | 
| +    function onSourceContent(sourceTextRange, content)
 | 
| +    {
 | 
| +        if (!content)
 | 
| +            return null;
 | 
| +        var text = textCache.get(content);
 | 
| +        if (!text) {
 | 
| +            text = new WebInspector.Text(content);
 | 
| +            textCache.set(content, text);
 | 
| +        }
 | 
| +        var originalIdentifier = text.extract(sourceTextRange).trim();
 | 
| +        return /[a-zA-Z0-9_$]+/.test(originalIdentifier) ? originalIdentifier : null;
 | 
|      }
 | 
|  }
 | 
|  
 | 
| @@ -151,7 +243,7 @@ WebInspector.SourceMapNamesResolver.resolveExpression = function(callFrame, orig
 | 
|          if (reverseMapping.has(originalText))
 | 
|              return Promise.resolve(reverseMapping.get(originalText) || "");
 | 
|  
 | 
| -        return WebInspector.SourceMapNamesResolver._resolveExpression(callFrame, uiSourceCode, lineNumber, startColumnNumber, endColumnNumber)
 | 
| +        return WebInspector.SourceMapNamesResolver._resolveExpression(callFrame, uiSourceCode, lineNumber, startColumnNumber, endColumnNumber);
 | 
|      }
 | 
|  }
 | 
|  
 | 
| 
 |