| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 /** | 6 /** |
| 7 * @fileoverview Renders an array of slices into the provided div, | 7 * @fileoverview Renders an array of slices into the provided div, |
| 8 * using a child canvas element. Uses a FastRectRenderer to draw only | 8 * using a child canvas element. Uses a FastRectRenderer to draw only |
| 9 * the visible slices. | 9 * the visible slices. |
| 10 */ | 10 */ |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 63 this.updateChildTracks_(); | 63 this.updateChildTracks_(); |
| 64 }, | 64 }, |
| 65 | 65 |
| 66 get firstCanvas() { | 66 get firstCanvas() { |
| 67 if (this.tracks_.length) | 67 if (this.tracks_.length) |
| 68 return this.tracks_[0].firstCanvas; | 68 return this.tracks_[0].firstCanvas; |
| 69 return undefined; | 69 return undefined; |
| 70 }, | 70 }, |
| 71 | 71 |
| 72 /** | 72 /** |
| 73 * Picks a slice, if any, at a given location. | 73 * Adds items intersecting a point to a selection. |
| 74 * @param {number} wX X location to search at, in worldspace. | 74 * @param {number} wX X location to search at, in worldspace. |
| 75 * @param {number} wY Y location to search at, in offset space. | 75 * @param {number} wY Y location to search at, in offset space. |
| 76 * offset space. | 76 * offset space. |
| 77 * @param {function():*} onHitCallback Callback to call with the slice, | 77 * @param {TimelineSelection} selection Selection to which to add hits. |
| 78 * if one is found. | |
| 79 * @return {boolean} true if a slice was found, otherwise false. | 78 * @return {boolean} true if a slice was found, otherwise false. |
| 80 */ | 79 */ |
| 81 pick: function(wX, wY, onHitCallback) { | 80 addIntersectingItemsToSelection: function(wX, wY, selection) { |
| 82 for (var i = 0; i < this.tracks_.length; i++) { | 81 for (var i = 0; i < this.tracks_.length; i++) { |
| 83 var trackClientRect = this.tracks_[i].getBoundingClientRect(); | 82 var trackClientRect = this.tracks_[i].getBoundingClientRect(); |
| 84 if (wY >= trackClientRect.top && wY < trackClientRect.bottom) | 83 if (wY >= trackClientRect.top && wY < trackClientRect.bottom) |
| 85 return this.tracks_[i].pick(wX, onHitCallback); | 84 this.tracks_[i].addIntersectingItemsToSelection(wX, wY, selection); |
| 86 } | 85 } |
| 87 return false; | 86 return false; |
| 88 }, | 87 }, |
| 89 | 88 |
| 90 /** | 89 /** |
| 91 * Finds slices intersecting the given interval. | 90 * Adds items intersecting the given range to a selection. |
| 92 * @param {number} loWX Lower X bound of the interval to search, in | 91 * @param {number} loWX Lower X bound of the interval to search, in |
| 93 * worldspace. | 92 * worldspace. |
| 94 * @param {number} hiWX Upper X bound of the interval to search, in | 93 * @param {number} hiWX Upper X bound of the interval to search, in |
| 95 * worldspace. | 94 * worldspace. |
| 96 * @param {number} loY Lower Y bound of the interval to search, in | 95 * @param {number} loY Lower Y bound of the interval to search, in |
| 97 * offset space. | 96 * offset space. |
| 98 * @param {number} hiY Upper Y bound of the interval to search, in | 97 * @param {number} hiY Upper Y bound of the interval to search, in |
| 99 * offset space. | 98 * offset space. |
| 100 * @param {function():*} onHitCallback Function to call for each slice | 99 * @param {TimelineSelection} selection Selection to which to add hits. |
| 101 * intersecting the interval. | |
| 102 */ | 100 */ |
| 103 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) { | 101 addIntersectingItemsInRangeToSelection: function( |
| 102 loWX, hiWX, loY, hiY, selection) { |
| 104 for (var i = 0; i < this.tracks_.length; i++) { | 103 for (var i = 0; i < this.tracks_.length; i++) { |
| 105 var trackClientRect = this.tracks_[i].getBoundingClientRect(); | 104 var trackClientRect = this.tracks_[i].getBoundingClientRect(); |
| 106 var a = Math.max(loY, trackClientRect.top); | 105 var a = Math.max(loY, trackClientRect.top); |
| 107 var b = Math.min(hiY, trackClientRect.bottom); | 106 var b = Math.min(hiY, trackClientRect.bottom); |
| 108 if (a <= b) | 107 if (a <= b) |
| 109 this.tracks_[i].pickRange(loWX, hiWX, loY, hiY, onHitCallback); | 108 this.tracks_[i].addIntersectingItemsInRangeToSelection( |
| 109 loWX, hiWX, loY, hiY, selection); |
| 110 } | 110 } |
| 111 }, | 111 }, |
| 112 | 112 |
| 113 /** | 113 addAllObjectsMatchingFilterToSelection: function(filter, selection) { |
| 114 * @return {Array} Objects matching the given filter. | 114 for (var i = 0; i < this.tracks_.length; i++) |
| 115 */ | 115 this.tracks_[i].addAllObjectsMatchingFilterToSelection( |
| 116 findAllObjectsMatchingFilter: function(filter) { | 116 filter, selection); |
| 117 var hits = []; | |
| 118 for (var i = 0; i < this.tracks_.length; i++) { | |
| 119 var trackHits = this.tracks_[i].findAllObjectsMatchingFilter(filter); | |
| 120 Array.prototype.push.apply(hits, trackHits); | |
| 121 } | |
| 122 return hits; | |
| 123 } | 117 } |
| 124 }; | 118 }; |
| 125 | 119 |
| 126 function addControlButtonElements(el, canCollapse) { | 120 function addControlButtonElements(el, canCollapse) { |
| 127 var closeEl = document.createElement('div'); | 121 var closeEl = document.createElement('div'); |
| 128 closeEl.classList.add('timeline-track-button'); | 122 closeEl.classList.add('timeline-track-button'); |
| 129 closeEl.classList.add('timeline-track-close-button'); | 123 closeEl.classList.add('timeline-track-close-button'); |
| 130 closeEl.textContent = String.fromCharCode(215); // × | 124 closeEl.textContent = String.fromCharCode(215); // × |
| 131 closeEl.addEventListener('click', function() { | 125 closeEl.addEventListener('click', function() { |
| 132 el.style.display = 'None'; | 126 el.style.display = 'None'; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 152 | 146 |
| 153 /** | 147 /** |
| 154 * Visualizes a TimelineThread using a series of of TimelineSliceTracks. | 148 * Visualizes a TimelineThread using a series of of TimelineSliceTracks. |
| 155 * @constructor | 149 * @constructor |
| 156 */ | 150 */ |
| 157 var TimelineThreadTrack = cr.ui.define(TimelineContainerTrack); | 151 var TimelineThreadTrack = cr.ui.define(TimelineContainerTrack); |
| 158 TimelineThreadTrack.prototype = { | 152 TimelineThreadTrack.prototype = { |
| 159 __proto__: TimelineContainerTrack.prototype, | 153 __proto__: TimelineContainerTrack.prototype, |
| 160 | 154 |
| 161 decorate: function() { | 155 decorate: function() { |
| 156 TimelineContainerTrack.prototype.decorate.call(this); |
| 162 this.classList.add('timeline-thread-track'); | 157 this.classList.add('timeline-thread-track'); |
| 163 }, | 158 }, |
| 164 | 159 |
| 165 get thread() { | 160 get thread() { |
| 166 return this.thread_; | 161 return this.thread_; |
| 167 }, | 162 }, |
| 168 | 163 |
| 169 set thread(thread) { | 164 set thread(thread) { |
| 170 this.thread_ = thread; | 165 this.thread_ = thread; |
| 171 this.updateChildTracks_(); | 166 this.updateChildTracks_(); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 }, | 206 }, |
| 212 | 207 |
| 213 updateChildTracks_: function() { | 208 updateChildTracks_: function() { |
| 214 this.detach(); | 209 this.detach(); |
| 215 this.textContent = ''; | 210 this.textContent = ''; |
| 216 this.tracks_ = []; | 211 this.tracks_ = []; |
| 217 if (this.thread_) { | 212 if (this.thread_) { |
| 218 if (this.thread_.cpuSlices) { | 213 if (this.thread_.cpuSlices) { |
| 219 var track = this.addTrack_(this.thread_.cpuSlices); | 214 var track = this.addTrack_(this.thread_.cpuSlices); |
| 220 track.height = '4px'; | 215 track.height = '4px'; |
| 216 track.decorateHit = function(hit) { |
| 217 hit.thread = this.thread_; |
| 218 } |
| 221 } | 219 } |
| 222 | 220 |
| 223 if (this.thread_.asyncSlices.length) { | 221 if (this.thread_.asyncSlices.length) { |
| 224 var subRows = this.thread_.asyncSlices.subRows; | 222 var subRows = this.thread_.asyncSlices.subRows; |
| 225 for (var srI = 0; srI < subRows.length; srI++) { | 223 for (var srI = 0; srI < subRows.length; srI++) { |
| 226 var track = this.addTrack_(subRows[srI]); | 224 var track = this.addTrack_(subRows[srI]); |
| 225 track.decorateHit = function(hit) { |
| 226 // TODO(simonjam): figure out how to associate subSlice hits back |
| 227 // to their parent slice. |
| 228 } |
| 227 track.asyncStyle = true; | 229 track.asyncStyle = true; |
| 228 } | 230 } |
| 229 } | 231 } |
| 230 | 232 |
| 231 for (var srI = 0; srI < this.thread_.subRows.length; srI++) { | 233 for (var srI = 0; srI < this.thread_.subRows.length; srI++) { |
| 232 this.addTrack_(this.thread_.subRows[srI]); | 234 var track = this.addTrack_(this.thread_.subRows[srI]); |
| 235 track.decorateHit = function(hit) { |
| 236 hit.thread = this.thread_; |
| 237 } |
| 233 } | 238 } |
| 234 | 239 |
| 235 if (this.tracks_.length > 0) { | 240 if (this.tracks_.length > 0) { |
| 236 if (this.thread_.cpuSlices) { | 241 if (this.thread_.cpuSlices) { |
| 237 this.tracks_[1].heading = this.heading_; | 242 this.tracks_[1].heading = this.heading_; |
| 238 this.tracks_[1].tooltip = this.tooltip_; | 243 this.tracks_[1].tooltip = this.tooltip_; |
| 239 } else { | 244 } else { |
| 240 this.tracks_[0].heading = this.heading_; | 245 this.tracks_[0].heading = this.heading_; |
| 241 this.tracks_[0].tooltip = this.tooltip_; | 246 this.tracks_[0].tooltip = this.tooltip_; |
| 242 } | 247 } |
| (...skipping 24 matching lines...) Expand all Loading... |
| 267 | 272 |
| 268 /** | 273 /** |
| 269 * Visualizes a TimelineCpu using a series of of TimelineSliceTracks. | 274 * Visualizes a TimelineCpu using a series of of TimelineSliceTracks. |
| 270 * @constructor | 275 * @constructor |
| 271 */ | 276 */ |
| 272 var TimelineCpuTrack = cr.ui.define(TimelineContainerTrack); | 277 var TimelineCpuTrack = cr.ui.define(TimelineContainerTrack); |
| 273 TimelineCpuTrack.prototype = { | 278 TimelineCpuTrack.prototype = { |
| 274 __proto__: TimelineContainerTrack.prototype, | 279 __proto__: TimelineContainerTrack.prototype, |
| 275 | 280 |
| 276 decorate: function() { | 281 decorate: function() { |
| 282 TimelineContainerTrack.prototype.decorate.call(this); |
| 277 this.classList.add('timeline-thread-track'); | 283 this.classList.add('timeline-thread-track'); |
| 278 }, | 284 }, |
| 279 | 285 |
| 280 get cpu() { | 286 get cpu() { |
| 281 return this.cpu_; | 287 return this.cpu_; |
| 282 }, | 288 }, |
| 283 | 289 |
| 284 set cpu(cpu) { | 290 set cpu(cpu) { |
| 285 this.cpu_ = cpu; | 291 this.cpu_ = cpu; |
| 286 this.updateChildTracks_(); | 292 this.updateChildTracks_(); |
| (...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 508 /** | 514 /** |
| 509 * Should we elide text on trace labels? | 515 * Should we elide text on trace labels? |
| 510 * Without eliding, text that is too wide isn't drawn at all. | 516 * Without eliding, text that is too wide isn't drawn at all. |
| 511 * Disable if you feel this causes a performance problem. | 517 * Disable if you feel this causes a performance problem. |
| 512 * This is a default value that can be overridden in tracks for testing. | 518 * This is a default value that can be overridden in tracks for testing. |
| 513 * @const | 519 * @const |
| 514 */ | 520 */ |
| 515 SHOULD_ELIDE_TEXT: true, | 521 SHOULD_ELIDE_TEXT: true, |
| 516 | 522 |
| 517 decorate: function() { | 523 decorate: function() { |
| 524 CanvasBasedTrack.prototype.decorate.call(this); |
| 518 this.classList.add('timeline-slice-track'); | 525 this.classList.add('timeline-slice-track'); |
| 519 this.elidedTitleCache = new ElidedTitleCache(); | 526 this.elidedTitleCache = new ElidedTitleCache(); |
| 520 this.asyncStyle_ = false; | 527 this.asyncStyle_ = false; |
| 521 }, | 528 }, |
| 522 | 529 |
| 530 /** |
| 531 * Called by all the addToSelection functions on the created selection |
| 532 * hit objects. Override this function on parent classes to add |
| 533 * context-specific information to the hit. |
| 534 */ |
| 535 decorateHit: function(hit) { |
| 536 }, |
| 537 |
| 523 get asyncStyle() { | 538 get asyncStyle() { |
| 524 return this.asyncStyle_; | 539 return this.asyncStyle_; |
| 525 }, | 540 }, |
| 526 | 541 |
| 527 set asyncStyle(v) { | 542 set asyncStyle(v) { |
| 528 this.asyncStyle_ = !!v; | 543 this.asyncStyle_ = !!v; |
| 529 this.invalidate(); | 544 this.invalidate(); |
| 530 }, | 545 }, |
| 531 | 546 |
| 532 get slices() { | 547 get slices() { |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 661 if (drawnWidth * pixWidth < slice.duration) { | 676 if (drawnWidth * pixWidth < slice.duration) { |
| 662 var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration); | 677 var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration); |
| 663 ctx.fillText(drawnTitle, cX, 2.5, drawnWidth); | 678 ctx.fillText(drawnTitle, cX, 2.5, drawnWidth); |
| 664 } | 679 } |
| 665 } | 680 } |
| 666 } | 681 } |
| 667 } | 682 } |
| 668 }, | 683 }, |
| 669 | 684 |
| 670 /** | 685 /** |
| 671 * Picks a slice, if any, at a given location. | 686 * Finds slices intersecting the given interval. |
| 672 * @param {number} wX X location to search at, in worldspace. | 687 * @param {number} wX X location to search at, in worldspace. |
| 673 * @param {number} wY Y location to search at, in offset space. | 688 * @param {number} wY Y location to search at, in offset space. |
| 674 * offset space. | 689 * offset space. |
| 675 * @param {function():*} onHitCallback Callback to call with the slice, | 690 * @param {TimelineSelection} selection Selection to which to add hits. |
| 676 * if one is found. | |
| 677 * @return {boolean} true if a slice was found, otherwise false. | 691 * @return {boolean} true if a slice was found, otherwise false. |
| 678 */ | 692 */ |
| 679 pick: function(wX, wY, onHitCallback) { | 693 addIntersectingItemsToSelection: function(wX, wY, selection) { |
| 680 var clientRect = this.getBoundingClientRect(); | 694 var clientRect = this.getBoundingClientRect(); |
| 681 if (wY < clientRect.top || wY >= clientRect.bottom) | 695 if (wY < clientRect.top || wY >= clientRect.bottom) |
| 682 return false; | 696 return false; |
| 683 var x = tracing.findLowIndexInSortedIntervals(this.slices_, | 697 var x = tracing.findLowIndexInSortedIntervals(this.slices_, |
| 684 function(x) { return x.start; }, | 698 function(x) { return x.start; }, |
| 685 function(x) { return x.duration; }, | 699 function(x) { return x.duration; }, |
| 686 wX); | 700 wX); |
| 687 if (x >= 0 && x < this.slices_.length) { | 701 if (x >= 0 && x < this.slices_.length) { |
| 688 onHitCallback('slice', this, this.slices_[x]); | 702 var hit = selection.addSlice(this, this.slices_[x]); |
| 703 this.decorateHit(hit); |
| 689 return true; | 704 return true; |
| 690 } | 705 } |
| 691 return false; | 706 return false; |
| 692 }, | 707 }, |
| 693 | 708 |
| 694 /** | 709 /** |
| 695 * Finds slices intersecting the given interval. | 710 * Adds items intersecting the given range to a selection. |
| 696 * @param {number} loWX Lower X bound of the interval to search, in | 711 * @param {number} loWX Lower X bound of the interval to search, in |
| 697 * worldspace. | 712 * worldspace. |
| 698 * @param {number} hiWX Upper X bound of the interval to search, in | 713 * @param {number} hiWX Upper X bound of the interval to search, in |
| 699 * worldspace. | 714 * worldspace. |
| 700 * @param {number} loY Lower Y bound of the interval to search, in | 715 * @param {number} loY Lower Y bound of the interval to search, in |
| 701 * offset space. | 716 * offset space. |
| 702 * @param {number} hiY Upper Y bound of the interval to search, in | 717 * @param {number} hiY Upper Y bound of the interval to search, in |
| 703 * offset space. | 718 * offset space. |
| 704 * @param {function():*} onHitCallback Function to call for each slice | 719 * @param {TimelineSelection} selection Selection to which to add hits. |
| 705 * intersecting the interval. | |
| 706 */ | 720 */ |
| 707 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) { | 721 addIntersectingItemsInRangeToSelection: function( |
| 722 loWX, hiWX, loY, hiY, selection) { |
| 708 var clientRect = this.getBoundingClientRect(); | 723 var clientRect = this.getBoundingClientRect(); |
| 709 var a = Math.max(loY, clientRect.top); | 724 var a = Math.max(loY, clientRect.top); |
| 710 var b = Math.min(hiY, clientRect.bottom); | 725 var b = Math.min(hiY, clientRect.bottom); |
| 711 if (a > b) | 726 if (a > b) |
| 712 return; | 727 return; |
| 713 | 728 |
| 714 var that = this; | 729 var that = this; |
| 715 function onPickHit(slice) { | 730 function onPickHit(slice) { |
| 716 onHitCallback('slice', that, slice); | 731 var hit = selection.addSlice(that, slice); |
| 732 that.decorateHit(hit); |
| 717 } | 733 } |
| 718 tracing.iterateOverIntersectingIntervals(this.slices_, | 734 tracing.iterateOverIntersectingIntervals(this.slices_, |
| 719 function(x) { return x.start; }, | 735 function(x) { return x.start; }, |
| 720 function(x) { return x.duration; }, | 736 function(x) { return x.duration; }, |
| 721 loWX, hiWX, | 737 loWX, hiWX, |
| 722 onPickHit); | 738 onPickHit); |
| 723 }, | 739 }, |
| 724 | 740 |
| 725 /** | 741 /** |
| 726 * Find the index for the given slice. | 742 * Find the index for the given slice. |
| 727 * @return {index} Index of the given slice, or undefined. | 743 * @return {index} Index of the given slice, or undefined. |
| 728 * @private | 744 * @private |
| 729 */ | 745 */ |
| 730 indexOfSlice_: function(slice) { | 746 indexOfSlice_: function(slice) { |
| 731 var index = tracing.findLowIndexInSortedArray(this.slices_, | 747 var index = tracing.findLowIndexInSortedArray(this.slices_, |
| 732 function(x) { return x.start; }, | 748 function(x) { return x.start; }, |
| 733 slice.start); | 749 slice.start); |
| 734 while (index < this.slices_.length && | 750 while (index < this.slices_.length && |
| 735 slice.start == this.slices_[index].start && | 751 slice.start == this.slices_[index].start && |
| 736 slice.colorId != this.slices_[index].colorId) { | 752 slice.colorId != this.slices_[index].colorId) { |
| 737 index++; | 753 index++; |
| 738 } | 754 } |
| 739 return index < this.slices_.length ? index : undefined; | 755 return index < this.slices_.length ? index : undefined; |
| 740 }, | 756 }, |
| 741 | 757 |
| 742 /** | 758 /** |
| 743 * Return the next slice, if any, after the given slice. | 759 * Add the item to the left or right of the provided hit, if any, to the |
| 744 * @param {slice} The previous slice. | 760 * selection. |
| 745 * @return {slice} The next slice, or undefined. | 761 * @param {slice} The current slice. |
| 762 * @param {Number} offset Number of slices away from the hit to look. |
| 763 * @param {TimelineSelection} selection The selection to add a hit to, |
| 764 * if found. |
| 765 * @return {boolean} Whether a hit was found. |
| 746 * @private | 766 * @private |
| 747 */ | 767 */ |
| 748 pickNext: function(slice) { | 768 addItemNearToProvidedHitToSelection: function(hit, offset, selection) { |
| 749 var index = this.indexOfSlice_(slice); | 769 if (!hit.slice) |
| 750 if (index != undefined) { | 770 return false; |
| 751 if (index < this.slices_.length - 1) | 771 |
| 752 index++; | 772 var index = this.indexOfSlice_(hit.slice); |
| 753 else | 773 if (index === undefined) |
| 754 index = undefined; | 774 return false; |
| 755 } | 775 |
| 756 return index != undefined ? this.slices_[index] : undefined; | 776 var newIndex = index + offset; |
| 777 if (newIndex < 0 || newIndex >= this.slices_.length) |
| 778 return false; |
| 779 |
| 780 var hit = selection.addSlice(this, this.slices_[newIndex]); |
| 781 this.decorateHit(hit); |
| 782 return true; |
| 757 }, | 783 }, |
| 758 | 784 |
| 759 /** | 785 addAllObjectsMatchingFilterToSelection: function(filter, selection) { |
| 760 * Return the previous slice, if any, before the given slice. | 786 for (var i = 0; i < this.slices_.length; ++i) { |
| 761 * @param {slice} A slice. | 787 if (filter.matchSlice(this.slices_[i])) { |
| 762 * @return {slice} The previous slice, or undefined. | 788 var hit = selection.addSlice(this, this.slices_[i]); |
| 763 */ | 789 this.decorateHit(hit); |
| 764 pickPrevious: function(slice) { | 790 } |
| 765 var index = this.indexOfSlice_(slice); | 791 } |
| 766 if (index == 0) | |
| 767 return undefined; | |
| 768 else if ((index != undefined) && (index > 0)) | |
| 769 index--; | |
| 770 return index != undefined ? this.slices_[index] : undefined; | |
| 771 }, | |
| 772 | |
| 773 findAllObjectsMatchingFilter: function(filter) { | |
| 774 var hits = []; | |
| 775 for (var i = 0; i < this.slices_.length; ++i) | |
| 776 if (filter.matchSlice(this.slices_[i])) | |
| 777 hits.push({track: this, | |
| 778 slice: this.slices_[i]}); | |
| 779 return hits; | |
| 780 } | 792 } |
| 781 }; | 793 }; |
| 782 | 794 |
| 783 /** | 795 /** |
| 784 * A track that displays the viewport size and scale. | 796 * A track that displays the viewport size and scale. |
| 785 * @constructor | 797 * @constructor |
| 786 * @extends {CanvasBasedTrack} | 798 * @extends {CanvasBasedTrack} |
| 787 */ | 799 */ |
| 788 | 800 |
| 789 var TimelineViewportTrack = cr.ui.define(CanvasBasedTrack); | 801 var TimelineViewportTrack = cr.ui.define(CanvasBasedTrack); |
| 790 | 802 |
| 791 var logOf10 = Math.log(10); | 803 var logOf10 = Math.log(10); |
| 792 function log10(x) { | 804 function log10(x) { |
| 793 return Math.log(x) / logOf10; | 805 return Math.log(x) / logOf10; |
| 794 } | 806 } |
| 795 | 807 |
| 796 TimelineViewportTrack.prototype = { | 808 TimelineViewportTrack.prototype = { |
| 797 | 809 |
| 798 __proto__: CanvasBasedTrack.prototype, | 810 __proto__: CanvasBasedTrack.prototype, |
| 799 | 811 |
| 800 decorate: function() { | 812 decorate: function() { |
| 813 CanvasBasedTrack.prototype.decorate.call(this); |
| 801 this.classList.add('timeline-viewport-track'); | 814 this.classList.add('timeline-viewport-track'); |
| 802 this.strings_secs_ = []; | 815 this.strings_secs_ = []; |
| 803 this.strings_msecs_ = []; | 816 this.strings_msecs_ = []; |
| 804 }, | 817 }, |
| 805 | 818 |
| 806 redraw: function() { | 819 redraw: function() { |
| 807 var ctx = this.ctx_; | 820 var ctx = this.ctx_; |
| 808 var canvasW = this.canvas_.width; | 821 var canvasW = this.canvas_.width; |
| 809 var canvasH = this.canvas_.height; | 822 var canvasH = this.canvas_.height; |
| 810 | 823 |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 889 var xView = Math.floor(curXView + minorMarkDistancePx * i); | 902 var xView = Math.floor(curXView + minorMarkDistancePx * i); |
| 890 ctx.moveTo(xView, canvasH - minorTickH); | 903 ctx.moveTo(xView, canvasH - minorTickH); |
| 891 ctx.lineTo(xView, canvasH); | 904 ctx.lineTo(xView, canvasH); |
| 892 } | 905 } |
| 893 | 906 |
| 894 ctx.stroke(); | 907 ctx.stroke(); |
| 895 } | 908 } |
| 896 }, | 909 }, |
| 897 | 910 |
| 898 /** | 911 /** |
| 899 * Picks a slice, if any, at a given location. | 912 * Adds items intersecting a point to a selection. |
| 900 * @param {number} wX X location to search at, in worldspace. | 913 * @param {number} wX X location to search at, in worldspace. |
| 901 * @param {number} wY Y location to search at, in offset space. | 914 * @param {number} wY Y location to search at, in offset space. |
| 902 * offset space. | 915 * offset space. |
| 903 * @param {function():*} onHitCallback Callback to call with the slice, | 916 * @param {TimelineSelection} selection Selection to which to add hits. |
| 904 * if one is found. | |
| 905 * @return {boolean} true if a slice was found, otherwise false. | 917 * @return {boolean} true if a slice was found, otherwise false. |
| 906 */ | 918 */ |
| 907 pick: function(wX, wY, onHitCallback) { | 919 addIntersectingItemsToSelection: function(wX, wY, selection) { |
| 908 // Does nothing. There's nothing interesting to pick on the viewport | 920 // Does nothing. There's nothing interesting to pick on the viewport |
| 909 // track. | 921 // track. |
| 910 }, | 922 }, |
| 911 | 923 |
| 912 /** | 924 /** |
| 913 * Finds slices intersecting the given interval. | 925 * Adds items intersecting the given range to a selection. |
| 914 * @param {number} loWX Lower X bound of the interval to search, in | 926 * @param {number} loWX Lower X bound of the interval to search, in |
| 915 * worldspace. | 927 * worldspace. |
| 916 * @param {number} hiWX Upper X bound of the interval to search, in | 928 * @param {number} hiWX Upper X bound of the interval to search, in |
| 917 * worldspace. | 929 * worldspace. |
| 918 * @param {number} loY Lower Y bound of the interval to search, in | 930 * @param {number} loY Lower Y bound of the interval to search, in |
| 919 * offset space. | 931 * offset space. |
| 920 * @param {number} hiY Upper Y bound of the interval to search, in | 932 * @param {number} hiY Upper Y bound of the interval to search, in |
| 921 * offset space. | 933 * offset space. |
| 922 * @param {function():*} onHitCallback Function to call for each slice | 934 * @param {TimelineSelection} selection Selection to which to add hits. |
| 923 * intersecting the interval. | |
| 924 */ | 935 */ |
| 925 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) { | 936 addIntersectingItemsInRangeToSelection: function( |
| 937 loWX, hiWX, loY, hiY, selection) { |
| 926 // Does nothing. There's nothing interesting to pick on the viewport | 938 // Does nothing. There's nothing interesting to pick on the viewport |
| 927 // track. | 939 // track. |
| 928 }, | 940 }, |
| 929 | 941 |
| 930 findAllObjectsMatchingFilter: function(filter) { | 942 addAllObjectsMatchingFilterToSelection: function(filter, selection) { |
| 931 return []; | |
| 932 } | 943 } |
| 933 | 944 |
| 934 }; | 945 }; |
| 935 | 946 |
| 936 /** | 947 /** |
| 937 * A track that displays a TimelineCounter object. | 948 * A track that displays a TimelineCounter object. |
| 938 * @constructor | 949 * @constructor |
| 939 * @extends {CanvasBasedTrack} | 950 * @extends {CanvasBasedTrack} |
| 940 */ | 951 */ |
| 941 | 952 |
| 942 var TimelineCounterTrack = cr.ui.define(CanvasBasedTrack); | 953 var TimelineCounterTrack = cr.ui.define(CanvasBasedTrack); |
| 943 | 954 |
| 944 TimelineCounterTrack.prototype = { | 955 TimelineCounterTrack.prototype = { |
| 945 | 956 |
| 946 __proto__: CanvasBasedTrack.prototype, | 957 __proto__: CanvasBasedTrack.prototype, |
| 947 | 958 |
| 948 decorate: function() { | 959 decorate: function() { |
| 960 CanvasBasedTrack.prototype.decorate.call(this); |
| 949 this.classList.add('timeline-counter-track'); | 961 this.classList.add('timeline-counter-track'); |
| 950 addControlButtonElements(this, false); | 962 addControlButtonElements(this, false); |
| 963 this.selectedSamples_ = {}; |
| 964 }, |
| 965 |
| 966 /** |
| 967 * Called by all the addToSelection functions on the created selection |
| 968 * hit objects. Override this function on parent classes to add |
| 969 * context-specific information to the hit. |
| 970 */ |
| 971 decorateHit: function(hit) { |
| 951 }, | 972 }, |
| 952 | 973 |
| 953 get counter() { | 974 get counter() { |
| 954 return this.counter_; | 975 return this.counter_; |
| 955 }, | 976 }, |
| 956 | 977 |
| 957 set counter(counter) { | 978 set counter(counter) { |
| 958 this.counter_ = counter; | 979 this.counter_ = counter; |
| 959 this.invalidate(); | 980 this.invalidate(); |
| 960 }, | 981 }, |
| 961 | 982 |
| 983 /** |
| 984 * @return {Object} A sparce, mutable map from sample index to bool. Samples |
| 985 * indices the map that are true are drawn as selected. Callers that mutate |
| 986 * the map must manually call invalidate on the track to trigger a redraw. |
| 987 */ |
| 988 get selectedSamples() { |
| 989 return this.selectedSamples_; |
| 990 }, |
| 991 |
| 962 redraw: function() { | 992 redraw: function() { |
| 963 var ctr = this.counter_; | 993 var ctr = this.counter_; |
| 964 var ctx = this.ctx_; | 994 var ctx = this.ctx_; |
| 965 var canvasW = this.canvas_.width; | 995 var canvasW = this.canvas_.width; |
| 966 var canvasH = this.canvas_.height; | 996 var canvasH = this.canvas_.height; |
| 967 | 997 |
| 968 ctx.clearRect(0, 0, canvasW, canvasH); | 998 ctx.clearRect(0, 0, canvasW, canvasH); |
| 969 | 999 |
| 970 // Culling parametrs. | 1000 // Culling parametrs. |
| 971 var vp = this.viewport_; | 1001 var vp = this.viewport_; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1039 } | 1069 } |
| 1040 ctx.lineTo(x, yLastView); | 1070 ctx.lineTo(x, yLastView); |
| 1041 ctx.lineTo(x, yView); | 1071 ctx.lineTo(x, yView); |
| 1042 iLast = i; | 1072 iLast = i; |
| 1043 xLast = x; | 1073 xLast = x; |
| 1044 yLastView = yView; | 1074 yLastView = yView; |
| 1045 } | 1075 } |
| 1046 ctx.closePath(); | 1076 ctx.closePath(); |
| 1047 ctx.fill(); | 1077 ctx.fill(); |
| 1048 } | 1078 } |
| 1079 ctx.fillStyle = 'rgba(255, 0, 0, 1)'; |
| 1080 for (var i in this.selectedSamples_) { |
| 1081 if (!this.selectedSamples_[i]) |
| 1082 continue; |
| 1083 |
| 1084 var x = ctr.timestamps[i]; |
| 1085 for (var seriesIndex = ctr.numSeries - 1; |
| 1086 seriesIndex >= 0; seriesIndex--) { |
| 1087 var y = ctr.totals[i * numSeries + seriesIndex]; |
| 1088 var yView = canvasH - (yScale * y); |
| 1089 ctx.fillRect(x - pixWidth, yView - 1, 3 * pixWidth, 3); |
| 1090 } |
| 1091 } |
| 1049 ctx.restore(); | 1092 ctx.restore(); |
| 1050 }, | 1093 }, |
| 1051 | 1094 |
| 1052 /** | 1095 /** |
| 1053 * Picks a slice, if any, at a given location. | 1096 * Adds items intersecting a point to a selection. |
| 1054 * @param {number} wX X location to search at, in worldspace. | 1097 * @param {number} wX X location to search at, in worldspace. |
| 1055 * @param {number} wY Y location to search at, in offset space. | 1098 * @param {number} wY Y location to search at, in offset space. |
| 1056 * offset space. | 1099 * offset space. |
| 1057 * @param {function():*} onHitCallback Callback to call with the slice, | 1100 * @param {TimelineSelection} selection Selection to which to add hits. |
| 1058 * if one is found. | |
| 1059 * @return {boolean} true if a slice was found, otherwise false. | 1101 * @return {boolean} true if a slice was found, otherwise false. |
| 1060 */ | 1102 */ |
| 1061 pick: function(wX, wY, onHitCallback) { | 1103 addIntersectingItemsToSelection: function(wX, wY, selection) { |
| 1104 var clientRect = this.getBoundingClientRect(); |
| 1105 if (wY < clientRect.top || wY >= clientRect.bottom) |
| 1106 return false; |
| 1107 var ctr = this.counter_; |
| 1108 if (wX < this.counter_.timestamps[0]) |
| 1109 return false; |
| 1110 var i = tracing.findLowIndexInSortedArray(ctr.timestamps, |
| 1111 function(x) { return x; }, |
| 1112 wX); |
| 1113 if (i < 0 || i >= ctr.timestamps.length) |
| 1114 return false; |
| 1115 |
| 1116 // Sample i is going to either be exactly at wX or slightly above it, |
| 1117 // E.g. asking for 7.5 in [7,8] gives i=1. So bump i back by 1 if needed. |
| 1118 if (i > 0 && wX > this.counter_.timestamps[i - 1]) |
| 1119 i--; |
| 1120 |
| 1121 // Some preliminaries. |
| 1122 var canvasH = this.getBoundingClientRect().height; |
| 1123 var yScale = canvasH / ctr.maxTotal; |
| 1124 |
| 1125 /* |
| 1126 // Figure out which sample we hit |
| 1127 var seriesIndexHit; |
| 1128 for (var seriesIndex = 0; seriesIndex < ctr.numSeries; seriesIndex++) { |
| 1129 var y = ctr.totals[i * ctr.numSeries + seriesIndex]; |
| 1130 var yView = canvasH - (yScale * y) + clientRect.top; |
| 1131 if (wY >= yView) { |
| 1132 seriesIndexHit = seriesIndex; |
| 1133 break; |
| 1134 } |
| 1135 } |
| 1136 if (seriesIndexHit === undefined) |
| 1137 return false; |
| 1138 */ |
| 1139 var hit = selection.addCounterSample(this, this.counter, i); |
| 1140 this.decorateHit(hit); |
| 1141 return true; |
| 1062 }, | 1142 }, |
| 1063 | 1143 |
| 1064 /** | 1144 /** |
| 1065 * Finds slices intersecting the given interval. | 1145 * Adds items intersecting the given range to a selection. |
| 1066 * @param {number} loWX Lower X bound of the interval to search, in | 1146 * @param {number} loWX Lower X bound of the interval to search, in |
| 1067 * worldspace. | 1147 * worldspace. |
| 1068 * @param {number} hiWX Upper X bound of the interval to search, in | 1148 * @param {number} hiWX Upper X bound of the interval to search, in |
| 1069 * worldspace. | 1149 * worldspace. |
| 1070 * @param {number} loY Lower Y bound of the interval to search, in | 1150 * @param {number} loY Lower Y bound of the interval to search, in |
| 1071 * offset space. | 1151 * offset space. |
| 1072 * @param {number} hiY Upper Y bound of the interval to search, in | 1152 * @param {number} hiY Upper Y bound of the interval to search, in |
| 1073 * offset space. | 1153 * offset space. |
| 1074 * @param {function():*} onHitCallback Function to call for each slice | 1154 * @param {TimelineSelection} selection Selection to which to add hits. |
| 1075 * intersecting the interval. | |
| 1076 */ | 1155 */ |
| 1077 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) { | 1156 addIntersectingItemsInRangeToSelection: function( |
| 1157 loWX, hiWX, loY, hiY, selection) { |
| 1158 |
| 1159 var clientRect = this.getBoundingClientRect(); |
| 1160 var a = Math.max(loY, clientRect.top); |
| 1161 var b = Math.min(hiY, clientRect.bottom); |
| 1162 if (a > b) |
| 1163 return; |
| 1164 |
| 1165 var ctr = this.counter_; |
| 1166 |
| 1167 var iLo = tracing.findLowIndexInSortedArray(ctr.timestamps, |
| 1168 function(x) { return x; }, |
| 1169 loWX); |
| 1170 var iHi = tracing.findLowIndexInSortedArray(ctr.timestamps, |
| 1171 function(x) { return x; }, |
| 1172 hiWX); |
| 1173 |
| 1174 // Sample i is going to either be exactly at wX or slightly above it, |
| 1175 // E.g. asking for 7.5 in [7,8] gives i=1. So bump i back by 1 if needed. |
| 1176 if (iLo > 0 && loWX > ctr.timestamps[iLo - 1]) |
| 1177 iLo--; |
| 1178 if (iHi > 0 && hiWX > ctr.timestamps[iHi - 1]) |
| 1179 iHi--; |
| 1180 |
| 1181 // Iterate over every sample intersecting.. |
| 1182 for (var i = iLo; i <= iHi; i++) { |
| 1183 if (i >= ctr.timestamps.length) |
| 1184 continue; |
| 1185 |
| 1186 // TODO(nduca): Pick the seriesIndexHit based on the loY - hiY values. |
| 1187 var hit = selection.addCounterSample(this, this.counter, i); |
| 1188 this.decorateHit(hit); |
| 1189 } |
| 1078 }, | 1190 }, |
| 1079 | 1191 |
| 1080 findAllObjectsMatchingFilter: function(filter) { | 1192 addAllObjectsMatchingFilterToSelection: function(filter, selection) { |
| 1081 return []; | |
| 1082 } | 1193 } |
| 1083 | 1194 |
| 1084 }; | 1195 }; |
| 1085 | 1196 |
| 1086 return { | 1197 return { |
| 1087 TimelineCounterTrack: TimelineCounterTrack, | 1198 TimelineCounterTrack: TimelineCounterTrack, |
| 1088 TimelineSliceTrack: TimelineSliceTrack, | 1199 TimelineSliceTrack: TimelineSliceTrack, |
| 1089 TimelineThreadTrack: TimelineThreadTrack, | 1200 TimelineThreadTrack: TimelineThreadTrack, |
| 1090 TimelineViewportTrack: TimelineViewportTrack, | 1201 TimelineViewportTrack: TimelineViewportTrack, |
| 1091 TimelineCpuTrack: TimelineCpuTrack | 1202 TimelineCpuTrack: TimelineCpuTrack |
| 1092 }; | 1203 }; |
| 1093 }); | 1204 }); |
| OLD | NEW |