1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 tvcm.requireStylesheet('tracing.tracks.trace_model_track');
9 tvcm.require('tvcm.measuring_stick');
10 tvcm.require('tracing.tracks.container_track');
11 tvcm.require('tracing.tracks.kernel_track');
12 tvcm.require('tracing.tracks.process_track');
13 tvcm.require('tracing.draw_helpers');
14 tvcm.require('tvcm.ui');
16 tvcm.exportTo('tracing.tracks', function() {
19 * Visualizes a Model by building ProcessTracks and
23 var TraceModelTrack = tvcm.ui.define(
24 'trace-model-track', tracing.tracks.ContainerTrack);
26 TraceModelTrack.prototype = {
28 __proto__: tracing.tracks.ContainerTrack.prototype,
30 decorate: function(viewport) {
31 tracing.tracks.ContainerTrack.prototype.decorate.call(this, viewport);
32 this.classList.add('model-track');
36 tracing.tracks.ContainerTrack.prototype.detach.call(this);
45 this.updateContents_();
48 get hasVisibleContent() {
49 return this.children.length > 0;
52 updateContents_: function() {
53 this.textContent = '';
57 this.appendKernelTrack_();
59 // Get a sorted list of processes.
60 var processes = this.model_.getAllProcesses();
61 processes.sort(tracing.trace_model.Process.compare);
63 for (var i = 0; i < processes.length; ++i) {
64 var process = processes[i];
66 var track = new tracing.tracks.ProcessTrack(this.viewport);
67 track.process = process;
68 if (!track.hasVisibleContent)
71 this.appendChild(track);
73 this.viewport_.rebuildEventToTrackMap();
76 addEventsToTrackMap: function(eventToTrackMap) {
80 var tracks = this.children;
81 for (var i = 0; i < tracks.length; ++i)
82 tracks[i].addEventsToTrackMap(eventToTrackMap);
84 if (this.instantEvents === undefined)
87 var vp = this.viewport_;
88 this.instantEvents.forEach(function(ev) {
89 eventToTrackMap.addEvent(ev, this);
93 appendKernelTrack_: function() {
94 var kernel = this.model.kernel;
95 var track = new tracing.tracks.KernelTrack(this.viewport);
96 track.kernel = this.model.kernel;
97 if (!track.hasVisibleContent)
99 this.appendChild(track);
102 drawTrack: function(type) {
103 var ctx = this.context();
105 var pixelRatio = window.devicePixelRatio || 1;
106 var bounds = this.getBoundingClientRect();
107 var canvasBounds = ctx.canvas.getBoundingClientRect();
110 ctx.translate(0, pixelRatio * (bounds.top - canvasBounds.top));
112 var dt = this.viewport.currentDisplayTransform;
113 var viewLWorld = dt.xViewToWorld(0);
114 var viewRWorld = dt.xViewToWorld(bounds.width * pixelRatio);
117 case tracing.tracks.DrawType.GRID:
118 this.viewport.drawMajorMarkLines(ctx);
119 // The model is the only thing that draws grid lines.
123 case tracing.tracks.DrawType.FLOW_ARROWS:
124 if (this.model_.flowIntervalTree.size === 0) {
129 this.drawFlowArrows_(viewLWorld, viewRWorld);
133 case tracing.tracks.DrawType.INSTANT_EVENT:
134 if (!this.model_.instantEvents ||
135 this.model_.instantEvents.length === 0)
138 tracing.drawInstantSlicesAsLines(
140 this.viewport.currentDisplayTransform,
144 this.model_.instantEvents,
149 case tracing.tracks.DrawType.MARKERS:
150 if (!this.viewport.interestRange.isEmpty) {
151 this.viewport.interestRange.draw(ctx, viewLWorld, viewRWorld);
152 this.viewport.interestRange.drawIndicators(
153 ctx, viewLWorld, viewRWorld);
160 tracing.tracks.ContainerTrack.prototype.drawTrack.call(this, type);
163 drawFlowArrows_: function(viewLWorld, viewRWorld) {
164 var ctx = this.context();
165 var dt = this.viewport.currentDisplayTransform;
166 dt.applyTransformToCanvas(ctx);
168 var pixWidth = dt.xViewVectorToWorld(1);
170 ctx.strokeStyle = 'rgba(0, 0, 0, 0.4)';
171 ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
172 ctx.lineWidth = pixWidth > 1.0 ? 1 : pixWidth;
175 this.model_.flowIntervalTree.findIntersection(viewLWorld, viewRWorld);
177 var minWidth = 2 * pixWidth;
178 var canvasBounds = ctx.canvas.getBoundingClientRect();
180 for (var i = 0; i < events.length; ++i) {
181 var startEvent = events[i][0];
182 var endEvent = events[i][1];
184 // Skip lines that will be, essentially, vertical.
185 var distance = endEvent.start - startEvent.start;
186 if (distance <= minWidth)
189 this.drawFlowArrowBetween_(
190 ctx, startEvent, endEvent, canvasBounds, pixWidth);
194 drawFlowArrowBetween_: function(ctx, startEvent, endEvent,
195 canvasBounds, pixWidth) {
196 var pixelRatio = window.devicePixelRatio || 1;
198 var startTrack = this.viewport.trackForEvent(startEvent);
199 var endTrack = this.viewport.trackForEvent(endEvent);
201 var startBounds = startTrack.getBoundingClientRect();
202 var endBounds = endTrack.getBoundingClientRect();
204 var startSize = startBounds.left + startBounds.top +
205 startBounds.bottom + startBounds.right;
206 var endSize = endBounds.left + endBounds.top +
207 endBounds.bottom + endBounds.right;
208 // Nothing to do if both ends of the track are collapsed.
209 if (startSize === 0 && endSize === 0)
212 var startY = this.calculateTrackY_(startTrack, canvasBounds);
213 var endY = this.calculateTrackY_(endTrack, canvasBounds);
215 var pixelStartY = pixelRatio * startY;
216 var pixelEndY = pixelRatio * endY;
217 var half = (endEvent.start - startEvent.start) / 2;
220 ctx.moveTo(startEvent.start, pixelStartY);
222 startEvent.start + half, pixelStartY,
223 startEvent.start + half, pixelEndY,
224 endEvent.start, pixelEndY);
227 var arrowWidth = 5 * pixWidth * pixelRatio;
228 var distance = endEvent.start - startEvent.start;
229 if (distance <= (2 * arrowWidth))
232 var tipX = endEvent.start;
233 var tipY = pixelEndY;
234 var arrowHeight = (endBounds.height / 4) * pixelRatio;
235 tracing.drawTriangle(ctx,
237 tipX - arrowWidth, tipY - arrowHeight,
238 tipX - arrowWidth, tipY + arrowHeight);
242 calculateTrackY_: function(track, canvasBounds) {
243 var bounds = track.getBoundingClientRect();
244 var size = bounds.left + bounds.top + bounds.bottom + bounds.right;
246 return this.calculateTrackY_(track.parentNode, canvasBounds);
248 return bounds.top - canvasBounds.top + (bounds.height / 2);
251 addIntersectingItemsInRangeToSelectionInWorldSpace: function(
252 loWX, hiWX, viewPixWidthWorld, selection) {
253 function onPickHit(instantEvent) {
254 selection.push(instantEvent);
256 tvcm.iterateOverIntersectingIntervals(this.model_.instantEvents,
257 function(x) { return x.start; },
258 function(x) { return x.duration; },
260 onPickHit.bind(this));
262 tracing.tracks.ContainerTrack.prototype.
263 addIntersectingItemsInRangeToSelectionInWorldSpace.
264 apply(this, arguments);
267 addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY,
269 this.addClosestInstantEventToSelection(this.model_.instantEvents,
270 worldX, worldMaxDist, selection);
271 tracing.tracks.ContainerTrack.prototype.addClosestEventToSelection.
272 apply(this, arguments);
277 TraceModelTrack: TraceModelTrack