summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/TimelineRecordBar.js
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
commit32761a6cee1d0dee366b885b7b9c777e67885688 (patch)
treed6bec92bebfb216f4126356e55518842c2f476a1 /Source/WebInspectorUI/UserInterface/TimelineRecordBar.js
parenta4e969f4965059196ca948db781e52f7cfebf19e (diff)
downloadWebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/TimelineRecordBar.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/TimelineRecordBar.js346
1 files changed, 346 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/TimelineRecordBar.js b/Source/WebInspectorUI/UserInterface/TimelineRecordBar.js
new file mode 100644
index 000000000..e04c1caa9
--- /dev/null
+++ b/Source/WebInspectorUI/UserInterface/TimelineRecordBar.js
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.TimelineRecordBar = function(records, renderMode)
+{
+ WebInspector.Object.call(this);
+
+ this._element = document.createElement("div");
+ this._element.classList.add(WebInspector.TimelineRecordBar.StyleClassName);
+
+ this.renderMode = renderMode;
+ this.records = records;
+};
+
+WebInspector.Object.addConstructorFunctions(WebInspector.TimelineRecordBar);
+
+WebInspector.TimelineRecordBar.StyleClassName = "timeline-record-bar";
+WebInspector.TimelineRecordBar.BarSegmentStyleClassName = "segment";
+WebInspector.TimelineRecordBar.InactiveStyleClassName = "inactive";
+WebInspector.TimelineRecordBar.UnfinishedStyleClassName = "unfinished";
+WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName = "has-inactive-segment";
+WebInspector.TimelineRecordBar.MinimumWidthPixels = 4;
+WebInspector.TimelineRecordBar.MinimumMarginPixels = 1;
+
+WebInspector.TimelineRecordBar.RenderMode = {
+ Normal: "timeline-record-bar-normal-render-mode",
+ InactiveOnly: "timeline-record-bar-inactive-only-render-mode",
+ ActiveOnly: "timeline-record-bar-active-only-render-mode"
+};
+
+WebInspector.TimelineRecordBar.createCombinedBars = function(records, secondsPerPixel, graphDataSource, createBarCallback)
+{
+ if (!records.length)
+ return;
+
+ var startTime = graphDataSource.startTime;
+ var currentTime = graphDataSource.currentTime;
+ var endTime = graphDataSource.endTime;
+
+ var visibleRecords = [];
+ var usesActiveStartTime = false;
+ var lastRecordType = null;
+
+ // FIXME: Do a binary search for records that fall inside start and current time.
+
+ for (var record of records) {
+ if (isNaN(record.startTime))
+ continue;
+
+ // If this bar is completely before the bounds of the graph, skip this record.
+ if (record.endTime < startTime)
+ continue;
+
+ // If this record is completely after the current time or end time, break out now.
+ // Records are sorted, so all records after this will be beyond the current or end time too.
+ if (record.startTime > currentTime || record.startTime > endTime)
+ break;
+
+ if (record.usesActiveStartTime)
+ usesActiveStartTime = true;
+
+ // If one record uses active time the rest are assumed to use it.
+ console.assert(record.usesActiveStartTime === usesActiveStartTime);
+
+ // Only a single record type is supported right now.
+ console.assert(!lastRecordType || record.type === lastRecordType);
+
+ visibleRecords.push(record);
+
+ lastRecordType = record.type;
+ }
+
+ if (!visibleRecords.length)
+ return;
+
+ if (visibleRecords.length === 1) {
+ createBarCallback(visibleRecords, WebInspector.TimelineRecordBar.RenderMode.Normal);
+ return;
+ }
+
+ function compareByActiveStartTime(a, b)
+ {
+ return a.activeStartTime - b.activeStartTime;
+ }
+
+ var minimumDuration = secondsPerPixel * WebInspector.TimelineRecordBar.MinimumWidthPixels;
+ var minimumMargin = secondsPerPixel * WebInspector.TimelineRecordBar.MinimumMarginPixels;
+
+ if (usesActiveStartTime) {
+ var inactiveStartTime = NaN;
+ var inactiveEndTime = NaN;
+ var inactiveRecords = [];
+
+ for (var record of visibleRecords) {
+ // Check if the previous record is far enough away to create the inactive bar.
+ if (!isNaN(inactiveStartTime) && inactiveStartTime + Math.max(inactiveEndTime - inactiveStartTime, minimumDuration) + minimumMargin <= record.startTime) {
+ createBarCallback(inactiveRecords, WebInspector.TimelineRecordBar.RenderMode.InactiveOnly);
+ inactiveRecords = [];
+ inactiveStartTime = NaN;
+ inactiveEndTime = NaN;
+ }
+
+ // If this is a new bar, peg the start time.
+ if (isNaN(inactiveStartTime))
+ inactiveStartTime = record.startTime;
+
+ // Update the end time to be the maximum we encounter. inactiveEndTime might be NaN, so "|| 0" to prevent Math.max from returning NaN.
+ inactiveEndTime = Math.max(inactiveEndTime || 0, record.activeStartTime);
+
+ inactiveRecords.push(record);
+ }
+
+ // Create the inactive bar for the last record if needed.
+ if (!isNaN(inactiveStartTime))
+ createBarCallback(inactiveRecords, WebInspector.TimelineRecordBar.RenderMode.InactiveOnly);
+
+ visibleRecords.sort(compareByActiveStartTime);
+ }
+
+ lastRecordType = null;
+
+ var activeStartTime = NaN;
+ var activeEndTime = NaN;
+ var activeRecords = [];
+
+ var startTimeProperty = usesActiveStartTime ? "activeStartTime" : "startTime";
+
+ for (var record of visibleRecords) {
+ // Check if the previous record is far enough away to create the active bar. We also create it now if the current record has no active state time.
+ if (!isNaN(activeStartTime) && (activeStartTime + Math.max(activeEndTime - activeStartTime, minimumDuration) + minimumMargin <= record[startTimeProperty]
+ || (isNaN(record[startTimeProperty]) && !isNaN(activeEndTime)))) {
+ createBarCallback(activeRecords, WebInspector.TimelineRecordBar.RenderMode.ActiveOnly);
+ activeRecords = [];
+ activeStartTime = NaN;
+ activeEndTime = NaN;
+ }
+
+ if (isNaN(record[startTimeProperty]))
+ continue;
+
+ // If this is a new bar, peg the start time.
+ if (isNaN(activeStartTime))
+ activeStartTime = record[startTimeProperty];
+
+ // Update the end time to be the maximum we encounter. activeEndTime might be NaN, so "|| 0" to prevent Math.max from returning NaN.
+ if (!isNaN(record.endTime))
+ activeEndTime = Math.max(activeEndTime || 0, record.endTime);
+
+ activeRecords.push(record);
+ }
+
+ // Create the active bar for the last record if needed.
+ if (!isNaN(activeStartTime))
+ createBarCallback(activeRecords, WebInspector.TimelineRecordBar.RenderMode.ActiveOnly);
+};
+
+WebInspector.TimelineRecordBar.prototype = {
+ constructor: WebInspector.TimelineRecordBar,
+ __proto__: WebInspector.Object.prototype,
+
+ // Public
+
+ get element()
+ {
+ return this._element;
+ },
+
+ get renderMode()
+ {
+ return this._renderMode;
+ },
+
+ set renderMode(renderMode)
+ {
+ this._renderMode = renderMode || WebInspector.TimelineRecordBar.RenderMode.Normal;
+ },
+
+ get records()
+ {
+ return this._records;
+ },
+
+ set records(records)
+ {
+ if (this._records && this._records.length)
+ this._element.classList.remove(this._records[0].type);
+
+ records = records || [];
+
+ if (!(records instanceof Array))
+ records = [records];
+
+ this._records = records;
+
+ // Assume all records are the same type.
+ if (this._records.length)
+ this._element.classList.add(this._records[0].type);
+ },
+
+ refresh: function(graphDataSource)
+ {
+ if (!this._records || !this._records.length)
+ return false;
+
+ var firstRecord = this._records[0];
+ var barStartTime = firstRecord.startTime;
+
+ // If this bar has no time info, return early.
+ if (isNaN(barStartTime))
+ return false;
+
+ var graphStartTime = graphDataSource.startTime;
+ var graphEndTime = graphDataSource.endTime;
+ var graphCurrentTime = graphDataSource.currentTime;
+
+ var barEndTime = this._records.reduce(function(previousValue, currentValue) { return Math.max(previousValue, currentValue.endTime); }, 0);
+
+ // If this bar is completely after the current time, return early.
+ if (barStartTime > graphCurrentTime)
+ return false;
+
+ // If this bar is completely before or after the bounds of the graph, return early.
+ if (barEndTime < graphStartTime || barStartTime > graphEndTime)
+ return false;
+
+ var barUnfinished = isNaN(barEndTime) || barEndTime >= graphCurrentTime;
+ if (barUnfinished)
+ barEndTime = graphCurrentTime;
+
+ var graphDuration = graphEndTime - graphStartTime;
+
+ this._element.classList.toggle(WebInspector.TimelineRecordBar.UnfinishedStyleClassName, barUnfinished);
+
+ var newBarLeftPosition = (barStartTime - graphStartTime) / graphDuration;
+ this._updateElementPosition(this._element, newBarLeftPosition, "left");
+
+ var newBarWidth = ((barEndTime - graphStartTime) / graphDuration) - newBarLeftPosition;
+ this._updateElementPosition(this._element, newBarWidth, "width");
+
+ if (!this._activeBarElement && this._renderMode !== WebInspector.TimelineRecordBar.RenderMode.InactiveOnly) {
+ this._activeBarElement = document.createElement("div");
+ this._activeBarElement.classList.add(WebInspector.TimelineRecordBar.BarSegmentStyleClassName);
+ }
+
+ if (!firstRecord.usesActiveStartTime) {
+ this._element.classList.remove(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
+
+ if (this._inactiveBarElement)
+ this._inactiveBarElement.remove();
+
+ if (this._renderMode === WebInspector.TimelineRecordBar.RenderMode.InactiveOnly) {
+ if (this._activeBarElement)
+ this._activeBarElement.remove();
+
+ return false;
+ }
+
+ // If this TimelineRecordBar is reused and had an inactive bar previously, clean it up.
+ this._activeBarElement.style.removeProperty("left");
+ this._activeBarElement.style.removeProperty("width");
+
+ if (!this._activeBarElement.parentNode)
+ this._element.appendChild(this._activeBarElement);
+
+ return true;
+ }
+
+ this._element.classList.add(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
+
+ // Find the earliest active start time for active only rendering, and the latest for the other modes.
+ // This matches the values that TimelineRecordBar.createCombinedBars uses when combining.
+ if (this._renderMode === WebInspector.TimelineRecordBar.RenderMode.ActiveOnly)
+ var barActiveStartTime = this._records.reduce(function(previousValue, currentValue) { return Math.min(previousValue, currentValue.activeStartTime); }, Infinity);
+ else
+ var barActiveStartTime = this._records.reduce(function(previousValue, currentValue) { return Math.max(previousValue, currentValue.activeStartTime); }, 0);
+
+ var barDuration = barEndTime - barStartTime;
+
+ var inactiveUnfinished = isNaN(barActiveStartTime) || barActiveStartTime >= graphCurrentTime;
+ this._element.classList.toggle(WebInspector.TimelineRecordBar.UnfinishedStyleClassName, inactiveUnfinished);
+
+ if (inactiveUnfinished)
+ barActiveStartTime = graphCurrentTime;
+
+ var middlePercentage = (barActiveStartTime - barStartTime) / barDuration;
+
+ if (this._renderMode !== WebInspector.TimelineRecordBar.RenderMode.ActiveOnly) {
+ if (!this._inactiveBarElement) {
+ this._inactiveBarElement = document.createElement("div");
+ this._inactiveBarElement.classList.add(WebInspector.TimelineRecordBar.BarSegmentStyleClassName);
+ this._inactiveBarElement.classList.add(WebInspector.TimelineRecordBar.InactiveStyleClassName);
+ }
+
+ this._updateElementPosition(this._inactiveBarElement, 1 - middlePercentage, "right");
+ this._updateElementPosition(this._inactiveBarElement, middlePercentage, "width");
+
+ if (!this._inactiveBarElement.parentNode)
+ this._element.insertBefore(this._inactiveBarElement, this._element.firstChild);
+ }
+
+ if (!inactiveUnfinished && this._renderMode !== WebInspector.TimelineRecordBar.RenderMode.InactiveOnly) {
+ this._updateElementPosition(this._activeBarElement, middlePercentage, "left");
+ this._updateElementPosition(this._activeBarElement, 1 - middlePercentage, "width");
+
+ if (!this._activeBarElement.parentNode)
+ this._element.appendChild(this._activeBarElement);
+ } else if (this._activeBarElement)
+ this._activeBarElement.remove();
+
+ return true;
+ },
+
+ // Private
+
+ _updateElementPosition: function(element, newPosition, property)
+ {
+ newPosition *= 100;
+ newPosition = newPosition.toFixed(2);
+
+ var currentPosition = parseFloat(element.style[property]).toFixed(2);
+ if (currentPosition !== newPosition)
+ element.style[property] = newPosition + "%";
+ }
+};