| Index: Source/devtools/front_end/CountersGraph.js
|
| diff --git a/Source/devtools/front_end/CountersGraph.js b/Source/devtools/front_end/CountersGraph.js
|
| index 02e96d2c719e3b832cc1e8573aeaf7aae8941dea..a36aeaf6a3a9573e1de5277eaa3d305822e5482c 100644
|
| --- a/Source/devtools/front_end/CountersGraph.js
|
| +++ b/Source/devtools/front_end/CountersGraph.js
|
| @@ -1,5 +1,5 @@
|
| /*
|
| - * Copyright (C) 2013 Google Inc. All rights reserved.
|
| + * Copyright (C) 2012 Google Inc. All rights reserved.
|
| *
|
| * Redistribution and use in source and binary forms, with or without
|
| * modification, are permitted provided that the following conditions are
|
| @@ -30,123 +30,240 @@
|
|
|
| /**
|
| * @constructor
|
| - * @extends {WebInspector.MemoryStatistics}
|
| - * @implements {WebInspector.TimelineModeView}
|
| + * @extends {WebInspector.SplitView}
|
| * @param {!WebInspector.TimelineModeViewDelegate} delegate
|
| * @param {!WebInspector.TimelineModel} model
|
| */
|
| WebInspector.CountersGraph = function(delegate, model)
|
| {
|
| - WebInspector.MemoryStatistics.call(this, delegate, model);
|
| -}
|
| + WebInspector.SplitView.call(this, true, false);
|
|
|
| -/**
|
| - * @constructor
|
| - * @extends {WebInspector.CounterUIBase}
|
| - * @param {!WebInspector.CountersGraph} memoryCountersPane
|
| - * @param {string} title
|
| - * @param {string} currentValueLabel
|
| - * @param {!string} color
|
| - * @param {!WebInspector.MemoryStatistics.Counter} counter
|
| - */
|
| -WebInspector.CounterUI = function(memoryCountersPane, title, currentValueLabel, color, counter)
|
| -{
|
| - WebInspector.CounterUIBase.call(this, memoryCountersPane, title, color, counter)
|
| - this._range = this._swatch.element.createChild("span");
|
| + this.element.id = "memory-graphs-container";
|
|
|
| - this._value = memoryCountersPane._currentValuesBar.createChild("span", "memory-counter-value");
|
| - this._value.style.color = color;
|
| - this._currentValueLabel = currentValueLabel;
|
| - this._marker = memoryCountersPane._canvasContainer.createChild("div", "memory-counter-marker");
|
| - this._marker.style.backgroundColor = color;
|
| - this.clearCurrentValueAndMarker();
|
| + this._delegate = delegate;
|
| + this._model = model;
|
| + this._calculator = new WebInspector.TimelineCalculator(this._model);
|
|
|
| - this.graphColor = color;
|
| - this.graphYValues = [];
|
| + this._graphsContainer = this.mainElement();
|
| + this._createCurrentValuesBar();
|
| + this._canvasView = new WebInspector.VBoxWithResizeCallback(this._resize.bind(this));
|
| + this._canvasView.show(this._graphsContainer);
|
| + this._canvasContainer = this._canvasView.element;
|
| + this._canvasContainer.id = "memory-graphs-canvas-container";
|
| + this._canvas = this._canvasContainer.createChild("canvas");
|
| + this._canvas.id = "memory-counters-graph";
|
| +
|
| + this._canvasContainer.addEventListener("mouseover", this._onMouseMove.bind(this), true);
|
| + this._canvasContainer.addEventListener("mousemove", this._onMouseMove.bind(this), true);
|
| + this._canvasContainer.addEventListener("mouseout", this._onMouseOut.bind(this), true);
|
| + this._canvasContainer.addEventListener("click", this._onClick.bind(this), true);
|
| + // We create extra timeline grid here to reuse its event dividers.
|
| + this._timelineGrid = new WebInspector.TimelineGrid();
|
| + this._canvasContainer.appendChild(this._timelineGrid.dividersElement);
|
| +
|
| + // Populate sidebar
|
| + this.sidebarElement().createChild("div", "sidebar-tree sidebar-tree-section").textContent = WebInspector.UIString("COUNTERS");
|
| + this._counters = [];
|
| + this._counterUI = [];
|
| }
|
|
|
| -WebInspector.CounterUI.prototype = {
|
| - reset: function()
|
| +WebInspector.CountersGraph.prototype = {
|
| + _createCurrentValuesBar: function()
|
| {
|
| - this._range.textContent = "";
|
| + this._currentValuesBar = this._graphsContainer.createChild("div");
|
| + this._currentValuesBar.id = "counter-values-bar";
|
| },
|
|
|
| /**
|
| - * @param {number} minValue
|
| - * @param {number} maxValue
|
| + * @param {string} uiName
|
| + * @param {string} uiValueTemplate
|
| + * @param {string} color
|
| + * @return {!WebInspector.CountersGraph.Counter}
|
| */
|
| - setRange: function(minValue, maxValue)
|
| + createCounter: function(uiName, uiValueTemplate, color)
|
| {
|
| - this._range.textContent = WebInspector.UIString("[%d:%d]", minValue, maxValue);
|
| + var counter = new WebInspector.CountersGraph.Counter();
|
| + this._counters.push(counter);
|
| + this._counterUI.push(new WebInspector.CountersGraph.CounterUI(this, uiName, uiValueTemplate, color, counter));
|
| + return counter;
|
| },
|
|
|
| - __proto__: WebInspector.CounterUIBase.prototype
|
| -}
|
| + reset: function()
|
| + {
|
| + for (var i = 0; i < this._counters.length; ++i) {
|
| + this._counters[i].reset();
|
| + this._counterUI[i].reset();
|
| + }
|
| + this.refresh();
|
| + },
|
|
|
| + _resize: function()
|
| + {
|
| + var parentElement = this._canvas.parentElement;
|
| + this._canvas.width = parentElement.clientWidth;
|
| + this._canvas.height = parentElement.clientHeight;
|
| + var timelinePaddingLeft = 15;
|
| + this._calculator.setDisplayWindow(timelinePaddingLeft, this._canvas.width);
|
| + this.refresh();
|
| + },
|
|
|
| -WebInspector.CountersGraph.prototype = {
|
| - _createCurrentValuesBar: function()
|
| + /**
|
| + * @param {number} startTime
|
| + * @param {number} endTime
|
| + */
|
| + setWindowTimes: function(startTime, endTime)
|
| {
|
| - this._currentValuesBar = this._graphsContainer.createChild("div");
|
| - this._currentValuesBar.id = "counter-values-bar";
|
| - this._graphsContainer.classList.add("dom-counters");
|
| + this._calculator.setWindow(startTime, endTime);
|
| + this.scheduleRefresh();
|
| },
|
|
|
| - createAllCounters: function()
|
| + scheduleRefresh: function()
|
| {
|
| - this._counters = [];
|
| - this._counterUI = [];
|
| - this._createCounter(WebInspector.UIString("Documents"), WebInspector.UIString("Documents: %d"), "#d00", "documents");
|
| - this._createCounter(WebInspector.UIString("Nodes"), WebInspector.UIString("Nodes: %d"), "#0a0", "nodes");
|
| - this._createCounter(WebInspector.UIString("Listeners"), WebInspector.UIString("Listeners: %d"), "#00d", "jsEventListeners");
|
| - if (WebInspector.experimentsSettings.gpuTimeline.isEnabled())
|
| - this._createCounter(WebInspector.UIString("GPU Memory"), WebInspector.UIString("GPU Memory [KB]: %d"), "#c0c", "gpuMemoryUsedKB");
|
| + if (this._refreshTimer)
|
| + return;
|
| + this._refreshTimer = setTimeout(this.refresh.bind(this), 300);
|
| + },
|
| +
|
| + draw: function()
|
| + {
|
| + for (var i = 0; i < this._counters.length; ++i) {
|
| + this._counters[i]._calculateVisibleIndexes(this._calculator);
|
| + this._counters[i]._calculateXValues(this._canvas.width);
|
| + }
|
| + this._clear();
|
| + this._setVerticalClip(10, this._canvas.height - 20);
|
| +
|
| + for (var i = 0; i < this._counterUI.length; i++)
|
| + this._drawGraph(this._counterUI[i]);
|
| },
|
|
|
| /**
|
| - * @param {string} uiName
|
| - * @param {string} uiValueTemplate
|
| - * @param {string} color
|
| - * @param {string} protocolName
|
| + * @param {?Event} event
|
| */
|
| - _createCounter: function(uiName, uiValueTemplate, color, protocolName)
|
| + _onClick: function(event)
|
| {
|
| - var counter = new WebInspector.MemoryStatistics.Counter(protocolName);
|
| - this._counters.push(counter);
|
| - this._counterUI.push(new WebInspector.CounterUI(this, uiName, uiValueTemplate, color, counter));
|
| + var x = event.x - this._canvasContainer.totalOffsetLeft();
|
| + var minDistance = Infinity;
|
| + var bestTime;
|
| + for (var i = 0; i < this._counterUI.length; ++i) {
|
| + var counterUI = this._counterUI[i];
|
| + if (!counterUI.counter.times.length)
|
| + continue;
|
| + var index = counterUI._recordIndexAt(x);
|
| + var distance = Math.abs(x - counterUI.counter.x[index]);
|
| + if (distance < minDistance) {
|
| + minDistance = distance;
|
| + bestTime = counterUI.counter.times[index];
|
| + }
|
| + }
|
| + if (bestTime !== undefined)
|
| + this._revealRecordAt(bestTime);
|
| },
|
|
|
| /**
|
| - * @param {!WebInspector.TimelineModel.Record} record
|
| + * @param {number} time
|
| */
|
| - addRecord: function(record)
|
| + _revealRecordAt: function(time)
|
| {
|
| - /**
|
| - * @param {!WebInspector.TimelineModel.Record} record
|
| - * @this {!WebInspector.CountersGraph}
|
| - */
|
| - function addStatistics(record)
|
| + var recordToReveal;
|
| + function findRecordToReveal(record)
|
| {
|
| - var counters = record.counters;
|
| - if (!counters)
|
| - return;
|
| - var time = record.endTime || record.startTime;
|
| - for (var i = 0; i < this._counters.length; ++i)
|
| - this._counters[i].appendSample(time, counters);
|
| + if (record.startTime <= time && time <= record.endTime) {
|
| + recordToReveal = record;
|
| + return true;
|
| + }
|
| + // If there is no record containing the time than use the latest one before that time.
|
| + if (!recordToReveal || record.endTime < time && recordToReveal.endTime < record.endTime)
|
| + recordToReveal = record;
|
| + return false;
|
| }
|
| - WebInspector.TimelineModel.forAllRecords([record], null, addStatistics.bind(this));
|
| - this.scheduleRefresh();
|
| + this._model.forAllRecords(null, findRecordToReveal);
|
| + this._delegate.selectRecord(recordToReveal);
|
| },
|
|
|
| - draw: function()
|
| + /**
|
| + * @param {?Event} event
|
| + */
|
| + _onMouseOut: function(event)
|
| + {
|
| + delete this._markerXPosition;
|
| + this._clearCurrentValueAndMarker();
|
| + },
|
| +
|
| + _clearCurrentValueAndMarker: function()
|
| {
|
| - WebInspector.MemoryStatistics.prototype.draw.call(this);
|
| for (var i = 0; i < this._counterUI.length; i++)
|
| - this._drawGraph(this._counterUI[i]);
|
| + this._counterUI[i]._clearCurrentValueAndMarker();
|
| + },
|
| +
|
| + /**
|
| + * @param {?Event} event
|
| + */
|
| + _onMouseMove: function(event)
|
| + {
|
| + var x = event.x - this._canvasContainer.totalOffsetLeft();
|
| + this._markerXPosition = x;
|
| + this._refreshCurrentValues();
|
| + },
|
| +
|
| + _refreshCurrentValues: function()
|
| + {
|
| + if (this._markerXPosition === undefined)
|
| + return;
|
| + for (var i = 0; i < this._counterUI.length; ++i)
|
| + this._counterUI[i].updateCurrentValue(this._markerXPosition);
|
| + },
|
| +
|
| + refresh: function()
|
| + {
|
| + delete this._refreshTimer;
|
| + this._timelineGrid.updateDividers(this._calculator);
|
| + this.draw();
|
| + this._refreshCurrentValues();
|
| + },
|
| +
|
| + refreshRecords: function()
|
| + {
|
| + this.reset();
|
| + var records = this._model.records();
|
| + for (var i = 0; i < records.length; ++i)
|
| + this.addRecord(records[i]);
|
| },
|
|
|
| /**
|
| - * @param {!WebInspector.CounterUIBase} counterUI
|
| + * @param {number} originY
|
| + * @param {number} height
|
| + */
|
| + _setVerticalClip: function(originY, height)
|
| + {
|
| + this._originY = originY;
|
| + this._clippedHeight = height;
|
| + },
|
| +
|
| + _clear: function()
|
| + {
|
| + var ctx = this._canvas.getContext("2d");
|
| + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
| + },
|
| +
|
| + /**
|
| + * @param {?WebInspector.TimelineModel.Record} record
|
| + * @param {string=} regex
|
| + * @param {boolean=} selectRecord
|
| + */
|
| + highlightSearchResult: function(record, regex, selectRecord)
|
| + {
|
| + },
|
| +
|
| + /**
|
| + * @param {?WebInspector.TimelineModel.Record} record
|
| + */
|
| + setSelectedRecord: function(record)
|
| + {
|
| + },
|
| +
|
| + /**
|
| + * @param {!WebInspector.CountersGraph.CounterUI} counterUI
|
| */
|
| _drawGraph: function(counterUI)
|
| {
|
| @@ -175,7 +292,7 @@ WebInspector.CountersGraph.prototype = {
|
|
|
| counterUI.setRange(minValue, maxValue);
|
|
|
| - if (!counterUI.visible)
|
| + if (!counterUI.visible())
|
| return;
|
|
|
| var yValues = counterUI.graphYValues;
|
| @@ -208,6 +325,205 @@ WebInspector.CountersGraph.prototype = {
|
| ctx.restore();
|
| },
|
|
|
| - __proto__: WebInspector.MemoryStatistics.prototype
|
| + __proto__: WebInspector.SplitView.prototype
|
| +}
|
| +
|
| +/**
|
| + * @constructor
|
| + */
|
| +WebInspector.CountersGraph.Counter = function()
|
| +{
|
| + this.times = [];
|
| + this.values = [];
|
| +}
|
| +
|
| +WebInspector.CountersGraph.Counter.prototype = {
|
| + /**
|
| + * @param {number} time
|
| + * @param {number} value
|
| + */
|
| + appendSample: function(time, value)
|
| + {
|
| + if (this.values.length && this.values.peekLast() === value)
|
| + return;
|
| + this.times.push(time);
|
| + this.values.push(value);
|
| + },
|
| +
|
| + reset: function()
|
| + {
|
| + this.times = [];
|
| + this.values = [];
|
| + },
|
| +
|
| + /**
|
| + * @param {!WebInspector.TimelineCalculator} calculator
|
| + */
|
| + _calculateVisibleIndexes: function(calculator)
|
| + {
|
| + var start = calculator.minimumBoundary();
|
| + var end = calculator.maximumBoundary();
|
| +
|
| + // Maximum index of element whose time <= start.
|
| + this._minimumIndex = Number.constrain(this.times.upperBound(start) - 1, 0, this.times.length - 1);
|
| +
|
| + // Minimum index of element whose time >= end.
|
| + this._maximumIndex = Number.constrain(this.times.lowerBound(end), 0, this.times.length - 1);
|
| +
|
| + // Current window bounds.
|
| + this._minTime = start;
|
| + this._maxTime = end;
|
| + },
|
| +
|
| + /**
|
| + * @param {number} width
|
| + */
|
| + _calculateXValues: function(width)
|
| + {
|
| + if (!this.values.length)
|
| + return;
|
| +
|
| + var xFactor = width / (this._maxTime - this._minTime);
|
| +
|
| + this.x = new Array(this.values.length);
|
| + this.x[this._minimumIndex] = 0;
|
| + for (var i = this._minimumIndex + 1; i < this._maximumIndex; i++)
|
| + this.x[i] = xFactor * (this.times[i] - this._minTime);
|
| + this.x[this._maximumIndex] = width;
|
| + }
|
| }
|
|
|
| +/**
|
| + * @constructor
|
| + * @param {!WebInspector.CountersGraph} memoryCountersPane
|
| + * @param {string} title
|
| + * @param {string} currentValueLabel
|
| + * @param {string} graphColor
|
| + * @param {!WebInspector.CountersGraph.Counter} counter
|
| + */
|
| +WebInspector.CountersGraph.CounterUI = function(memoryCountersPane, title, currentValueLabel, graphColor, counter)
|
| +{
|
| + this._memoryCountersPane = memoryCountersPane;
|
| + this.counter = counter;
|
| + var container = memoryCountersPane.sidebarElement().createChild("div", "memory-counter-sidebar-info");
|
| + var swatchColor = graphColor;
|
| + this._swatch = new WebInspector.SwatchCheckbox(WebInspector.UIString(title), swatchColor);
|
| + this._swatch.addEventListener(WebInspector.SwatchCheckbox.Events.Changed, this._toggleCounterGraph.bind(this));
|
| + container.appendChild(this._swatch.element);
|
| + this._range = this._swatch.element.createChild("span");
|
| +
|
| + this._value = memoryCountersPane._currentValuesBar.createChild("span", "memory-counter-value");
|
| + this._value.style.color = graphColor;
|
| + this.graphColor = graphColor;
|
| + this.strokeColor = graphColor;
|
| + this.graphYValues = [];
|
| +
|
| + this._currentValueLabel = currentValueLabel;
|
| + this._marker = memoryCountersPane._canvasContainer.createChild("div", "memory-counter-marker");
|
| + this._marker.style.backgroundColor = graphColor;
|
| + this._clearCurrentValueAndMarker();
|
| +}
|
| +
|
| +WebInspector.CountersGraph.CounterUI.prototype = {
|
| + reset: function()
|
| + {
|
| + this._range.textContent = "";
|
| + },
|
| +
|
| + /**
|
| + * @param {number} minValue
|
| + * @param {number} maxValue
|
| + */
|
| + setRange: function(minValue, maxValue)
|
| + {
|
| + this._range.textContent = WebInspector.UIString("[%d:%d]", minValue, maxValue);
|
| + },
|
| +
|
| + _toggleCounterGraph: function(event)
|
| + {
|
| + this._value.classList.toggle("hidden", !this._swatch.checked);
|
| + this._memoryCountersPane.refresh();
|
| + },
|
| +
|
| + /**
|
| + * @param {number} x
|
| + * @return {number}
|
| + */
|
| + _recordIndexAt: function(x)
|
| + {
|
| + return this.counter.x.upperBound(x, null, this.counter._minimumIndex + 1, this.counter._maximumIndex + 1) - 1;
|
| + },
|
| +
|
| + /**
|
| + * @param {number} x
|
| + */
|
| + updateCurrentValue: function(x)
|
| + {
|
| + if (!this.visible() || !this.counter.values.length)
|
| + return;
|
| + var index = this._recordIndexAt(x);
|
| + this._value.textContent = WebInspector.UIString(this._currentValueLabel, this.counter.values[index]);
|
| + var y = this.graphYValues[index];
|
| + this._marker.style.left = x + "px";
|
| + this._marker.style.top = y + "px";
|
| + this._marker.classList.remove("hidden");
|
| + },
|
| +
|
| + _clearCurrentValueAndMarker: function()
|
| + {
|
| + this._value.textContent = "";
|
| + this._marker.classList.add("hidden");
|
| + },
|
| +
|
| + /**
|
| + * @return {boolean}
|
| + */
|
| + visible: function()
|
| + {
|
| + return this._swatch.checked;
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * @constructor
|
| + * @extends {WebInspector.Object}
|
| + */
|
| +WebInspector.SwatchCheckbox = function(title, color)
|
| +{
|
| + this.element = document.createElement("div");
|
| + this._swatch = this.element.createChild("div", "swatch");
|
| + this.element.createChild("span", "title").textContent = title;
|
| + this._color = color;
|
| + this.checked = true;
|
| +
|
| + this.element.addEventListener("click", this._toggleCheckbox.bind(this), true);
|
| +}
|
| +
|
| +WebInspector.SwatchCheckbox.Events = {
|
| + Changed: "Changed"
|
| +}
|
| +
|
| +WebInspector.SwatchCheckbox.prototype = {
|
| + get checked()
|
| + {
|
| + return this._checked;
|
| + },
|
| +
|
| + set checked(v)
|
| + {
|
| + this._checked = v;
|
| + if (this._checked)
|
| + this._swatch.style.backgroundColor = this._color;
|
| + else
|
| + this._swatch.style.backgroundColor = "";
|
| + },
|
| +
|
| + _toggleCheckbox: function(event)
|
| + {
|
| + this.checked = !this.checked;
|
| + this.dispatchEventToListeners(WebInspector.SwatchCheckbox.Events.Changed);
|
| + },
|
| +
|
| + __proto__: WebInspector.Object.prototype
|
| +}
|
|
|