Added MacOS SDK
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
WI.TimelineRecordFrame = class TimelineRecordFrame extends WI.Object
|
||||
{
|
||||
constructor(graphDataSource, record)
|
||||
{
|
||||
super();
|
||||
|
||||
this._element = document.createElement("div");
|
||||
this._element.classList.add("timeline-record-frame");
|
||||
|
||||
this._graphDataSource = graphDataSource;
|
||||
this._record = record || null;
|
||||
this._filtered = false;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get element()
|
||||
{
|
||||
return this._element;
|
||||
}
|
||||
|
||||
get record()
|
||||
{
|
||||
return this._record;
|
||||
}
|
||||
|
||||
set record(record)
|
||||
{
|
||||
this._record = record;
|
||||
}
|
||||
|
||||
get selected()
|
||||
{
|
||||
return this._element.classList.contains("selected");
|
||||
}
|
||||
|
||||
set selected(x)
|
||||
{
|
||||
if (this.selected === x)
|
||||
return;
|
||||
|
||||
this._element.classList.toggle("selected");
|
||||
}
|
||||
|
||||
get filtered()
|
||||
{
|
||||
return this._filtered;
|
||||
}
|
||||
|
||||
set filtered(x)
|
||||
{
|
||||
if (this._filtered === x)
|
||||
return;
|
||||
|
||||
this._filtered = x;
|
||||
this._element.classList.toggle("filtered");
|
||||
}
|
||||
|
||||
refresh(graphDataSource)
|
||||
{
|
||||
if (!this._record)
|
||||
return false;
|
||||
|
||||
var frameIndex = this._record.frameIndex;
|
||||
var graphStartFrameIndex = Math.floor(graphDataSource.startTime);
|
||||
var graphEndFrameIndex = graphDataSource.endTime;
|
||||
|
||||
// If this frame is completely before or after the bounds of the graph, return early.
|
||||
if (frameIndex < graphStartFrameIndex || frameIndex > graphEndFrameIndex)
|
||||
return false;
|
||||
|
||||
this._element.style.width = (1 / graphDataSource.timelineOverview.secondsPerPixel) + "px";
|
||||
|
||||
var graphDuration = graphDataSource.endTime - graphDataSource.startTime;
|
||||
let recordPosition = (frameIndex - graphDataSource.startTime) / graphDuration;
|
||||
|
||||
let property = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL ? "right" : "left";
|
||||
this._updateElementPosition(this._element, recordPosition, property);
|
||||
|
||||
this._updateChildElements(graphDataSource);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_calculateFrameDisplayData(graphDataSource)
|
||||
{
|
||||
var secondsPerBlock = (graphDataSource.graphHeightSeconds / graphDataSource.height) * WI.TimelineRecordFrame.MinimumHeightPixels;
|
||||
var segments = [];
|
||||
var invisibleSegments = [];
|
||||
var currentSegment = null;
|
||||
|
||||
function updateDurationRemainder(segment)
|
||||
{
|
||||
if (segment.duration <= secondsPerBlock) {
|
||||
segment.remainder = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var roundedDuration = Math.roundTo(segment.duration, secondsPerBlock);
|
||||
segment.remainder = Math.max(segment.duration - roundedDuration, 0);
|
||||
}
|
||||
|
||||
function pushCurrentSegment()
|
||||
{
|
||||
updateDurationRemainder(currentSegment);
|
||||
segments.push(currentSegment);
|
||||
if (currentSegment.duration < secondsPerBlock)
|
||||
invisibleSegments.push({segment: currentSegment, index: segments.length - 1});
|
||||
|
||||
currentSegment = null;
|
||||
}
|
||||
|
||||
// Frame segments aren't shown at arbitrary pixel heights, but are divided into blocks of pixels. One block
|
||||
// represents the minimum displayable duration of a rendering frame, in seconds. Contiguous tasks less than a
|
||||
// block high are grouped until the minimum is met, or a task meeting the minimum is found. The group is then
|
||||
// added to the list of segment candidates. Large tasks (one block or more) are not grouped with other tasks
|
||||
// and are simply added to the candidate list.
|
||||
for (var key in WI.RenderingFrameTimelineRecord.TaskType) {
|
||||
var taskType = WI.RenderingFrameTimelineRecord.TaskType[key];
|
||||
var duration = this._record.durationForTask(taskType);
|
||||
if (duration === 0)
|
||||
continue;
|
||||
|
||||
if (currentSegment && duration >= secondsPerBlock)
|
||||
pushCurrentSegment();
|
||||
|
||||
if (!currentSegment)
|
||||
currentSegment = {taskType: null, longestTaskDuration: 0, duration: 0, remainder: 0};
|
||||
|
||||
currentSegment.duration += duration;
|
||||
if (duration > currentSegment.longestTaskDuration) {
|
||||
currentSegment.taskType = taskType;
|
||||
currentSegment.longestTaskDuration = duration;
|
||||
}
|
||||
|
||||
if (currentSegment.duration >= secondsPerBlock)
|
||||
pushCurrentSegment();
|
||||
}
|
||||
|
||||
if (currentSegment)
|
||||
pushCurrentSegment();
|
||||
|
||||
// A frame consisting of a single segment is always visible.
|
||||
if (segments.length === 1) {
|
||||
segments[0].duration = Math.max(segments[0].duration, secondsPerBlock);
|
||||
invisibleSegments = [];
|
||||
}
|
||||
|
||||
// After grouping sub-block tasks, a second pass is needed to handle those groups that are still beneath the
|
||||
// minimum displayable duration. Each sub-block task has one or two adjacent display segments greater than one
|
||||
// block. The rounded-off time from these tasks is added to the sub-block, if it's sufficient to create a full
|
||||
// block. Failing that, the task is merged with an adjacent segment.
|
||||
invisibleSegments.sort(function(a, b) { return a.segment.duration - b.segment.duration; });
|
||||
|
||||
for (var item of invisibleSegments) {
|
||||
var segment = item.segment;
|
||||
var previousSegment = item.index > 0 ? segments[item.index - 1] : null;
|
||||
var nextSegment = item.index < segments.length - 1 ? segments[item.index + 1] : null;
|
||||
console.assert(previousSegment || nextSegment, "Invisible segment should have at least one adjacent visible segment.");
|
||||
|
||||
// Try to increase the segment's size to exactly one block, by taking subblock time from neighboring segments.
|
||||
// If there are two neighbors, the one with greater subblock duration is borrowed from first.
|
||||
var adjacentSegments;
|
||||
var availableDuration;
|
||||
if (previousSegment && nextSegment) {
|
||||
adjacentSegments = previousSegment.remainder > nextSegment.remainder ? [previousSegment, nextSegment] : [nextSegment, previousSegment];
|
||||
availableDuration = previousSegment.remainder + nextSegment.remainder;
|
||||
} else {
|
||||
adjacentSegments = [previousSegment || nextSegment];
|
||||
availableDuration = adjacentSegments[0].remainder;
|
||||
}
|
||||
|
||||
if (availableDuration < (secondsPerBlock - segment.duration)) {
|
||||
// Merge with largest adjacent segment.
|
||||
var targetSegment;
|
||||
if (previousSegment && nextSegment)
|
||||
targetSegment = previousSegment.duration > nextSegment.duration ? previousSegment : nextSegment;
|
||||
else
|
||||
targetSegment = previousSegment || nextSegment;
|
||||
|
||||
targetSegment.duration += segment.duration;
|
||||
updateDurationRemainder(targetSegment);
|
||||
continue;
|
||||
}
|
||||
|
||||
adjacentSegments.forEach(function(adjacentSegment) {
|
||||
if (segment.duration >= secondsPerBlock)
|
||||
return;
|
||||
var remainder = Math.min(secondsPerBlock - segment.duration, adjacentSegment.remainder);
|
||||
segment.duration += remainder;
|
||||
adjacentSegment.remainder -= remainder;
|
||||
});
|
||||
}
|
||||
|
||||
// Round visible segments to the nearest block, and compute the rounded frame duration.
|
||||
var frameDuration = 0;
|
||||
segments = segments.filter(function(segment) {
|
||||
if (segment.duration < secondsPerBlock)
|
||||
return false;
|
||||
segment.duration = Math.roundTo(segment.duration, secondsPerBlock);
|
||||
frameDuration += segment.duration;
|
||||
return true;
|
||||
});
|
||||
|
||||
return {frameDuration, segments};
|
||||
}
|
||||
|
||||
_updateChildElements(graphDataSource)
|
||||
{
|
||||
this._element.removeChildren();
|
||||
|
||||
console.assert(this._record);
|
||||
if (!this._record)
|
||||
return;
|
||||
|
||||
if (graphDataSource.graphHeightSeconds === 0)
|
||||
return;
|
||||
|
||||
var frameElement = document.createElement("div");
|
||||
frameElement.classList.add("frame");
|
||||
this._element.appendChild(frameElement);
|
||||
|
||||
// Display data must be recalculated when the overview graph's vertical axis changes.
|
||||
if (this._record.__displayData && this._record.__displayData.graphHeightSeconds !== graphDataSource.graphHeightSeconds)
|
||||
this._record.__displayData = null;
|
||||
|
||||
if (!this._record.__displayData) {
|
||||
this._record.__displayData = this._calculateFrameDisplayData(graphDataSource);
|
||||
this._record.__displayData.graphHeightSeconds = graphDataSource.graphHeightSeconds;
|
||||
}
|
||||
|
||||
var frameHeight = this._record.__displayData.frameDuration / graphDataSource.graphHeightSeconds;
|
||||
if (frameHeight >= 0.95)
|
||||
this._element.classList.add("tall");
|
||||
else
|
||||
this._element.classList.remove("tall");
|
||||
|
||||
this._updateElementPosition(frameElement, frameHeight, "height");
|
||||
|
||||
for (var segment of this._record.__displayData.segments) {
|
||||
var element = document.createElement("div");
|
||||
this._updateElementPosition(element, segment.duration / this._record.__displayData.frameDuration, "height");
|
||||
element.classList.add("duration", segment.taskType);
|
||||
frameElement.insertBefore(element, frameElement.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
_updateElementPosition(element, newPosition, property)
|
||||
{
|
||||
newPosition *= 100;
|
||||
|
||||
let newPositionAprox = Math.round(newPosition * 100);
|
||||
let currentPositionAprox = Math.round(parseFloat(element.style[property]) * 100);
|
||||
if (currentPositionAprox !== newPositionAprox)
|
||||
element.style[property] = (newPositionAprox / 100) + "%";
|
||||
}
|
||||
};
|
||||
|
||||
WI.TimelineRecordFrame.MinimumHeightPixels = 3;
|
||||
WI.TimelineRecordFrame.MaximumWidthPixels = 14;
|
||||
WI.TimelineRecordFrame.MinimumWidthPixels = 4;
|
||||
Reference in New Issue
Block a user