Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Unified Diff: chrome/browser/resources/tracing/timeline.js

Issue 8513009: Add TRACE_COUNTER support to about:tracing (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/tracing/timeline.js
diff --git a/chrome/browser/resources/tracing/timeline.js b/chrome/browser/resources/tracing/timeline.js
index 593113446cd7589eb21404ce574e857731f5b474..da2a52f882f5d81f0328ac16c2244fd17fef1720 100644
--- a/chrome/browser/resources/tracing/timeline.js
+++ b/chrome/browser/resources/tracing/timeline.js
@@ -30,17 +30,100 @@ cr.define('tracing', function() {
* @constructor
* @extends {cr.EventTarget}
*/
- function TimelineViewport() {
+ function TimelineViewport(parentEl) {
+ if (!parentEl)
+ throw 'parentEl must be provided';
James Hawkins 2011/11/16 17:41:38 In general we don't check parameters like this.
nduca 2011/11/16 19:03:56 Done.
+ this.parentEl_ = parentEl;
this.scaleX_ = 1;
this.panX_ = 0;
this.gridTimebase_ = 0;
this.gridStep_ = 1000 / 60;
this.gridEnabled_ = false;
+ this.hasCalledSetupFunction_ = false;
+
+ this.onResizeBoundToThis_ = this.onResize_.bind(this);
James Hawkins 2011/11/16 17:41:38 Hmm I'm not a fan of this construct. You lose rea
nduca 2011/11/16 19:03:56 Sorry, what's the alternative? I need to remove th
+
+ // The following code uses an interval to detect when the parent element
+ // is attached to the document. That is a trigger to run the setup function
+ // and install a resize listener.
+ this.checkForAttachInterval_ = setInterval(
+ this.checkForAttach_.bind(this), 250);
}
TimelineViewport.prototype = {
__proto__: cr.EventTarget.prototype,
+ /**
+ * Allows initialization of the viewport when the viewport's parent element
+ * has been attached to the document and given a size.
+ * @param {Function} fn Function to call when the viewport can be safely
+ * initialized.
+ */
+ setWhenPossible: function(fn) {
+ this.pendingSetFunction_ = fn;
+ },
+
James Hawkins 2011/11/16 17:41:38 Remove extra blank line.
nduca 2011/11/16 19:03:56 Done.
+
+ /**
+ * @return {boolean} Whether the current timeline is attached to the
+ * document.
+ */
+ get isAttachedToDocument_() {
+ var cur = this.parentEl_;
+ while (cur.parentNode)
+ cur = cur.parentNode;
+ return cur == this.parentEl_.ownerDocument;
+ },
+
+ onResize_: function() {
+ cr.dispatchSimpleEvent(this, 'change');
James Hawkins 2011/11/16 17:41:38 You have this as fireChanged() below; probably goo
nduca 2011/11/16 19:03:56 Done.
+ },
+
+ checkForAttach_: function() {
+ if (!this.isAttachedToDocument_ || this.clientWidth == 0)
+ return;
+
+ if (!this.iframe_) {
+ this.iframe_ = document.createElement('iframe');
+ this.iframe_.style.cssText =
+ 'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
+ this.parentEl_.appendChild(this.iframe_);
+
+ this.iframe_.contentWindow.addEventListener('resize',
+ this.onResizeBoundToThis_);
+ }
+
+ var curSize = this.clientWidth + 'x' + this.clientHeight;
+ if (this.pendingSetFunction_) {
+ this.lastSize_ = curSize;
+ try {
+ this.pendingSetFunction_();
James Hawkins 2011/11/16 17:41:38 Are you expecting this to fail in some way?
nduca 2011/11/16 19:03:56 Done.
+ } finally {
+ this.pendingSetFunction_ = undefined;
+ }
+ }
+
+ window.clearInterval(this.checkForAttachInterval_);
+ this.checkForAttachInterval_ = undefined;
+ },
+
+ /**
+ * Fires the change event on this viewport. Used to notify listeners
+ * to redraw when the underlying model has been mutated.
+ */
+ fireChanged: function() {
+ cr.dispatchSimpleEvent(this, 'change');
+ },
+
+ detach: function() {
+ if (this.checkForAttachInterval_) {
+ window.clearInterval(this.checkForAttachInterval_);
+ this.checkForAttachInterval_ = undefined;
+ }
+ this.iframe_.removeListener('resize', this.onResizeBoundToThis_);
+ this.parentEl_.removeChild(this.iframe_);
+ },
+
get scaleX() {
return this.scaleX_;
},
@@ -152,16 +235,10 @@ cr.define('tracing', function() {
decorate: function() {
this.classList.add('timeline');
- this.needsViewportReset_ = false;
-
- this.viewport_ = new TimelineViewport();
- this.viewport_.addEventListener('change',
- this.viewportChange_.bind(this));
- this.invalidatePending_ = false;
+ this.viewport_ = new TimelineViewport(this);
this.tracks_ = this.ownerDocument.createElement('div');
- this.tracks_.invalidate = this.invalidate.bind(this);
this.appendChild(this.tracks_);
this.dragBox_ = this.ownerDocument.createElement('div');
@@ -169,22 +246,6 @@ cr.define('tracing', function() {
this.appendChild(this.dragBox_);
this.hideDragBox_();
- // The following code uses a setInterval to monitor the timeline control
- // for size changes. This is so that we can keep the canvas' bitmap size
- // correctly synchronized with its presentation size.
- // TODO(nduca): detect this in a more efficient way, e.g. iframe hack.
- this.lastSize_ = this.clientWidth + 'x' + this.clientHeight;
- this.checkForResizeInterval_ =
- this.ownerDocument.defaultView.setInterval(function() {
- if (!this.isAttachedToDocument_)
- return;
- var curSize = this.clientWidth + 'x' + this.clientHeight;
- if (this.clientWidth && curSize != this.lastSize_) {
- this.lastSize_ = curSize;
- this.onResize();
- }
- }.bind(this), 250);
-
this.bindEventListener_(document, 'keypress', this.onKeypress_, this);
this.bindEventListener_(document, 'keydown', this.onKeydown_, this);
this.bindEventListener_(document, 'mousedown', this.onMouseDown_, this);
@@ -213,13 +274,19 @@ cr.define('tracing', function() {
},
detach: function() {
+ for (var i = 0; i < this.tracks_.children.length; ++i)
James Hawkins 2011/11/16 17:41:38 JS style is i++.
nduca 2011/11/16 19:03:56 Done.
+ this.tracks_.children[i].detach();
+
for (var i = 0; i < this.boundListeners_.length; i++) {
var binding = this.boundListeners_[i];
binding.object.removeEventListener(binding.event, binding.boundFunc);
}
this.boundListeners_ = undefined;
- window.clearInterval(this.checkForResizeInterval_);
- this.checkForResizeInterval_ = undefined;
+ this.viewport_.detach();
+ },
+
+ get viewport() {
+ return this.viewport_;
},
get model() {
@@ -234,113 +301,114 @@ cr.define('tracing', function() {
}
this.model_ = model;
- // Create tracks and measure their heading size.
- var threads = model.getAllThreads();
+ // Figure out all the headings.
+ var allHeadings = [];
+ model.getAllThreads().forEach(function(t) {
+ allHeadings.push(t.userFriendlyName);
+ });
+ model.getAllCounters().forEach(function(c) {
+ allHeadings.push(c.userFriendlyName);
+ });
+
+ // Figure out the maximum heading size.
var maxHeadingWidth = 0;
- var tracks = [];
var measuringStick = new tracing.MeasuringStick();
var headingEl = document.createElement('div');
headingEl.style.position = 'fixed';
- headingEl.className = 'timeline-slice-track-title';
- for (var tI = 0; tI < threads.length; tI++) {
- var thread = threads[tI];
- var track = new TimelineThreadTrack();
- track.thread = thread;
- track.viewport = this.viewport_;
- tracks.push(track);
- headingEl.textContent = track.heading;
+ headingEl.className = 'timeline-canvas-based-track-title';
+ allHeadings.forEach(function(text) {
+ headingEl.textContent = text + ':__';
var w = measuringStick.measure(headingEl).width;
// Limit heading width to 300px.
if (w > 300)
w = 300;
if (w > maxHeadingWidth)
maxHeadingWidth = w;
- }
- var extraHeadingPadding = 4;
- maxHeadingWidth += maxHeadingWidth + extraHeadingPadding;
+ });
+ maxHeadingWidth = maxHeadingWidth + 'px';
- // Attach tracks and set width.
+ // Reset old tracks.
+ for (var i = 0; i < this.tracks_.children.length; ++i)
+ this.tracks_.children[i].detach();
this.tracks_.textContent = '';
- threads.sort(tracing.TimelineThread.compare);
- for (var tI = 0; tI < tracks.length; tI++) {
- var track = tracks[tI];
- track.headingWidth = maxHeadingWidth + 'px';
- this.tracks_.appendChild(track);
- }
-
- if (this.isAttachedToDocument_)
- this.onResize();
- else
- this.needsViewportReset_ = true;
- },
-
- viewportChange_: function() {
- this.invalidate();
- },
-
- invalidate: function() {
- if (this.invalidatePending_)
- return;
- this.invalidatePending_ = true;
- if (this.isAttachedToDocument_)
- window.setTimeout(function() {
- this.invalidatePending_ = false;
- this.redrawAllTracks_();
- }.bind(this), 0);
- },
-
- /**
- * @return {boolean} Whether the current timeline is attached to the
- * document.
- */
- get isAttachedToDocument_() {
- var cur = this;
- while (cur.parentNode)
- cur = cur.parentNode;
- return cur == this.ownerDocument;
- },
-
- onResize: function() {
- if (!this.isAttachedToDocument_)
- throw 'Not attached to document!';
- for (var i = 0; i < this.tracks_.children.length; i++) {
- var track = this.tracks_.children[i];
- track.onResize();
- }
- if (this.invalidatePending_) {
- this.invalidatePending_ = false;
- this.redrawAllTracks_();
- }
- },
- redrawAllTracks_: function() {
- if (this.needsViewportReset_ && this.clientWidth != 0) {
- if (!this.isAttachedToDocument_)
- throw 'Not attached to document!';
- this.needsViewportReset_ = false;
- /* update viewport */
+ // Get a sorted list of processes.
+ var processes = [];
+ for (var pid in model.processes)
+ processes.push(model.processes[pid]);
+ processes.sort(tracing.TimelineProcess.compare);
+
+ // Create tracks for each process.
+ processes.forEach(function(process) {
+ // Add counter tracks for this process.
+ var counters = [];
+ for (var tid in process.counters)
+ counters.push(process.counters[tid]);
+ counters.sort(tracing.TimelineCounter.compare);
+
+ // Create the counters for this process.
+ counters.forEach(function(counter) {
+ var track = new tracing.TimelineCounterTrack();
+ track.heading = counter.name + ':';
+ track.headingWidth = maxHeadingWidth;
+ track.viewport = this.viewport_;
+ track.counter = counter;
+ this.tracks_.appendChild(track);
+ }.bind(this));
+
+ // Get a sorted list of threads.
+ var threads = [];
+ for (var tid in process.threads)
+ threads.push(process.threads[tid]);
+ threads.sort(tracing.TimelineThread.compare);
+
+ // Create the threads.
+ threads.forEach(function(thread) {
+ var track = new tracing.TimelineThreadTrack();
+ track.heading = thread.userFriendlyName + ':';
+ track.tooltip = thread.userFriendlyDetials;
+ track.headingWidth = maxHeadingWidth;
+ track.viewport = this.viewport_;
+ track.thread = thread;
+ this.tracks_.appendChild(track);
+ }.bind(this));
+ }.bind(this));
+
+ // Set up a reasonable viewport.
+ this.viewport_.setWhenPossible(function() {
var rangeTimestamp = this.model_.maxTimestamp -
this.model_.minTimestamp;
var w = this.firstCanvas.width;
var scaleX = w / rangeTimestamp;
var panX = -this.model_.minTimestamp;
this.viewport_.setPanAndScale(panX, scaleX);
- }
- for (var i = 0; i < this.tracks_.children.length; i++) {
- this.tracks_.children[i].redraw();
- }
+ }.bind(this));
},
- updateChildViewports_: function() {
- for (var cI = 0; cI < this.tracks_.children.length; cI++) {
- var child = this.tracks_.children[cI];
- child.setViewport(this.panX, this.scaleX);
- }
+ /**
+ * @return {Element} The element whose focused state determines
+ * whether whether to respond to keyboard inputs.
+ * Defaults to the parent element.
+ */
+ get focusElement() {
+ if (this.focusElement_)
+ return this.focusElement_;
+ return this.parentElement;
+ },
+
+ /**
+ * Sets the element whose focus state will determine whether
+ * to respond to keybaord input.
+ */
+ set focusElement(value) {
+ this.focusElement_ = value;
},
get listenToKeys_() {
- if (this.parentElement.parentElement.tabIndex >= 0)
- return document.activeElement == this.parentElement.parentElement;
+ if (!this.focusElement_)
+ return true;
+ if (this.focusElement.tabIndex >= 0)
+ return document.activeElement == this.focusElement;
return true;
},
@@ -408,7 +476,7 @@ cr.define('tracing', function() {
e.preventDefault();
break;
case 9: // TAB
- if (this.parentElement.parentElement.tabIndex == -1) {
+ if (this.focusElement.tabIndex == -1) {
if (e.shiftKey)
this.selectPrevious_(e);
else
@@ -468,12 +536,7 @@ cr.define('tracing', function() {
if (adjoining != undefined)
selection.push({track: track, slice: adjoining});
}
- // Activate the new selection.
- this.selection_ = selection;
- for (i = 0; i < this.selection_.length; i++)
- this.selection_[i].slice.selected = true;
- cr.dispatchSimpleEvent(this, 'selectionChange');
- this.invalidate(); // Cause tracks to redraw.
+ this.selection = selection;
e.preventDefault();
},
@@ -484,14 +547,13 @@ cr.define('tracing', function() {
' e : Center on mouse\n' +
' g/G : Shows grid at the start/end of the selected task\n';
- if (this.parentElement.parentElement.tabIndex) {
+ if (this.focusElement.tabIndex) {
help += ' <- : Select previous event on current timeline\n' +
' -> : Select next event on current timeline\n';
} else {
help += ' <-,^TAB : Select previous event on current timeline\n' +
' ->, TAB : Select next event on current timeline\n';
}
-
help +=
'\n' +
'Dbl-click to zoom in; Shift dbl-click to zoom out\n';
@@ -512,7 +574,7 @@ cr.define('tracing', function() {
cr.dispatchSimpleEvent(this, 'selectionChange');
for (i = 0; i < this.selection_.length; i++)
this.selection_[i].slice.selected = true;
- this.invalidate(); // Cause tracks to redraw.
+ this.viewport_.fireChanged(); // Triggers a redraw.
},
get firstCanvas() {
@@ -569,10 +631,12 @@ cr.define('tracing', function() {
},
onMouseDown_: function(e) {
- if (e.clientX < this.offsetLeft ||
- e.clientX >= this.offsetLeft + this.offsetWidth ||
- e.clientY < this.offsetTop ||
- e.clientY >= this.offsetTop + this.offsetHeight)
+ rect = this.getClientRects()[0];
+ if (!rect ||
+ e.clientX < rect.left ||
+ e.clientX >= rect.right ||
+ e.clientY < rect.top ||
+ e.clientY >= rect.bottom)
return;
var canv = this.firstCanvas;
@@ -585,8 +649,8 @@ cr.define('tracing', function() {
this.dragBeginEvent_ = e;
e.preventDefault();
- if (this.parentElement.parentElement.tabIndex)
- this.parentElement.parentElement.focus();
+ if (this.focusElement.tabIndex >= 0)
+ this.focusElement.focus();
},
onMouseMove_: function(e) {
@@ -653,7 +717,7 @@ cr.define('tracing', function() {
scale = 1 / scale;
this.zoomBy_(scale);
e.preventDefault();
- },
+ }
};
/**
@@ -663,6 +727,7 @@ cr.define('tracing', function() {
cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS);
return {
- Timeline: Timeline
+ Timeline: Timeline,
+ TimelineViewport: TimelineViewport
};
});

Powered by Google App Engine
This is Rietveld 408576698