| 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 'use strict'; | 5 'use strict'; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * @fileoverview TimelineView visualizes TRACE_EVENT events using the | 8 * @fileoverview TimelineView visualizes TRACE_EVENT events using the |
| 9 * tracing.Timeline component. | 9 * tracing.Timeline component and adds in selection summary and control buttons. |
| 10 */ | 10 */ |
| 11 cr.define('tracing', function() { | 11 cr.define('tracing', function() { |
| 12 function tsRound(ts) { | 12 function tsRound(ts) { |
| 13 return Math.round(ts * 1000.0) / 1000.0; | 13 return Math.round(ts * 1000.0) / 1000.0; |
| 14 } | 14 } |
| 15 function getPadding(text, width) { | 15 function getPadding(text, width) { |
| 16 width = width || 0; | 16 width = width || 0; |
| 17 | 17 |
| 18 if (typeof text != 'string') | 18 if (typeof text != 'string') |
| 19 text = String(text); | 19 text = String(text); |
| 20 | 20 |
| 21 if (text.length >= width) | 21 if (text.length >= width) |
| 22 return ''; | 22 return ''; |
| 23 | 23 |
| 24 var pad = ''; | 24 var pad = ''; |
| 25 for (var i = 0; i < width - text.length; i++) | 25 for (var i = 0; i < width - text.length; i++) |
| 26 pad += ' '; | 26 pad += ' '; |
| 27 return pad; | 27 return pad; |
| 28 } | 28 } |
| 29 | 29 |
| 30 function leftAlign(text, width) { | 30 function leftAlign(text, width) { |
| 31 return text + getPadding(text, width); | 31 return text + getPadding(text, width); |
| 32 } | 32 } |
| 33 | 33 |
| 34 function rightAlign(text, width) { | 34 function rightAlign(text, width) { |
| 35 return getPadding(text, width) + text; | 35 return getPadding(text, width) + text; |
| 36 } | 36 } |
| 37 | 37 |
| 38 /** | 38 /** |
| 39 * TimelineFindControl |
| 40 * @constructor |
| 41 * @extends {tracing.Overlay} |
| 42 */ |
| 43 var TimelineFindControl = cr.ui.define('div'); |
| 44 |
| 45 TimelineFindControl.prototype = { |
| 46 __proto__: tracing.Overlay.prototype, |
| 47 |
| 48 decorate: function() { |
| 49 tracing.Overlay.prototype.decorate.call(this); |
| 50 |
| 51 this.className = 'timeline-find-control'; |
| 52 |
| 53 this.hitCountEl_ = document.createElement('span'); |
| 54 this.hitCountEl_.className = 'hit-count-label'; |
| 55 this.hitCountEl_.textContent = '1 of 7'; |
| 56 |
| 57 var findPreviousBn = document.createElement('span'); |
| 58 findPreviousBn.className = 'find-button find-previous'; |
| 59 findPreviousBn.textContent = '\u2190'; |
| 60 findPreviousBn.addEventListener('click', function() { |
| 61 this.controller.findPrevious(); |
| 62 this.updateHitCountEl_(); |
| 63 }.bind(this)); |
| 64 |
| 65 var findNextBn = document.createElement('span'); |
| 66 findNextBn.className = 'find-button find-next'; |
| 67 findNextBn.textContent = '\u2192'; |
| 68 findNextBn.addEventListener('click', function() { |
| 69 this.controller.findNext(); |
| 70 this.updateHitCountEl_(); |
| 71 }.bind(this)); |
| 72 |
| 73 // Filter input element. |
| 74 this.filterEl_ = document.createElement('input'); |
| 75 this.filterEl_.type = 'input'; |
| 76 |
| 77 this.filterEl_.addEventListener('input', function(e) { |
| 78 this.controller.filterText = this.filterEl_.value; |
| 79 this.updateHitCountEl_(); |
| 80 }.bind(this)); |
| 81 |
| 82 this.filterEl_.addEventListener('keydown', function(e) { |
| 83 if (e.keyCode == 13) { |
| 84 findNextBn.click(); |
| 85 } else if (e.keyCode == 27) { |
| 86 this.filterEl_.blur(); |
| 87 this.updateHitCountEl_(); |
| 88 } |
| 89 }.bind(this)); |
| 90 |
| 91 this.filterEl_.addEventListener('blur', function(e) { |
| 92 this.updateHitCountEl_(); |
| 93 }.bind(this)); |
| 94 |
| 95 this.filterEl_.addEventListener('focus', function(e) { |
| 96 this.updateHitCountEl_(); |
| 97 }.bind(this)); |
| 98 |
| 99 // Attach everything. |
| 100 this.appendChild(this.filterEl_); |
| 101 |
| 102 this.appendChild(findPreviousBn); |
| 103 this.appendChild(findNextBn); |
| 104 this.appendChild(this.hitCountEl_); |
| 105 |
| 106 this.updateHitCountEl_(); |
| 107 }, |
| 108 |
| 109 get controller() { |
| 110 return this.controller_; |
| 111 }, |
| 112 |
| 113 set controller(c) { |
| 114 this.controller_ = c; |
| 115 this.updateHitCountEl_(); |
| 116 }, |
| 117 |
| 118 focus: function() { |
| 119 this.filterEl_.selectionStart = 0; |
| 120 this.filterEl_.selectionEnd = this.filterEl_.value.length; |
| 121 this.filterEl_.focus(); |
| 122 }, |
| 123 |
| 124 updateHitCountEl_: function() { |
| 125 if (!this.controller || document.activeElement != this.filterEl_) { |
| 126 this.hitCountEl_.textContent = ''; |
| 127 return; |
| 128 } |
| 129 var i = this.controller.currentHitIndex; |
| 130 var n = this.controller.filterHits.length; |
| 131 if (n == 0) |
| 132 this.hitCountEl_.textContent = '0 of 0'; |
| 133 else |
| 134 this.hitCountEl_.textContent = (i + 1) + ' of ' + n; |
| 135 } |
| 136 }; |
| 137 |
| 138 function TimelineFindController() { |
| 139 this.timeline_ = undefined; |
| 140 this.model_ = undefined; |
| 141 this.filterText_ = ''; |
| 142 this.filterHitsDirty_ = true; |
| 143 this.currentHitIndex_ = 0; |
| 144 }; |
| 145 |
| 146 TimelineFindController.prototype = { |
| 147 __proto__: Object.prototype, |
| 148 |
| 149 get timeline() { |
| 150 return this.timeline_; |
| 151 }, |
| 152 |
| 153 set timeline(t) { |
| 154 this.timeline_ = t; |
| 155 this.filterHitsDirty_ = true; |
| 156 }, |
| 157 |
| 158 get filterText() { |
| 159 return this.filterText_; |
| 160 }, |
| 161 |
| 162 set filterText(f) { |
| 163 if (f == this.filterText_) |
| 164 return; |
| 165 this.filterText_ = f; |
| 166 this.filterHitsDirty_ = true; |
| 167 this.findNext(); |
| 168 }, |
| 169 |
| 170 get filterHits() { |
| 171 if (this.filterHitsDirty_) { |
| 172 this.filterHitsDirty_ = false; |
| 173 if (this.timeline_) { |
| 174 var filter = new tracing.TimelineFilter(this.filterText); |
| 175 this.filterHits_ = this.timeline.findAllObjectsMatchingFilter(filter); |
| 176 this.currentHitIndex_ = this.filterHits_.length - 1; |
| 177 } else { |
| 178 this.filterHits_ = []; |
| 179 this.currentHitIndex_ = 0; |
| 180 } |
| 181 } |
| 182 return this.filterHits_; |
| 183 }, |
| 184 |
| 185 get currentHitIndex() { |
| 186 return this.currentHitIndex_; |
| 187 }, |
| 188 |
| 189 find_: function(dir) { |
| 190 if (!this.timeline) |
| 191 return; |
| 192 |
| 193 var N = this.filterHits.length; |
| 194 this.currentHitIndex_ = this.currentHitIndex_ + dir; |
| 195 |
| 196 if (this.currentHitIndex_ < 0) this.currentHitIndex_ = N - 1; |
| 197 if (this.currentHitIndex_ >= N) this.currentHitIndex_ = 0; |
| 198 |
| 199 if (this.currentHitIndex_ < 0 || this.currentHitIndex_ >= N) { |
| 200 this.timeline.selection = []; |
| 201 return; |
| 202 } |
| 203 |
| 204 var hit = this.filterHits[this.currentHitIndex_]; |
| 205 |
| 206 // We allow the zoom level to change on the first hit level. But, when |
| 207 // then cycling through subsequent changes, restrict it to panning. |
| 208 var zoomAllowed = this.currentHitIndex_ == 0; |
| 209 this.timeline.setSelectionAndMakeVisible([hit], zoomAllowed); |
| 210 }, |
| 211 |
| 212 findNext: function() { |
| 213 this.find_(1); |
| 214 }, |
| 215 |
| 216 findPrevious: function() { |
| 217 this.find_(-1); |
| 218 }, |
| 219 }; |
| 220 |
| 221 /** |
| 39 * TimelineView | 222 * TimelineView |
| 40 * @constructor | 223 * @constructor |
| 41 * @extends {HTMLDivElement} | 224 * @extends {HTMLDivElement} |
| 42 */ | 225 */ |
| 43 var TimelineView = cr.ui.define('div'); | 226 var TimelineView = cr.ui.define('div'); |
| 44 | 227 |
| 45 TimelineView.prototype = { | 228 TimelineView.prototype = { |
| 46 __proto__: HTMLDivElement.prototype, | 229 __proto__: HTMLDivElement.prototype, |
| 47 | 230 |
| 48 decorate: function() { | 231 decorate: function() { |
| 49 this.classList.add('timeline-view'); | 232 this.classList.add('timeline-view'); |
| 50 | 233 |
| 234 // Create individual elements. |
| 235 this.titleEl_ = document.createElement('span'); |
| 236 this.titleEl_.textContent = 'Tracing: '; |
| 237 |
| 238 this.controlDiv_ = document.createElement('div'); |
| 239 this.controlDiv_.className = 'control'; |
| 240 |
| 241 this.leftControlsEl_ = document.createElement('div'); |
| 242 this.rightControlsEl_ = document.createElement('div'); |
| 243 |
| 244 var spacingEl = document.createElement('div'); |
| 245 spacingEl.className = 'spacer'; |
| 246 |
| 51 this.timelineContainer_ = document.createElement('div'); | 247 this.timelineContainer_ = document.createElement('div'); |
| 52 this.timelineContainer_.className = 'timeline-container'; | 248 this.timelineContainer_.className = 'timeline-container'; |
| 53 | 249 |
| 54 var summaryContainer_ = document.createElement('div'); | 250 var summaryContainer_ = document.createElement('div'); |
| 55 summaryContainer_.className = 'summary-container'; | 251 summaryContainer_.className = 'summary-container'; |
| 56 | 252 |
| 57 this.summaryEl_ = document.createElement('pre'); | 253 this.summaryEl_ = document.createElement('pre'); |
| 58 this.summaryEl_.className = 'summary'; | 254 this.summaryEl_.className = 'summary'; |
| 59 | 255 |
| 256 this.findCtl_ = new TimelineFindControl(); |
| 257 this.findCtl_.controller = new TimelineFindController(); |
| 258 |
| 259 // Connect everything up. |
| 260 this.rightControls.appendChild(this.findCtl_); |
| 261 this.controlDiv_.appendChild(this.titleEl_); |
| 262 this.controlDiv_.appendChild(this.leftControlsEl_); |
| 263 this.controlDiv_.appendChild(spacingEl); |
| 264 this.controlDiv_.appendChild(this.rightControlsEl_); |
| 265 this.appendChild(this.controlDiv_); |
| 266 |
| 267 this.appendChild(this.timelineContainer_); |
| 268 |
| 60 summaryContainer_.appendChild(this.summaryEl_); | 269 summaryContainer_.appendChild(this.summaryEl_); |
| 61 this.appendChild(this.timelineContainer_); | |
| 62 this.appendChild(summaryContainer_); | 270 this.appendChild(summaryContainer_); |
| 63 | 271 |
| 272 // Bookkeeping. |
| 64 this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this); | 273 this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this); |
| 274 document.addEventListener('keypress', this.onKeypress_.bind(this), true); |
| 275 }, |
| 276 |
| 277 get leftControls() { |
| 278 return this.leftControlsEl_; |
| 279 }, |
| 280 |
| 281 get rightControls() { |
| 282 return this.rightControlsEl_; |
| 283 }, |
| 284 |
| 285 get title() { |
| 286 return this.titleEl_.textContent.substring( |
| 287 this.titleEl_.textContent.length - 2); |
| 288 }, |
| 289 |
| 290 set title(text) { |
| 291 this.titleEl_.textContent = text + ':'; |
| 65 }, | 292 }, |
| 66 | 293 |
| 67 set traceData(traceData) { | 294 set traceData(traceData) { |
| 68 this.model = new tracing.TimelineModel(traceData); | 295 this.model = new tracing.TimelineModel(traceData); |
| 69 }, | 296 }, |
| 70 | 297 |
| 71 get model() { | 298 get model() { |
| 72 return this.timelineModel_; | 299 return this.timelineModel_; |
| 73 }, | 300 }, |
| 74 | 301 |
| 75 set model(model) { | 302 set model(model) { |
| 76 this.timelineModel_ = model; | 303 this.timelineModel_ = model; |
| 77 | 304 |
| 78 // remove old timeline | 305 // remove old timeline |
| 79 this.timelineContainer_.textContent = ''; | 306 this.timelineContainer_.textContent = ''; |
| 80 | 307 |
| 81 // create new timeline if needed | 308 // create new timeline if needed |
| 82 if (this.timelineModel_.minTimestamp !== undefined) { | 309 if (this.timelineModel_.minTimestamp !== undefined) { |
| 83 if (this.timeline_) | 310 if (this.timeline_) |
| 84 this.timeline_.detach(); | 311 this.timeline_.detach(); |
| 85 this.timeline_ = new tracing.Timeline(); | 312 this.timeline_ = new tracing.Timeline(); |
| 86 this.timeline_.model = this.timelineModel_; | 313 this.timeline_.model = this.timelineModel_; |
| 87 this.timeline_.focusElement = this.parentElement; | 314 this.timeline_.focusElement = |
| 315 this.focusElement_ ? this.focusElement_ : this.parentElement; |
| 88 this.timelineContainer_.appendChild(this.timeline_); | 316 this.timelineContainer_.appendChild(this.timeline_); |
| 89 this.timeline_.addEventListener('selectionChange', | 317 this.timeline_.addEventListener('selectionChange', |
| 90 this.onSelectionChangedBoundToThis_); | 318 this.onSelectionChangedBoundToThis_); |
| 319 |
| 320 this.findCtl_.controller.timeline = this.timeline_; |
| 91 this.onSelectionChanged_(); | 321 this.onSelectionChanged_(); |
| 92 } else { | 322 } else { |
| 93 this.timeline_ = null; | 323 this.timeline_ = undefined; |
| 324 this.findCtl_.controller.timeline = undefined; |
| 94 } | 325 } |
| 95 }, | 326 }, |
| 96 | 327 |
| 97 get timeline() { | 328 get timeline() { |
| 98 return this.timeline_; | 329 return this.timeline_; |
| 99 }, | 330 }, |
| 100 | 331 |
| 332 /** |
| 333 * Sets the element whose focus state will determine whether |
| 334 * to respond to keybaord input. |
| 335 */ |
| 336 set focusElement(value) { |
| 337 this.focusElement_ = value; |
| 338 if (this.timeline_) |
| 339 this.timeline_.focusElement = value; |
| 340 }, |
| 341 |
| 342 /** |
| 343 * @return {Element} The element whose focused state determines |
| 344 * whether to respond to keyboard inputs. |
| 345 * Defaults to the parent element. |
| 346 */ |
| 347 get focusElement() { |
| 348 if (this.focusElement_) |
| 349 return this.focusElement_; |
| 350 return this.parentElement; |
| 351 }, |
| 352 |
| 353 /** |
| 354 * @return {boolean} Whether the current timeline is attached to the |
| 355 * document. |
| 356 */ |
| 357 get isAttachedToDocument_() { |
| 358 var cur = this; |
| 359 while (cur.parentNode) |
| 360 cur = cur.parentNode; |
| 361 return cur == this.ownerDocument; |
| 362 }, |
| 363 |
| 364 get listenToKeys_() { |
| 365 if (!this.isAttachedToDocument_) |
| 366 return; |
| 367 if (!this.focusElement_) |
| 368 return true; |
| 369 if (this.focusElement.tabIndex >= 0) |
| 370 return document.activeElement == this.focusElement; |
| 371 return true; |
| 372 }, |
| 373 |
| 374 onKeypress_: function(e) { |
| 375 if (!this.listenToKeys_) |
| 376 return; |
| 377 |
| 378 if (event.keyCode == 47) { |
| 379 this.findCtl_.focus(); |
| 380 event.preventDefault(); |
| 381 return; |
| 382 } |
| 383 }, |
| 384 |
| 385 beginFind: function() { |
| 386 if (this.findInProgress_) |
| 387 return; |
| 388 this.findInProgress_ = true; |
| 389 var dlg = TimelineFindControl(); |
| 390 dlg.controller = new TimelineFindController(); |
| 391 dlg.controller.timeline = this.timeline; |
| 392 dlg.visible = true; |
| 393 dlg.addEventListener('close', function() { |
| 394 this.findInProgress_ = false; |
| 395 }.bind(this)); |
| 396 dlg.addEventListener('findNext', function() { |
| 397 }); |
| 398 dlg.addEventListener('findPrevious', function() { |
| 399 }); |
| 400 }, |
| 401 |
| 101 onSelectionChanged_: function(e) { | 402 onSelectionChanged_: function(e) { |
| 102 var timeline = this.timeline_; | 403 var timeline = this.timeline_; |
| 103 var selection = timeline.selection; | 404 var selection = timeline.selection; |
| 104 if (!selection.length) { | 405 if (!selection.length) { |
| 105 var oldScrollTop = this.timelineContainer_.scrollTop; | 406 var oldScrollTop = this.timelineContainer_.scrollTop; |
| 106 this.summaryEl_.textContent = timeline.keyHelp; | 407 this.summaryEl_.textContent = timeline.keyHelp; |
| 107 this.timelineContainer_.scrollTop = oldScrollTop; | 408 this.timelineContainer_.scrollTop = oldScrollTop; |
| 108 return; | 409 return; |
| 109 } | 410 } |
| 110 | 411 |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 } | 487 } |
| 187 | 488 |
| 188 // done | 489 // done |
| 189 var oldScrollTop = this.timelineContainer_.scrollTop; | 490 var oldScrollTop = this.timelineContainer_.scrollTop; |
| 190 this.summaryEl_.textContent = text; | 491 this.summaryEl_.textContent = text; |
| 191 this.timelineContainer_.scrollTop = oldScrollTop; | 492 this.timelineContainer_.scrollTop = oldScrollTop; |
| 192 } | 493 } |
| 193 }; | 494 }; |
| 194 | 495 |
| 195 return { | 496 return { |
| 497 TimelineFindControl: TimelineFindControl, |
| 498 TimelineFindController: TimelineFindController, |
| 196 TimelineView: TimelineView | 499 TimelineView: TimelineView |
| 197 }; | 500 }; |
| 198 }); | 501 }); |
| OLD | NEW |