| Index: chrome/browser/resources/tracing/timeline.js
|
| diff --git a/chrome/browser/resources/tracing/timeline.js b/chrome/browser/resources/tracing/timeline.js
|
| index e38ec41671f64122dcf8e997cea21d0bb39454d8..da9a3ab2843a382b9567b5815414672962a1654d 100644
|
| --- a/chrome/browser/resources/tracing/timeline.js
|
| +++ b/chrome/browser/resources/tracing/timeline.js
|
| @@ -232,6 +232,156 @@ cr.define('tracing', function() {
|
| }
|
| };
|
|
|
| + function TimelineSelectionSliceHit(track, slice) {
|
| + this.track = track;
|
| + this.slice = slice;
|
| + }
|
| + TimelineSelectionSliceHit.prototype = {
|
| + get selected() {
|
| + return this.slice.selected;
|
| + },
|
| + set selected(v) {
|
| + this.slice.selected = v;
|
| + }
|
| + };
|
| +
|
| + function TimelineSelectionCounterSampleHit(track, counter, sampleIndex) {
|
| + this.track = track;
|
| + this.counter = counter;
|
| + this.sampleIndex = sampleIndex;
|
| + }
|
| + TimelineSelectionCounterSampleHit.prototype = {
|
| + get selected() {
|
| + return this.track.selectedSamples[this.sampleIndex] == true;
|
| + },
|
| + set selected(v) {
|
| + if (v)
|
| + this.track.selectedSamples[this.sampleIndex] = true;
|
| + else
|
| + this.track.selectedSamples[this.sampleIndex] = false;
|
| + this.track.invalidate();
|
| + }
|
| + };
|
| +
|
| +
|
| + /**
|
| + * Represents a selection within a Timeline and its associated set of tracks.
|
| + * @constructor
|
| + */
|
| + function TimelineSelection() {
|
| + this.range_dirty_ = true;
|
| + this.range_ = {};
|
| + this.length_ = 0;
|
| + }
|
| + TimelineSelection.prototype = {
|
| + __proto__: Object.prototype,
|
| +
|
| + get range() {
|
| + if (this.range_dirty_) {
|
| + var wmin = Infinity;
|
| + var wmax = -wmin;
|
| + for (var i = 0; i < this.length_; i++) {
|
| + var hit = this[i];
|
| + if (hit.slice) {
|
| + wmin = Math.min(wmin, hit.slice.start);
|
| + wmax = Math.max(wmax, hit.slice.end);
|
| + }
|
| + }
|
| + this.range_ = {
|
| + min: wmin,
|
| + max: wmax
|
| + };
|
| + this.range_dirty_ = false;
|
| + }
|
| + return this.range_;
|
| + },
|
| +
|
| + get duration() {
|
| + return this.range.max - this.range.min;
|
| + },
|
| +
|
| + get length() {
|
| + return this.length_;
|
| + },
|
| +
|
| + clear: function() {
|
| + for (var i = 0; i < this.length_; ++i)
|
| + delete this[i];
|
| + this.length_ = 0;
|
| + this.range_dirty_ = true;
|
| + },
|
| +
|
| + push_: function(hit) {
|
| + this[this.length_++] = hit;
|
| + this.range_dirty_ = true;
|
| + return hit;
|
| + },
|
| +
|
| + addSlice: function(track, slice) {
|
| + return this.push_(new TimelineSelectionSliceHit(track, slice));
|
| + },
|
| +
|
| + addCounterSample: function(track, counter, sampleIndex) {
|
| + return this.push_(
|
| + new TimelineSelectionCounterSampleHit(
|
| + track, counter, sampleIndex));
|
| + },
|
| +
|
| + subSelection: function(index, count) {
|
| + count = count || 1;
|
| +
|
| + var selection = new TimelineSelection();
|
| + selection.range_dirty_ = true;
|
| + if (index < 0 || index + count > this.length_)
|
| + throw 'Index out of bounds';
|
| +
|
| + for (var i = index; i < index + count; i++)
|
| + selection.push_(this[i]);
|
| +
|
| + return selection;
|
| + },
|
| +
|
| + getCounterSampleHits: function() {
|
| + var selection = new TimelineSelection();
|
| + for (var i = 0; i < this.length_; i++)
|
| + if (this[i] instanceof TimelineSelectionCounterSampleHit)
|
| + selection.push_(this[i]);
|
| + return selection;
|
| + },
|
| +
|
| + getSliceHits: function() {
|
| + var selection = new TimelineSelection();
|
| + for (var i = 0; i < this.length_; i++)
|
| + if (this[i] instanceof TimelineSelectionSliceHit)
|
| + selection.push_(this[i]);
|
| + return selection;
|
| + },
|
| +
|
| + map: function(fn) {
|
| + for (var i = 0; i < this.length_; i++)
|
| + fn(this[i]);
|
| + },
|
| +
|
| + /**
|
| + * Helper for selection previous or next.
|
| + * @param {boolean} forwardp If true, select one forward (next).
|
| + * Else, select previous.
|
| + * @return {boolean} true if current selection changed.
|
| + */
|
| + getShiftedSelection: function(offset) {
|
| + var newSelection = new TimelineSelection();
|
| + for (var i = 0; i < this.length_; i++) {
|
| + var hit = this[i];
|
| + hit.track.addItemNearToProvidedHitToSelection(
|
| + hit, offset, newSelection);
|
| + }
|
| +
|
| + if (newSelection.length == 0)
|
| + return undefined;
|
| + return newSelection;
|
| + },
|
| + };
|
| +
|
| /**
|
| * Renders a TimelineModel into a div element, making one
|
| * TimelineTrack for each subrow in each thread of the model, managing
|
| @@ -270,7 +420,7 @@ cr.define('tracing', function() {
|
|
|
| this.lastMouseViewPos_ = {x: 0, y: 0};
|
|
|
| - this.selection_ = [];
|
| + this.selection_ = new TimelineSelection();
|
| },
|
|
|
| /**
|
| @@ -430,17 +580,15 @@ cr.define('tracing', function() {
|
| },
|
|
|
| /**
|
| + * @param {TimelineFilter} filter The filter to use for finding matches.
|
| + * @param {TimelineSelection} selection The selection to add matches to.
|
| * @return {Array} An array of objects that match the provided
|
| * TimelineFilter.
|
| */
|
| - findAllObjectsMatchingFilter: function(filter) {
|
| - var hits = [];
|
| - for (var i = 0; i < this.tracks_.children.length; ++i) {
|
| - var trackHits =
|
| - this.tracks_.children[i].findAllObjectsMatchingFilter(filter);
|
| - Array.prototype.push.apply(hits, trackHits);
|
| - }
|
| - return hits;
|
| + addAllObjectsMatchingFilterToSelection: function(filter, selection) {
|
| + for (var i = 0; i < this.tracks_.children.length; ++i)
|
| + this.tracks_.children[i].addAllObjectsMatchingFilterToSelection(
|
| + filter, selection);
|
| },
|
|
|
| /**
|
| @@ -526,12 +674,21 @@ cr.define('tracing', function() {
|
| onKeydown_: function(e) {
|
| if (!this.listenToKeys_)
|
| return;
|
| + var sel;
|
| switch (e.keyCode) {
|
| case 37: // left arrow
|
| - this.selectPrevious_(e);
|
| + sel = this.selection.getShiftedSelection(-1);
|
| + if (sel) {
|
| + this.setSelectionAndMakeVisible(sel);
|
| + e.preventDefault();
|
| + }
|
| break;
|
| case 39: // right arrow
|
| - this.selectNext_(e);
|
| + sel = this.selection.getShiftedSelection(1);
|
| + if (sel) {
|
| + this.setSelectionAndMakeVisible(sel);
|
| + e.preventDefault();
|
| + }
|
| break;
|
| case 9: // TAB
|
| if (this.focusElement.tabIndex == -1) {
|
| @@ -560,60 +717,6 @@ cr.define('tracing', function() {
|
| vp.xPanWorldPosToViewPos(curCenterW, curMouseV, viewWidth);
|
| },
|
|
|
| - /** Select the next slice on the timeline. Applies to each track. */
|
| - selectNext_: function(e) {
|
| - if (this.selectAdjoining_(true))
|
| - e.preventDefault();
|
| - },
|
| -
|
| - /** Select the previous slice on the timeline. Applies to each track. */
|
| - selectPrevious_: function(e) {
|
| - if (this.selectAdjoining_(false))
|
| - e.preventDefault();
|
| - },
|
| -
|
| - /**
|
| - * Helper for selection previous or next.
|
| - * @param {boolean} forwardp If true, select one forward (next).
|
| - * Else, select previous.
|
| - * @return {boolean} true if current selection changed.
|
| - */
|
| - selectAdjoining_: function(forwardp) {
|
| - var i, track, slice, adjoining;
|
| - var selection = [];
|
| - var minTime = Number.MAX_VALUE;
|
| - var maxTime = -Number.MAX_VALUE;
|
| - // Try and select next.
|
| - for (i = 0; i < this.selection_.length; i++) {
|
| - adjoining = undefined;
|
| - track = this.selection_[i].track;
|
| - slice = this.selection_[i].slice;
|
| - if (slice) {
|
| - if (forwardp)
|
| - adjoining = track.pickNext(slice);
|
| - else
|
| - adjoining = track.pickPrevious(slice);
|
| - }
|
| - if (adjoining != undefined) {
|
| - selection.push({track: track, slice: adjoining});
|
| - if (slice.start < minTime)
|
| - minTime = slice.start;
|
| - if (slice.start + slice.duration > maxTime)
|
| - maxTime = slice.start + slice.duration;
|
| - }
|
| - }
|
| - if (selection.length == 0) {
|
| - // Nothing adjoining was found; leave the current selection.
|
| - return false;
|
| - }
|
| - this.selection = selection;
|
| -
|
| - // Potentially move the viewport to keep the new selection in view.
|
| - this.viewport_.xPanWorldRangeIntoView(minTime, maxTime,
|
| - this.firstCanvas.width);
|
| - return true;
|
| - },
|
| -
|
| get keyHelp() {
|
| var help = 'Keyboard shortcuts:\n' +
|
| ' w/s : Zoom in/out (with shift: go faster)\n' +
|
| @@ -639,38 +742,27 @@ cr.define('tracing', function() {
|
| },
|
|
|
| set selection(selection) {
|
| + if (!(selection instanceof TimelineSelection))
|
| + throw 'Expected TimelineSelection';
|
| +
|
| // Clear old selection.
|
| var i;
|
| for (i = 0; i < this.selection_.length; i++)
|
| - this.selection_[i].slice.selected = false;
|
| + this.selection_[i].selected = false;
|
|
|
| this.selection_ = selection;
|
|
|
| cr.dispatchSimpleEvent(this, 'selectionChange');
|
| for (i = 0; i < this.selection_.length; i++)
|
| - this.selection_[i].slice.selected = true;
|
| + this.selection_[i].selected = true;
|
| this.viewport_.dispatchChangeEvent(); // Triggers a redraw.
|
| },
|
|
|
| - getSelectionRange: function() {
|
| - var wmin = Infinity;
|
| - var wmax = -wmin;
|
| - for (var i = 0; i < this.selection_.length; i++) {
|
| - var hit = this.selection_[i];
|
| - if (hit.slice) {
|
| - wmin = Math.min(wmin, hit.slice.start);
|
| - wmax = Math.max(wmax, hit.slice.end);
|
| - }
|
| - }
|
| - return {
|
| - min: wmin,
|
| - max: wmax
|
| - };
|
| - },
|
| -
|
| setSelectionAndMakeVisible: function(selection, zoomAllowed) {
|
| + if (!(selection instanceof TimelineSelection))
|
| + throw 'Expected TimelineSelection';
|
| this.selection = selection;
|
| - var range = this.getSelectionRange();
|
| + var range = this.selection.range;
|
| var size = this.viewport_.xWorldVectorToView(range.max - range.min);
|
| if (zoomAllowed && size < 50) {
|
| var worldCenter = range.min + (range.max - range.min) * 0.5;
|
| @@ -724,11 +816,9 @@ cr.define('tracing', function() {
|
| onGridToggle_: function(left) {
|
| var tb;
|
| if (left)
|
| - tb = Math.min.apply(Math, this.selection_.map(
|
| - function(x) { return x.slice.start; }));
|
| + tb = this.selection_.range.min;
|
| else
|
| - tb = Math.max.apply(Math, this.selection_.map(
|
| - function(x) { return x.slice.end; }));
|
| + tb = this.selection_.range.max;
|
|
|
| // Shift the timebase left until its just left of minTimestamp.
|
| var numInterfvalsSinceStart = Math.ceil((tb - this.model_.minTimestamp) /
|
| @@ -801,25 +891,21 @@ cr.define('tracing', function() {
|
| var hiWX = this.viewport_.xViewToWorld(hiX - canv.offsetLeft);
|
|
|
| // Figure out what has been hit.
|
| - (function() {
|
| - var selection = [];
|
| - function addHit(type, track, slice) {
|
| - selection.push({track: track, slice: slice});
|
| + var selection = new TimelineSelection();
|
| + for (i = 0; i < this.tracks_.children.length; i++) {
|
| + var track = this.tracks_.children[i];
|
| +
|
| + // Only check tracks that insersect the rect.
|
| + var trackClientRect = track.getBoundingClientRect();
|
| + var a = Math.max(loY, trackClientRect.top);
|
| + var b = Math.min(hiY, trackClientRect.bottom);
|
| + if (a <= b) {
|
| + track.addIntersectingItemsInRangeToSelection(
|
| + loWX, hiWX, loY, hiY, selection);
|
| }
|
| - for (i = 0; i < this.tracks_.children.length; i++) {
|
| - var track = this.tracks_.children[i];
|
| -
|
| - // Only check tracks that insersect the rect.
|
| - var trackClientRect = track.getBoundingClientRect();
|
| - var a = Math.max(loY, trackClientRect.top);
|
| - var b = Math.min(hiY, trackClientRect.bottom);
|
| - if (a <= b) {
|
| - track.pickRange(loWX, hiWX, loY, hiY, addHit);
|
| - }
|
| - }
|
| - // Activate the new selection.
|
| - this.selection = selection;
|
| - }).call(this);
|
| + }
|
| + // Activate the new selection.
|
| + this.selection = selection;
|
| }
|
| },
|
|
|
| @@ -844,6 +930,9 @@ cr.define('tracing', function() {
|
|
|
| return {
|
| Timeline: Timeline,
|
| + TimelineSelectionSliceHit: TimelineSelectionSliceHit,
|
| + TimelineSelectionCounterSampleHit: TimelineSelectionCounterSampleHit,
|
| + TimelineSelection: TimelineSelection,
|
| TimelineViewport: TimelineViewport
|
| };
|
| });
|
|
|