| Index: third_party/WebKit/Source/devtools/front_end/sdk/CPUProfileDataModel.js | 
| diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/CPUProfileDataModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/CPUProfileDataModel.js | 
| index d51f72d510a9e418ce1b0e3a7dbfae8743f22f34..702ae6c71ee3a351160e57d1ff7fa86f66139e91 100644 | 
| --- a/third_party/WebKit/Source/devtools/front_end/sdk/CPUProfileDataModel.js | 
| +++ b/third_party/WebKit/Source/devtools/front_end/sdk/CPUProfileDataModel.js | 
| @@ -2,72 +2,82 @@ | 
| // Use of this source code is governed by a BSD-style license that can be | 
| // found in the LICENSE file. | 
|  | 
| +/** | 
| + * @constructor | 
| + * @extends {WebInspector.ProfileNode} | 
| + * @param {!ProfilerAgent.CPUProfileNode} sourceNode | 
| + * @param {number} sampleTime | 
| + */ | 
| +WebInspector.CPUProfileNode = function(sourceNode, sampleTime) | 
| +{ | 
| +    WebInspector.ProfileNode.call(this, sourceNode.functionName, sourceNode.scriptId, sourceNode.url, sourceNode.lineNumber, sourceNode.columnNumber); | 
| +    this.id = sourceNode.id; | 
| +    this.self = sourceNode.hitCount * sampleTime; | 
| +    this.callUID = sourceNode.callUID; | 
| +    this.positionTicks = sourceNode.positionTicks; | 
| +    this.deoptReason = sourceNode.deoptReason; | 
| +    // TODO: Remove the following field in favor of this.self | 
| +    this.selfTime = this.self; | 
| +} | 
| + | 
| +WebInspector.CPUProfileNode.prototype = { | 
| +    __proto__: WebInspector.ProfileNode.prototype | 
| +} | 
|  | 
| /** | 
| * @constructor | 
| + * @extends {WebInspector.ProfileTreeModel} | 
| * @param {!ProfilerAgent.CPUProfile} profile | 
| */ | 
| WebInspector.CPUProfileDataModel = function(profile) | 
| { | 
| -    this.profileHead = profile.head; | 
| this.samples = profile.samples; | 
| this.timestamps = profile.timestamps; | 
| +    // Convert times from sec to msec. | 
| this.profileStartTime = profile.startTime * 1000; | 
| this.profileEndTime = profile.endTime * 1000; | 
| -    this._assignParentsInProfile(); | 
| +    this.totalHitCount = 0; | 
| +    if (!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get()) | 
| +        this._filterNativeFrames(profile.head); | 
| +    this.profileHead = this._translateProfileTree(profile.head); | 
| +    WebInspector.ProfileTreeModel.call(this, this.profileHead, this.profileStartTime, this.profileEndTime); | 
| +    this._extractMetaNodes(); | 
| if (this.samples) { | 
| +        this._buildIdToNodeMap(); | 
| this._sortSamples(); | 
| this._normalizeTimestamps(); | 
| -        this._buildIdToNodeMap(); | 
| this._fixMissingSamples(); | 
| } | 
| -    if (!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get()) | 
| -        this._filterNativeFrames(); | 
| -    this._assignDepthsInProfile(); | 
| -    this._calculateTimes(profile); | 
| +    this._assignTotalTimes(this.profileHead); | 
| } | 
|  | 
| WebInspector.CPUProfileDataModel.prototype = { | 
| /** | 
| -     * @param {!ProfilerAgent.CPUProfile} profile | 
| +     * @param {!ProfilerAgent.CPUProfileNode} root | 
| */ | 
| -    _calculateTimes: function(profile) | 
| -    { | 
| -        function totalHitCount(node) { | 
| -            var result = node.hitCount; | 
| -            for (var i = 0; i < node.children.length; i++) | 
| -                result += totalHitCount(node.children[i]); | 
| -            return result; | 
| -        } | 
| -        profile.totalHitCount = totalHitCount(profile.head); | 
| -        this.totalHitCount = profile.totalHitCount; | 
| - | 
| -        var duration = this.profileEndTime - this.profileStartTime; | 
| -        var samplingInterval = duration / profile.totalHitCount; | 
| -        this.samplingInterval = samplingInterval; | 
| - | 
| -        function calculateTimesForNode(node) { | 
| -            node.selfTime = node.hitCount * samplingInterval; | 
| -            var totalHitCount = node.hitCount; | 
| -            for (var i = 0; i < node.children.length; i++) | 
| -                totalHitCount += calculateTimesForNode(node.children[i]); | 
| -            node.totalTime = totalHitCount * samplingInterval; | 
| -            return totalHitCount; | 
| -        } | 
| -        calculateTimesForNode(profile.head); | 
| -    }, | 
| - | 
| -    _filterNativeFrames: function() | 
| +    _filterNativeFrames: function(root) | 
| { | 
| +        // TODO: get rid of this function and do the filtering while _translateProfileTree | 
| if (this.samples) { | 
| +            /** @type {!Map<number, !ProfilerAgent.CPUProfileNode>} */ | 
| +            var idToNode = new Map(); | 
| +            var stack = [root]; | 
| +            while (stack.length) { | 
| +                var node = stack.pop(); | 
| +                idToNode.set(node.id, node); | 
| +                for (var i = 0; i < node.children.length; i++) { | 
| +                    node.children[i].parent = node; | 
| +                    stack.push(node.children[i]); | 
| +                } | 
| +            } | 
| for (var i = 0; i < this.samples.length; ++i) { | 
| -                var node = this.nodeByIndex(i); | 
| +                var node = idToNode.get(this.samples[i]); | 
| while (isNativeNode(node)) | 
| node = node.parent; | 
| this.samples[i] = node.id; | 
| } | 
| } | 
| -        processSubtree(this.profileHead); | 
| +        processSubtree(root); | 
|  | 
| /** | 
| * @param {!ProfilerAgent.CPUProfileNode} node | 
| @@ -118,44 +128,43 @@ WebInspector.CPUProfileDataModel.prototype = { | 
| } | 
| }, | 
|  | 
| -    _assignParentsInProfile: function() | 
| +    /** | 
| +     * @param {!ProfilerAgent.CPUProfileNode} root | 
| +     * @return {!WebInspector.CPUProfileNode} | 
| +     */ | 
| +    _translateProfileTree: function(root) | 
| { | 
| -        var head = this.profileHead; | 
| -        head.parent = null; | 
| -        var nodesToTraverse = [ head ]; | 
| -        while (nodesToTraverse.length) { | 
| -            var parent = nodesToTraverse.pop(); | 
| -            var children = parent.children; | 
| -            var length = children.length; | 
| -            for (var i = 0; i < length; ++i) { | 
| -                var child = children[i]; | 
| -                child.parent = parent; | 
| -                if (child.children.length) | 
| -                    nodesToTraverse.push(child); | 
| -            } | 
| +        /** | 
| +         * @param  {!ProfilerAgent.CPUProfileNode} node | 
| +         * @return {number} | 
| +         */ | 
| +        function computeHitCountForSubtree(node) | 
| +        { | 
| +            return node.children.reduce((acc, node) => acc + computeHitCountForSubtree(node), node.hitCount); | 
| +        } | 
| +        this.totalHitCount = computeHitCountForSubtree(root); | 
| +        var sampleTime = (this.profileEndTime - this.profileStartTime) / this.totalHitCount; | 
| +        var resultRoot = new WebInspector.CPUProfileNode(root, sampleTime); | 
| +        var targetNodeStack = [resultRoot]; | 
| +        var sourceNodeStack = [root]; | 
| +        while (sourceNodeStack.length) { | 
| +            var sourceNode = sourceNodeStack.pop(); | 
| +            var parentNode = targetNodeStack.pop(); | 
| +            parentNode.children = sourceNode.children.map(child => new WebInspector.CPUProfileNode(child, sampleTime)); | 
| +            sourceNodeStack.push.apply(sourceNodeStack, sourceNode.children); | 
| +            targetNodeStack.push.apply(targetNodeStack, parentNode.children); | 
| } | 
| +        return resultRoot; | 
| }, | 
|  | 
| -    _assignDepthsInProfile: function() | 
| +    /** | 
| +     * @param {!WebInspector.ProfileNode} node | 
| +     */ | 
| +    _assignTotalTimes: function(node) | 
| { | 
| -        var head = this.profileHead; | 
| -        head.depth = -1; | 
| -        this.maxDepth = 0; | 
| -        var nodesToTraverse = [ head ]; | 
| -        while (nodesToTraverse.length) { | 
| -            var parent = nodesToTraverse.pop(); | 
| -            var depth = parent.depth + 1; | 
| -            if (depth > this.maxDepth) | 
| -                this.maxDepth = depth; | 
| -            var children = parent.children; | 
| -            var length = children.length; | 
| -            for (var i = 0; i < length; ++i) { | 
| -                var child = children[i]; | 
| -                child.depth = depth; | 
| -                if (child.children.length) | 
| -                    nodesToTraverse.push(child); | 
| -            } | 
| -        } | 
| +        // TODO: get rid of this field in favor of this.total | 
| +        node.totalTime = node.total; | 
| +        node.children.forEach(this._assignTotalTimes, this); | 
| }, | 
|  | 
| _sortSamples: function() | 
| @@ -213,7 +222,7 @@ WebInspector.CPUProfileDataModel.prototype = { | 
|  | 
| _buildIdToNodeMap: function() | 
| { | 
| -        /** @type {!Object.<number, !ProfilerAgent.CPUProfileNode>} */ | 
| +        /** @type {!Object<number, !WebInspector.CPUProfileNode>} */ | 
| this._idToNode = {}; | 
| var idToNode = this._idToNode; | 
| var stack = [this.profileHead]; | 
| @@ -223,7 +232,10 @@ WebInspector.CPUProfileDataModel.prototype = { | 
| for (var i = 0; i < node.children.length; i++) | 
| stack.push(node.children[i]); | 
| } | 
| +    }, | 
|  | 
| +    _extractMetaNodes: function() | 
| +    { | 
| var topLevelNodes = this.profileHead.children; | 
| for (var i = 0; i < topLevelNodes.length && !(this.gcNode && this.programNode && this.idleNode); i++) { | 
| var node = topLevelNodes[i]; | 
| @@ -265,12 +277,12 @@ WebInspector.CPUProfileDataModel.prototype = { | 
| } | 
|  | 
| /** | 
| -         * @param {!ProfilerAgent.CPUProfileNode} node | 
| -         * @return {!ProfilerAgent.CPUProfileNode} | 
| +         * @param {!WebInspector.ProfileNode} node | 
| +         * @return {!WebInspector.ProfileNode} | 
| */ | 
| function bottomNode(node) | 
| { | 
| -            while (node.parent.parent) | 
| +            while (node.parent && node.parent.parent) | 
| node = node.parent; | 
| return node; | 
| } | 
| @@ -286,8 +298,8 @@ WebInspector.CPUProfileDataModel.prototype = { | 
| }, | 
|  | 
| /** | 
| -     * @param {function(number, !ProfilerAgent.CPUProfileNode, number)} openFrameCallback | 
| -     * @param {function(number, !ProfilerAgent.CPUProfileNode, number, number, number)} closeFrameCallback | 
| +     * @param {function(number, !WebInspector.CPUProfileNode, number)} openFrameCallback | 
| +     * @param {function(number, !WebInspector.CPUProfileNode, number, number, number)} closeFrameCallback | 
| * @param {number=} startTime | 
| * @param {number=} stopTime | 
| */ | 
| @@ -358,7 +370,7 @@ WebInspector.CPUProfileDataModel.prototype = { | 
| var start = stackStartTimes[stackTop]; | 
| var duration = sampleTime - start; | 
| stackChildrenDuration[stackTop - 1] += duration; | 
| -                closeFrameCallback(prevNode.depth, prevNode, start, duration, duration - stackChildrenDuration[stackTop]); | 
| +                closeFrameCallback(prevNode.depth, /** @type {!WebInspector.CPUProfileNode} */(prevNode), start, duration, duration - stackChildrenDuration[stackTop]); | 
| --stackTop; | 
| if (node.depth === prevNode.depth) { | 
| stackNodes.push(node); | 
| @@ -390,18 +402,19 @@ WebInspector.CPUProfileDataModel.prototype = { | 
| var start = stackStartTimes[stackTop]; | 
| var duration = sampleTime - start; | 
| stackChildrenDuration[stackTop - 1] += duration; | 
| -            closeFrameCallback(node.depth, node, start, duration, duration - stackChildrenDuration[stackTop]); | 
| +            closeFrameCallback(node.depth, /** @type {!WebInspector.CPUProfileNode} */(node), start, duration, duration - stackChildrenDuration[stackTop]); | 
| --stackTop; | 
| } | 
| }, | 
|  | 
| /** | 
| * @param {number} index | 
| -     * @return {!ProfilerAgent.CPUProfileNode} | 
| +     * @return {!WebInspector.CPUProfileNode} | 
| */ | 
| nodeByIndex: function(index) | 
| { | 
| return this._idToNode[this.samples[index]]; | 
| -    } | 
| +    }, | 
|  | 
| +    __proto__: WebInspector.ProfileTreeModel.prototype | 
| } | 
|  |