summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authornduca@chromium.org <nduca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-21 00:06:53 +0000
committernduca@chromium.org <nduca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-21 00:06:53 +0000
commit77f93a0fee5d33d31c4a28443f576f38b845194a (patch)
tree89d657f62f066c1f4c74de63ff3c19b48dc7a404 /chrome
parentb83fbfe820d7c5ae9e7df246e482db957f671004 (diff)
downloadchromium_src-77f93a0fee5d33d31c4a28443f576f38b845194a.zip
chromium_src-77f93a0fee5d33d31c4a28443f576f38b845194a.tar.gz
chromium_src-77f93a0fee5d33d31c4a28443f576f38b845194a.tar.bz2
Find feature for tracing, at long last.
BUG=104567 Review URL: https://chromiumcodereview.appspot.com/10170001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133308 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/resources/tracing/interactive_tests.html105
-rw-r--r--chrome/browser/resources/tracing/kernel_trace_viewer.html1
-rw-r--r--chrome/browser/resources/tracing/linux_perf_importer_test.html2
-rw-r--r--chrome/browser/resources/tracing/overlay.js7
-rw-r--r--chrome/browser/resources/tracing/overlay_test.html2
-rw-r--r--chrome/browser/resources/tracing/profiling_view.css16
-rw-r--r--chrome/browser/resources/tracing/profiling_view.js33
-rw-r--r--chrome/browser/resources/tracing/profiling_view_test.html100
-rw-r--r--chrome/browser/resources/tracing/test_utils.js22
-rw-r--r--chrome/browser/resources/tracing/tests.html4
-rw-r--r--chrome/browser/resources/tracing/timeline.js65
-rw-r--r--chrome/browser/resources/tracing/timeline_model.js26
-rw-r--r--chrome/browser/resources/tracing/timeline_model_test.html31
-rw-r--r--chrome/browser/resources/tracing/timeline_test.html125
-rw-r--r--chrome/browser/resources/tracing/timeline_track.js30
-rw-r--r--chrome/browser/resources/tracing/timeline_track_test.html21
-rw-r--r--chrome/browser/resources/tracing/timeline_view.css68
-rw-r--r--chrome/browser/resources/tracing/timeline_view.js311
-rw-r--r--chrome/browser/resources/tracing/timeline_view_test.html257
19 files changed, 1007 insertions, 219 deletions
diff --git a/chrome/browser/resources/tracing/interactive_tests.html b/chrome/browser/resources/tracing/interactive_tests.html
new file mode 100644
index 0000000..b40c47a
--- /dev/null
+++ b/chrome/browser/resources/tracing/interactive_tests.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Copyright (c) 2012 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<head i18n-values="dir:textdirection;">
+<title>Interactive Timeline Tests</title>
+<link rel="stylesheet" href="timeline.css">
+<link rel="stylesheet" href="timeline_view.css">
+<link rel="stylesheet" href="overlay.css">
+<script src="../shared/js/cr.js"></script>
+<script src="../shared/js/cr/event_target.js"></script>
+<script src="../shared/js/cr/ui.js"></script>
+<script src="../shared/js/util.js"></script>
+<script src="timeline_model.js"></script>
+<script src="linux_perf_importer.js"></script>
+<script src="trace_event_importer.js"></script>
+<script src="sorted_array_utils.js"></script>
+<script src="measuring_stick.js"></script>
+<script src="overlay.js"></script>
+<script src="timeline.js"></script>
+<script src="timeline_view.js"></script>
+<script src="timeline_track.js"></script>
+<script src="fast_rect_renderer.js"></script>
+<script src="test_utils.js"></script>
+</head>
+<body>
+ <div class="timeline-test" src="./tests/trivial_trace.json" create-detached=1>
+ </div>
+
+ <div class="timeline-test" src="./tests/trivial_trace.json">
+ </div>
+
+ <div class="timeline-test" src="./tests/simple_trace.json">
+ </div>
+
+ <div class="timeline-test" src="./tests/instance_counters.json">
+ </div>
+
+ <div class="timeline-test" src="./tests/tall_trace.json">
+ </div>
+
+ <div class="timeline-test" src="./tests/big_trace.json">
+ </div>
+
+ <div class="timeline-test" src="./tests/huge_trace.json">
+ </div>
+
+ <div class="timeline-test" src="./tests/main_thread_has_unclosed_slices.json">
+ </div>
+
+ <div class="timeline-test" src="./tests/async_begin_end.json">
+ </div>
+
+ <script>
+ function load(parentEl) {
+ var src = parentEl.getAttribute('src');
+ if (document.location.hash && document.location.hash.substring(1) != src) {
+ parentEl.hidden = true;
+ return;
+ }
+ parentEl.hidden = false;
+ parentEl.textContent = '';
+ var titleEl = document.createElement('h3');
+ var linkEl = document.createElement('a');
+ linkEl.textContent = src;
+ linkEl.href = '#' + src;
+ titleEl.appendChild(linkEl);
+
+ var containerEl = document.createElement('div');
+ containerEl.tabIndex = 0;
+ containerEl.style.border = '1px solid red';
+
+ var timelineViewEl = document.createElement('div');
+ cr.ui.decorate(timelineViewEl, tracing.TimelineView);
+ timelineViewEl.focusElement = containerEl;
+
+ parentEl.appendChild(titleEl);
+ parentEl.appendChild(containerEl);
+
+ // Creating attached vs detached stress tests the canvas- and viewport-
+ // setup code.
+ var create_detached = parentEl.getAttribute('create-attached') == 1;
+ function createModel(data) {
+ timelineViewEl.model = new tracing.TimelineModel(data);
+ if (!create_detached)
+ containerEl.appendChild(timelineViewEl);
+ }
+ if (create_detached)
+ containerEl.appendChild(timelineViewEl);
+ test_utils.getAsync(src, createModel);
+ }
+
+ function onLoad() {
+ Array.prototype.forEach.call(document.querySelectorAll('.timeline-test'),
+ load);
+ }
+
+ document.addEventListener('DOMContentLoaded', onLoad);
+ window.addEventListener('hashchange', onLoad);
+ </script>
+</body>
+</html>
diff --git a/chrome/browser/resources/tracing/kernel_trace_viewer.html b/chrome/browser/resources/tracing/kernel_trace_viewer.html
index 39482ef..5ff986b 100644
--- a/chrome/browser/resources/tracing/kernel_trace_viewer.html
+++ b/chrome/browser/resources/tracing/kernel_trace_viewer.html
@@ -18,6 +18,7 @@ found in the LICENSE file.
<script src="trace_event_importer.js"></script>
<script src="sorted_array_utils.js"></script>
<script src="measuring_stick.js"></script>
+<script src="overlay.js"></script>
<script src="timeline.js"></script>
<script src="timeline_track.js"></script>
<script src="timeline_view.js"></script>
diff --git a/chrome/browser/resources/tracing/linux_perf_importer_test.html b/chrome/browser/resources/tracing/linux_perf_importer_test.html
index f2e7241..0ac958f 100644
--- a/chrome/browser/resources/tracing/linux_perf_importer_test.html
+++ b/chrome/browser/resources/tracing/linux_perf_importer_test.html
@@ -6,7 +6,7 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head>
-<title>Perf Importer tests</title>
+<title>LinuxPerfImporter tests</title>
<script src=
"http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js">
</script>
diff --git a/chrome/browser/resources/tracing/overlay.js b/chrome/browser/resources/tracing/overlay.js
index bcf3771..1a911a8 100644
--- a/chrome/browser/resources/tracing/overlay.js
+++ b/chrome/browser/resources/tracing/overlay.js
@@ -56,7 +56,12 @@ cr.define('tracing', function() {
// Bring overlay into focus.
overlay.tabIndex = 0;
- overlay.focus();
+ var focusElement =
+ overlay.querySelector('button, input, list, select, a');
+ if (!focusElement) {
+ focusElement = overlay;
+ }
+ focusElement.focus();
// Listen to key and focus events to prevent focus from
// leaving the overlay.
diff --git a/chrome/browser/resources/tracing/overlay_test.html b/chrome/browser/resources/tracing/overlay_test.html
index 7dc7c79..8747118 100644
--- a/chrome/browser/resources/tracing/overlay_test.html
+++ b/chrome/browser/resources/tracing/overlay_test.html
@@ -6,7 +6,7 @@ found in the LICENSE file.
-->
<html>
<head>
-<title></title>
+<title>Overlay tests</title>
<link rel="stylesheet" href="overlay.css">
<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<script src="../shared/js/cr.js"></script>
diff --git a/chrome/browser/resources/tracing/profiling_view.css b/chrome/browser/resources/tracing/profiling_view.css
index db1cd49..84cfe09 100644
--- a/chrome/browser/resources/tracing/profiling_view.css
+++ b/chrome/browser/resources/tracing/profiling_view.css
@@ -9,22 +9,6 @@
padding: 0;
}
-.profiling-view > .control {
- border-bottom: 1px solid #555;
- display: -webkit-box;
-}
-
-.profiling-view > .control > span {
- padding-left: 5px;
- padding-right: 10px;
-}
-
-.profiling-view > .control > button {
- font-size: 75%;
- height: 20px;
- min-height: 10px;
-}
-
.profiling-view > .container {
-webkit-box-flex: 1;
display: -webkit-box;
diff --git a/chrome/browser/resources/tracing/profiling_view.js b/chrome/browser/resources/tracing/profiling_view.js
index 2d2cf6c..1fdc791 100644
--- a/chrome/browser/resources/tracing/profiling_view.js
+++ b/chrome/browser/resources/tracing/profiling_view.js
@@ -5,8 +5,8 @@
'use strict';
/**
- * @fileoverview ProfilingView visualizes TRACE_EVENT events using the
- * tracing.Timeline component.
+ * @fileoverview ProfilingView glues the TimelineView control to
+ * TracingController.
*/
cr.define('tracing', function() {
/**
@@ -27,13 +27,6 @@ cr.define('tracing', function() {
this.classList.add('profiling-view');
// make the <list>/add/save/record element
- this.controlDiv_ = document.createElement('div');
- this.controlDiv_.className = 'control';
- this.appendChild(this.controlDiv_);
-
- var tracingEl = document.createElement('span');
- tracingEl.textContent = 'Tracing: ';
-
this.recordBn_ = document.createElement('button');
this.recordBn_.className = 'record';
this.recordBn_.textContent = 'Record';
@@ -47,16 +40,6 @@ cr.define('tracing', function() {
this.loadBn_.textContent = 'Load';
this.loadBn_.addEventListener('click', this.onLoad_.bind(this));
- this.container_ = document.createElement('div');
- this.container_.className = 'container';
-
- this.timelineView_ = new tracing.TimelineView();
-
- this.controlDiv_.appendChild(tracingEl);
- this.controlDiv_.appendChild(this.recordBn_);
- this.controlDiv_.appendChild(this.loadBn_);
- this.controlDiv_.appendChild(this.saveBn_);
-
if (cr.isChromeOS) {
this.systemTracingBn_ = document.createElement('input');
this.systemTracingBn_.type = 'checkbox';
@@ -66,12 +49,16 @@ cr.define('tracing', function() {
systemTracingLabelEl.className = 'label';
systemTracingLabelEl.textContent = 'System events';
systemTracingLabelEl.appendChild(this.systemTracingBn_);
-
- this.controlDiv_.appendChild(systemTracingLabelEl);
}
- this.container_.appendChild(this.timelineView_);
- this.appendChild(this.container_);
+ this.timelineView_ = new tracing.TimelineView();
+ this.timelineView_.leftControls.appendChild(this.recordBn_);
+ this.timelineView_.leftControls.appendChild(this.saveBn_);
+ this.timelineView_.leftControls.appendChild(this.loadBn_);
+ if (cr.isChromeOS)
+ this.timelineView_.leftControls.appendChild(this.systemTracingBn_);
+
+ this.appendChild(this.timelineView_);
document.addEventListener('keypress', this.onKeypress_.bind(this));
diff --git a/chrome/browser/resources/tracing/profiling_view_test.html b/chrome/browser/resources/tracing/profiling_view_test.html
index 3297c4e..18121be 100644
--- a/chrome/browser/resources/tracing/profiling_view_test.html
+++ b/chrome/browser/resources/tracing/profiling_view_test.html
@@ -6,15 +6,29 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head i18n-values="dir:textdirection;">
-<title>Interactive Timeline Tests</title>
+<title>ProfilingView tests</title>
<link rel="stylesheet" href="profiling_view.css">
+<link rel="stylesheet" href="timeline_view.css">
+<link rel="stylesheet" href="overlay.css">
+<link rel="stylesheet" href="timeline.css">
<link rel="stylesheet" href="../shared/css/tabs.css">
<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<script src="../shared/js/cr.js"></script>
<script src="../shared/js/cr/event_target.js"></script>
<script src="../shared/js/cr/ui.js"></script>
<script src="../shared/js/cr/ui/tabs.js"></script>
+<script src="overlay.js"></script>
+<script src="measuring_stick.js"></script>
<script src="profiling_view.js"></script>
+<script src="timeline_view.js"></script>
+<script src="timeline_model.js"></script>
+<script src="linux_perf_importer.js"></script>
+<script src="trace_event_importer.js"></script>
+<script src="timeline.js"></script>
+<script src="timeline_track.js"></script>
+<script src="sorted_array_utils.js"></script>
+<script src="fast_rect_renderer.js"></script>
+<script src="test_utils.js"></script>
<script>
goog.require('goog.testing.jsunit');
</script>
@@ -32,8 +46,8 @@ found in the LICENSE file.
* Just enough of the TracingController to support the tests below.
*/
function FakeTracingController() {
-
}
+
FakeTracingController.prototype = {
__proto__: cr.EventTarget.prototype,
@@ -50,74 +64,33 @@ found in the LICENSE file.
get systemTraceEvents() {
if (!this.wasBeginTracingCalled)
- return undefined;
+ return [];
if (!this.wasBeginTracingCalledWithSystemTracingEnabled)
- return undefined;
+ return [];
return FakeTracingController.systemTraceTestData;
}
};
FakeTracingController.testData = [
- "hello",
- "world"
+ {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
+ {name: 'b', args: {}, pid: 52, ts: 629, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'b', args: {}, pid: 52, ts: 631, cat: 'foo', tid: 53, ph: 'E'}
];
FakeTracingController.systemTraceTestData = [
- "the kernel",
- "says it wants",
- "its memory back"
- ];
-
- /*
- * Just enough of the TimelineModel to support the tests below.
- */
- function FakeTimelineModel() {
-
- }
- FakeTimelineModel.prototype = {
- __proto__: Object.prototype,
-
- importEvents: function(eventData,
- opt_zeroAndBoost, opt_additionalEventData) {
- assertEquals(eventData, FakeTracingController.testData);
- if (cr.isChromeOS) {
- assertEquals(1, opt_additionalEventData.length);
- assertEquals(opt_additionalEventData[0], FakeTracingController.systemTraceTestData)
- }
- },
-
- something: function() {
- },
- };
-
- /*
- * Just enough of the TimelineView to support the tests below.
- */
- var FakeTimelineView = cr.ui.define('div');
-
- FakeTimelineView.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- decorate: function() {
- this.statusEl_ = document.createElement('span');
- this.appendChild(this.statusEl_);
- this.refresh_();
- },
-
- refresh_: function() {
- var status;
- if (this.timelineModel)
- status = "timelineModel";
- else
- status = "!timelineModel";
- this.statusEl_.textContent = status;
- },
- };
-
- /* Monkeypatch timeline model and view so ProfilingView
- * instantiates them instead.
- */
- tracing.TimelineModel = FakeTimelineModel;
- tracing.TimelineView = FakeTimelineView;
+ 'systrace.sh-8170 [001] 15180.978813: sched_switch: ' +
+ 'prev_comm=systrace.sh prev_pid=8170 prev_prio=120 ' +
+ 'prev_state=x ==> next_comm=kworker/1:0 next_pid=7873 ' +
+ 'next_prio=120',
+ ' kworker/1:0-7873 [001] 15180.978836: sched_switch: ' +
+ 'prev_comm=kworker/1:0 prev_pid=7873 prev_prio=120 ' +
+ 'prev_state=S ==> next_comm=debugd next_pid=4404 next_prio=120',
+ ' debugd-4404 [001] 15180.979010: sched_switch: prev_comm=debugd ' +
+ 'prev_pid=4404 prev_prio=120 prev_state=S ==> ' +
+ 'next_comm=dbus-daemon next_pid=510 next_prio=120',
+ 'systrace.sh-8182 [000] 15186.203900: tracing_mark_write: ' +
+ 'trace_event_clock_sync: parent_ts=0.0'
+ ].join('\n');
/* This test just instantiates a ProflingView and adds it to the DOM
* to help with non-unittest UI work.
@@ -125,6 +98,7 @@ found in the LICENSE file.
function testInstantiate() {
var view = new tracing.ProfilingView();
view.tracingController = new FakeTracingController();
+ view.focusElement = view;
document.body.appendChild(view);
}
@@ -136,7 +110,7 @@ found in the LICENSE file.
assertTrue(tracingController.wasBeginTracingCalled);
assertEquals(cr.isChromeOS,
tracingController.wasBeginTracingCalledWithSystemTracingEnabled);
-
+
var e = new cr.Event('traceEnded');
var didRefresh = false;
e.events = tracingController.traceEvents;
diff --git a/chrome/browser/resources/tracing/test_utils.js b/chrome/browser/resources/tracing/test_utils.js
index a8800f1..30dd5e6 100644
--- a/chrome/browser/resources/tracing/test_utils.js
+++ b/chrome/browser/resources/tracing/test_utils.js
@@ -38,7 +38,27 @@ cr.define('test_utils', function() {
};
req.send(null);
}
+
+ function newAsyncSlice(start, duration, startThread, endThread) {
+ return newAsyncSliceNamed('a', start, duration, startThread, endThread);
+ }
+
+ function newAsyncSliceNamed(name, start, duration, startThread, endThread) {
+ var s = new tracing.TimelineAsyncSlice(name, 0, start);
+ s.duration = duration;
+ s.startThread = startThread;
+ s.endThread = endThread;
+ var subSlice = new tracing.TimelineAsyncSlice(name, 0, start);
+ subSlice.duration = duration;
+ subSlice.startThread = startThread;
+ subSlice.endThread = endThread;
+ s.subSlices = [subSlice];
+ return s;
+ }
+
return {
- getAsync: getAsync
+ getAsync: getAsync,
+ newAsyncSlice: newAsyncSlice,
+ newAsyncSliceNamed: newAsyncSliceNamed
};
});
diff --git a/chrome/browser/resources/tracing/tests.html b/chrome/browser/resources/tracing/tests.html
index b00ed91..ec7b771 100644
--- a/chrome/browser/resources/tracing/tests.html
+++ b/chrome/browser/resources/tracing/tests.html
@@ -9,11 +9,14 @@ found in the LICENSE file.
<title>All Tracing Tests</title>
<script>
tests = [
+ 'overlay_test.html',
'timeline_model_test.html',
'timeline_track_test.html',
'linux_perf_importer_test.html',
'trace_event_importer_test.html',
'profiling_view_test.html',
+ 'timeline_test.html',
+ 'timeline_view_test.html',
];
</script>
<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
@@ -28,6 +31,7 @@ found in the LICENSE file.
var testRunner = new goog.testing.MultiTestRunner()
.setName(document.title)
.setBasePath('./')
+ .setPoolSize(8)
.setStatsBucketSizes(5, 500)
.setHidePasses(true)
.addTests(tests);
diff --git a/chrome/browser/resources/tracing/timeline.js b/chrome/browser/resources/tracing/timeline.js
index ef80c6e..e38ec41 100644
--- a/chrome/browser/resources/tracing/timeline.js
+++ b/chrome/browser/resources/tracing/timeline.js
@@ -194,6 +194,13 @@ cr.define('tracing', function() {
this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth);
},
+ xSetWorldRange: function(worldMin, worldMax, viewWidth) {
+ var worldRange = worldMax - worldMin;
+ var scaleX = viewWidth / worldRange;
+ var panX = -worldMin;
+ this.setPanAndScale(panX, scaleX);
+ },
+
get gridEnabled() {
return this.gridEnabled_;
},
@@ -415,16 +422,28 @@ cr.define('tracing', function() {
// 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);
+ this.viewport_.xSetWorldRange(this.model_.minTimestamp,
+ this.model_.maxTimestamp,
+ w);
}.bind(this));
},
/**
+ * @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;
+ },
+
+ /**
* @return {Element} The element whose focused state determines
* whether to respond to keyboard inputs.
* Defaults to the parent element.
@@ -444,6 +463,8 @@ cr.define('tracing', function() {
},
get listenToKeys_() {
+ if (!this.viewport_.isAttachedToDocument_)
+ return false;
if (!this.focusElement_)
return true;
if (this.focusElement.tabIndex >= 0)
@@ -586,6 +607,7 @@ cr.define('tracing', function() {
return false;
}
this.selection = selection;
+
// Potentially move the viewport to keep the new selection in view.
this.viewport_.xPanWorldRangeIntoView(minTime, maxTime,
this.firstCanvas.width);
@@ -630,6 +652,39 @@ cr.define('tracing', function() {
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) {
+ this.selection = selection;
+ var range = this.getSelectionRange();
+ var size = this.viewport_.xWorldVectorToView(range.max - range.min);
+ if (zoomAllowed && size < 50) {
+ var worldCenter = range.min + (range.max - range.min) * 0.5;
+ var worldRange = (range.max - range.min) * 5;
+ this.viewport_.xSetWorldRange(worldCenter - worldRange * 0.5,
+ worldCenter + worldRange * 0.5,
+ this.firstCanvas.width);
+ return;
+ }
+
+ this.viewport_.xPanWorldRangeIntoView(range.min, range.max,
+ this.firstCanvas.width);
+ },
+
get firstCanvas() {
return this.tracks_.firstChild ?
this.tracks_.firstChild.firstCanvas : undefined;
diff --git a/chrome/browser/resources/tracing/timeline_model.js b/chrome/browser/resources/tracing/timeline_model.js
index 6c86331d..193cea3 100644
--- a/chrome/browser/resources/tracing/timeline_model.js
+++ b/chrome/browser/resources/tracing/timeline_model.js
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+'use strict';
/**
* @fileoverview TimelineModel is a parsed representation of the
@@ -212,7 +213,6 @@ cr.define('tracing', function() {
', tid: ' + this.tid +
(this.name ? ', name: ' + this.name : '');
}
-
};
/**
@@ -579,7 +579,6 @@ cr.define('tracing', function() {
}
return groups;
}
-
};
/**
@@ -1006,7 +1005,7 @@ cr.define('tracing', function() {
if (opt_zeroAndBoost === undefined)
opt_zeroAndBoost = true;
- activeImporters = [];
+ var activeImporters = [];
var importer = this.importOneTrace_(eventData, false);
activeImporters.push(importer);
if (opt_additionalEventData) {
@@ -1036,6 +1035,24 @@ cr.define('tracing', function() {
}
};
+ /**
+ * @constructor A filter that can be passed into
+ * Timeline.findAllObjectsMatchingFilter
+ */
+ function TimelineFilter(text) {
+ this.text_ = text;
+ }
+ TimelineFilter.prototype = {
+ __proto__: Object.prototype,
+
+ matchSlice: function(slice) {
+ if (this.text_.length == 0)
+ return false;
+ return slice.title.indexOf(this.text_) != -1;
+ }
+
+ };
+
return {
getPallette: getPallette,
getPalletteHighlightIdBoost: getPalletteHighlightIdBoost,
@@ -1051,7 +1068,8 @@ cr.define('tracing', function() {
TimelineProcess: TimelineProcess,
TimelineCpu: TimelineCpu,
TimelineAsyncSliceGroup: TimelineAsyncSliceGroup,
- TimelineModel: TimelineModel
+ TimelineModel: TimelineModel,
+ TimelineFilter: TimelineFilter
};
});
diff --git a/chrome/browser/resources/tracing/timeline_model_test.html b/chrome/browser/resources/tracing/timeline_model_test.html
index 7b62520..eed1290 100644
--- a/chrome/browser/resources/tracing/timeline_model_test.html
+++ b/chrome/browser/resources/tracing/timeline_model_test.html
@@ -10,6 +10,7 @@ found in the LICENSE file.
<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<script src="../shared/js/cr.js"></script>
<script src="../shared/js/cr/event_target.js"></script>
+<script src="test_utils.js"></script>
<script src="timeline_model.js"></script>
<script>
goog.require('goog.testing.jsunit');
@@ -25,22 +26,10 @@ var TimelineThreadSlice = tracing.TimelineThreadSlice;
var TimelineProcess = tracing.TimelineProcess;
var TimelineThread = tracing.TimelineThread;
var TimelineModel = tracing.TimelineModel;
+var TimelineFilter = tracing.TimelineFilter;
var TimelineAsyncSlice = tracing.TimelineAsyncSlice;
var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup;
-
-// Helper function to create a slice.
-function newAsyncSlice(start, duration, startThread, endThread) {
- var s = new TimelineAsyncSlice('a', 0, start);
- s.duration = duration;
- s.startThread = startThread;
- s.endThread = endThread;
- var subSlice = new TimelineAsyncSlice('a', 0, start);
- subSlice.duration = duration;
- subSlice.startThread = startThread;
- subSlice.endThread = endThread;
- s.subSlices = [subSlice];
- return s;
-}
+var newAsyncSlice = test_utils.newAsyncSlice;
function testThreadBounds_Empty() {
var t = new TimelineThread(new TimelineProcess(7), 1);
@@ -248,6 +237,20 @@ function testModelCanImportEmpty() {
m = new TimelineModel([]);
m = new TimelineModel('');
}
+
+function testTimelineFilter() {
+ var s0 = new TimelineSlice('a', 0, 1, {}, 3);
+ assertFalse(new TimelineFilter('').matchSlice(s0));
+
+ assertTrue(new TimelineFilter('a').matchSlice(s0));
+ assertFalse(new TimelineFilter('x').matchSlice(s0));
+
+ var s1 = new TimelineSlice('ba', 0, 1, {}, 3);
+ assertTrue(new TimelineFilter('a').matchSlice(s1));
+ assertTrue(new TimelineFilter('ba').matchSlice(s1));
+ assertFalse(new TimelineFilter('x').matchSlice(s1));
+}
+
</script>
</body>
</html>
diff --git a/chrome/browser/resources/tracing/timeline_test.html b/chrome/browser/resources/tracing/timeline_test.html
index f735653..f6b718c 100644
--- a/chrome/browser/resources/tracing/timeline_test.html
+++ b/chrome/browser/resources/tracing/timeline_test.html
@@ -6,96 +6,87 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head i18n-values="dir:textdirection;">
-<title>Interactive Timeline Tests</title>
+<title>Timeline tests</title>
+<link rel="stylesheet" href="overlay.css">
+<link rel="stylesheet" href="timeline_view.css">
<link rel="stylesheet" href="timeline.css">
+<link rel="stylesheet" href="../shared/css/tabs.css">
+<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
<script src="../shared/js/cr.js"></script>
<script src="../shared/js/cr/event_target.js"></script>
<script src="../shared/js/cr/ui.js"></script>
-<script src="../shared/js/util.js"></script>
+<script src="../shared/js/cr/ui/tabs.js"></script>
+<script src="overlay.js"></script>
+<script src="measuring_stick.js"></script>
+<script src="profiling_view.js"></script>
+<script src="timeline_view.js"></script>
<script src="timeline_model.js"></script>
<script src="linux_perf_importer.js"></script>
<script src="trace_event_importer.js"></script>
-<script src="sorted_array_utils.js"></script>
-<script src="measuring_stick.js"></script>
<script src="timeline.js"></script>
<script src="timeline_track.js"></script>
+<script src="sorted_array_utils.js"></script>
<script src="fast_rect_renderer.js"></script>
<script src="test_utils.js"></script>
+<script>
+ goog.require('goog.testing.jsunit');
+</script>
+<style>
+</style>
</head>
<body>
- <div class="timeline-test" src="./tests/trivial_trace.json" create-detached=1>
- </div>
-
- <div class="timeline-test" src="./tests/trivial_trace.json">
- </div>
-
- <div class="timeline-test" src="./tests/simple_trace.json">
- </div>
-
- <div class="timeline-test" src="./tests/instance_counters.json">
- </div>
+ <script>
+ 'use strict';
- <div class="timeline-test" src="./tests/tall_trace.json">
- </div>
+ /*
+ * This test just instantiates a TimelineView and adds it to the DOM
+ * to help with non-unittest UI work.
+ */
+ function testInstantiateTimeline() {
+ var events = [
+ {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
+ {name: 'b', args: {}, pid: 52, ts: 629, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'b', args: {}, pid: 52, ts: 631, cat: 'foo', tid: 53, ph: 'E'}
+ ];
+ var model = new tracing.TimelineModel();
+ model.importEvents(events);
+ var timeline = new tracing.Timeline();
+ timeline.model = model;
+ document.body.appendChild(timeline);
+ }
- <div class="timeline-test" src="./tests/big_trace.json">
- </div>
+ function testFindAllObjectsMatching() {
+ var model = new tracing.TimelineModel();
+ var p1 = model.getOrCreateProcess(1);
+ var t1 = p1.getOrCreateThread(1);
- <div class="timeline-test" src="./tests/huge_trace.json">
- </div>
+ t1.subRows[0].push(new tracing.TimelineThreadSlice('a', 0, 1, {}, 3));
+ t1.subRows[0].push(new tracing.TimelineThreadSlice('b', 0, 1, {}, 3));
- <div class="timeline-test" src="./tests/main_thread_has_unclosed_slices.json">
- </div>
+ var t1asg = t1.asyncSlices;
+ t1asg.slices.push(test_utils.newAsyncSliceNamed('a', 0, 1, t1, t1));
+ t1asg.slices.push(test_utils.newAsyncSliceNamed('b', 1, 2, t1, t1));
- <div class="timeline-test" src="./tests/async_begin_end.json">
- </div>
- <script>
- function load(parentEl) {
- var src = parentEl.getAttribute('src');
- if (document.location.hash && document.location.hash.substring(1) != src) {
- parentEl.hidden = true;
- return;
- }
- parentEl.hidden = false;
- parentEl.textContent = '';
- var titleEl = document.createElement('h3');
- var linkEl = document.createElement('a');
- linkEl.textContent = src;
- linkEl.href = '#' + src;
- titleEl.appendChild(linkEl);
+ var timeline = new tracing.Timeline();
+ timeline.model = model;
- var containerEl = document.createElement('div');
- containerEl.tabIndex = 0;
- containerEl.style.border = '1px solid red';
+ var expected = [{slice: t1asg.slices[0].subSlices[0]},
+ {slice: t1.subRows[0][0]}];
+ var result = timeline.findAllObjectsMatchingFilter(new tracing.TimelineFilter('a'));
+ assertEquals(2, result.length);
+ assertEquals(expected[0].slice, result[0].slice);
+ assertEquals(expected[1].slice, result[1].slice);
- var timelineEl = document.createElement('div');
- cr.ui.decorate(timelineEl, tracing.Timeline);
- timelineEl.focusElement = containerEl;
-
- parentEl.appendChild(titleEl);
- parentEl.appendChild(containerEl);
-
- // Creating attached vs detached stress tests the canvas- and viewport-
- // setup code.
- var create_detached = parentEl.getAttribute('create-attached') == 1;
- function createModel(data) {
- timelineEl.model = new tracing.TimelineModel(data);
- if (!create_detached)
- containerEl.appendChild(timelineEl);
+ var expected = [{slice: t1asg.slices[1].subSlices[0]},
+ {slice: t1.subRows[0][1]}];
+ var result = timeline.findAllObjectsMatchingFilter(new tracing.TimelineFilter('b'));
+ assertEquals(2, result.length);
+ assertEquals(expected[0].slice, result[0].slice);
+ assertEquals(expected[1].slice, result[1].slice);
}
- if (create_detached)
- containerEl.appendChild(timelineEl);
- test_utils.getAsync(src, createModel);
- }
-
- function onLoad() {
- Array.prototype.forEach.call(document.querySelectorAll('.timeline-test'),
- load);
- }
- document.addEventListener('DOMContentLoaded', onLoad);
- window.addEventListener('hashchange', onLoad);
</script>
</body>
</html>
diff --git a/chrome/browser/resources/tracing/timeline_track.js b/chrome/browser/resources/tracing/timeline_track.js
index 23057af..3cd2e06 100644
--- a/chrome/browser/resources/tracing/timeline_track.js
+++ b/chrome/browser/resources/tracing/timeline_track.js
@@ -108,6 +108,18 @@ cr.define('tracing', function() {
if (a <= b)
this.tracks_[i].pickRange(loWX, hiWX, loY, hiY, onHitCallback);
}
+ },
+
+ /**
+ * @return {Array} Objects matching the given filter.
+ */
+ findAllObjectsMatchingFilter: function(filter) {
+ var hits = [];
+ for (var i = 0; i < this.tracks_.length; i++) {
+ var trackHits = this.tracks_[i].findAllObjectsMatchingFilter(filter);
+ Array.prototype.push.apply(hits, trackHits);
+ }
+ return hits;
}
};
@@ -756,8 +768,16 @@ cr.define('tracing', function() {
else if ((index != undefined) && (index > 0))
index--;
return index != undefined ? this.slices_[index] : undefined;
- }
+ },
+ findAllObjectsMatchingFilter: function(filter) {
+ var hits = [];
+ for (var i = 0; i < this.slices_.length; ++i)
+ if (filter.matchSlice(this.slices_[i]))
+ hits.push({track: this,
+ slice: this.slices_[i]});
+ return hits;
+ }
};
/**
@@ -905,6 +925,10 @@ cr.define('tracing', function() {
pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
// Does nothing. There's nothing interesting to pick on the viewport
// track.
+ },
+
+ findAllObjectsMatchingFilter: function(filter) {
+ return [];
}
};
@@ -1051,6 +1075,10 @@ cr.define('tracing', function() {
* intersecting the interval.
*/
pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
+ },
+
+ findAllObjectsMatchingFilter: function(filter) {
+ return [];
}
};
diff --git a/chrome/browser/resources/tracing/timeline_track_test.html b/chrome/browser/resources/tracing/timeline_track_test.html
index 6a8fb99..855b0349 100644
--- a/chrome/browser/resources/tracing/timeline_track_test.html
+++ b/chrome/browser/resources/tracing/timeline_track_test.html
@@ -40,6 +40,8 @@ found in the LICENSE file.
<script>
</script>
<script>
+ 'use strict';
+
var TimelineAsyncSlice = tracing.TimelineAsyncSlice;
var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup;
var TimelineCounter = tracing.TimelineCounter;
@@ -89,21 +91,19 @@ found in the LICENSE file.
track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end));
}
- function testBasicSlicesWithAsyncFlag() {
- var testEl = getTestDiv('testBasicSlicesWithAsyncFlag');
+ function testFindAllObjectsMatchingInSliceTrack() {
var track = TimelineSliceTrack();
- testEl.appendChild(track);
- track.asyncStyle = true;
- track.heading = 'testBasicSlices+AsyncFlag';
track.slices = [
new TimelineSlice('a', 0, 1, {}, 1),
new TimelineSlice('b', 1, 2.1, {}, 4.8),
new TimelineSlice('b', 1, 7, {}, 0.5),
new TimelineSlice('c', 2, 7.6, {}, 0.4)
];
- track.viewport = new TimelineViewport(testEl);
- track.viewport.setPanAndScale(0,
- track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end));
+ var hits = track.findAllObjectsMatchingFilter(
+ new tracing.TimelineFilter("b"));
+ assertEquals(2, hits.length);
+ assertEquals(track.slices[1], hits[0].slice);
+ assertEquals(track.slices[2], hits[1].slice);
}
function testShrinkingSliceSizes() {
@@ -192,7 +192,6 @@ found in the LICENSE file.
var ctr = new TimelineCounter(undefined,
'testBasicCounter', 'testBasicCounter');
- ctr.numSeries = 1;
ctr.seriesNames = ['value1', 'value2'];
ctr.seriesColors = [tracing.getStringColorId('testBasicCounter.value1'),
tracing.getStringColorId('testBasicCounter.value2')];
@@ -220,8 +219,8 @@ found in the LICENSE file.
function testElideVisualInspection() {
var optDicts = [{ trackName: 'elideOff', elide: false },
{ trackName: 'elideOn', elide: true }];
- for (dictIndex in optDicts) {
- dict = optDicts[dictIndex];
+ for (var dictIndex in optDicts) {
+ var dict = optDicts[dictIndex];
var testEl = getTestDiv(dict.trackName);
var track = TimelineSliceTrack();
if (dict.elide) {
diff --git a/chrome/browser/resources/tracing/timeline_view.css b/chrome/browser/resources/tracing/timeline_view.css
index f5bc159..0e89f89 100644
--- a/chrome/browser/resources/tracing/timeline_view.css
+++ b/chrome/browser/resources/tracing/timeline_view.css
@@ -9,26 +9,40 @@
padding: 0;
}
-.timeline-view > .timeline {
- -webkit-box-flex: 1;
+.timeline-view > .control {
+ border-bottom: 1px solid #555;
display: -webkit-box;
- overflow: auto;
}
-.timeline-view .timeline-container {
+.timeline-view > .control > span {
+ padding-left: 5px;
+ padding-right: 10px;
+}
+
+.timeline-view > .control > button {
+ font-size: 75%;
+ height: 20px;
+ min-height: 10px;
+}
+
+.timeline-view > .control > .spacer {
+ -webkit-box-flex: 1;
+}
+
+.timeline-view > .timeline-container {
-webkit-box-flex: 1;
display: -webkit-box;
overflow: auto;
}
-.timeline-view .timeline-container > * {
+.timeline-view > .timeline-container > * {
-webkit-box-flex: 1;
}
-.timeline-view .summary-container * {
+.timeline-view > .summary-container * {
-webkit-user-select: text;
}
-.timeline-view .summary-container {
+.timeline-view > .summary-container {
border-top: 1px solid black;
font-family: monospace;
max-height: 250px;
@@ -43,3 +57,43 @@
.timeline-view .selection ul {
margin: 0;
}
+
+.timeline-find-control {
+ -webkit-user-select: none;
+ position: relative;
+}
+
+.timeline-find-control .hit-count-label {
+ left: 0;
+ opacity: 0.25;
+ pointer-events: none;
+ position: absolute;
+ text-align: right;
+ top: 2px;
+ width: 170px;
+ z-index: 1;
+}
+
+.timeline-find-control input {
+ -webkit-user-select: auto;
+ margin-right: 1px;
+ width: 170px;
+}
+
+.timeline-find-control .find-button {
+ background-color: rgba(255, 255, 255, 0.5);
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ color: rgba(0,0,0,0.2);
+ font-size: 14px;
+ height: 23px;
+ text-align: center;
+ width: 23px;
+}
+
+.timeline-find-control .find-button:hover {
+ background-color: rgba(255, 255, 255, 1.0);
+ border: 1px solid rgba(0, 0, 0, 0.8);
+ border-radius: 25%;
+ box-shadow: 0 0 .05em rgba(0, 0, 0, 0.4);
+ color: rgba(0, 0, 0, 1);
+}
diff --git a/chrome/browser/resources/tracing/timeline_view.js b/chrome/browser/resources/tracing/timeline_view.js
index 1f50b9e..c593f528 100644
--- a/chrome/browser/resources/tracing/timeline_view.js
+++ b/chrome/browser/resources/tracing/timeline_view.js
@@ -6,7 +6,7 @@
/**
* @fileoverview TimelineView visualizes TRACE_EVENT events using the
- * tracing.Timeline component.
+ * tracing.Timeline component and adds in selection summary and control buttons.
*/
cr.define('tracing', function() {
function tsRound(ts) {
@@ -36,6 +36,189 @@ cr.define('tracing', function() {
}
/**
+ * TimelineFindControl
+ * @constructor
+ * @extends {tracing.Overlay}
+ */
+ var TimelineFindControl = cr.ui.define('div');
+
+ TimelineFindControl.prototype = {
+ __proto__: tracing.Overlay.prototype,
+
+ decorate: function() {
+ tracing.Overlay.prototype.decorate.call(this);
+
+ this.className = 'timeline-find-control';
+
+ this.hitCountEl_ = document.createElement('span');
+ this.hitCountEl_.className = 'hit-count-label';
+ this.hitCountEl_.textContent = '1 of 7';
+
+ var findPreviousBn = document.createElement('span');
+ findPreviousBn.className = 'find-button find-previous';
+ findPreviousBn.textContent = '\u2190';
+ findPreviousBn.addEventListener('click', function() {
+ this.controller.findPrevious();
+ this.updateHitCountEl_();
+ }.bind(this));
+
+ var findNextBn = document.createElement('span');
+ findNextBn.className = 'find-button find-next';
+ findNextBn.textContent = '\u2192';
+ findNextBn.addEventListener('click', function() {
+ this.controller.findNext();
+ this.updateHitCountEl_();
+ }.bind(this));
+
+ // Filter input element.
+ this.filterEl_ = document.createElement('input');
+ this.filterEl_.type = 'input';
+
+ this.filterEl_.addEventListener('input', function(e) {
+ this.controller.filterText = this.filterEl_.value;
+ this.updateHitCountEl_();
+ }.bind(this));
+
+ this.filterEl_.addEventListener('keydown', function(e) {
+ if (e.keyCode == 13) {
+ findNextBn.click();
+ } else if (e.keyCode == 27) {
+ this.filterEl_.blur();
+ this.updateHitCountEl_();
+ }
+ }.bind(this));
+
+ this.filterEl_.addEventListener('blur', function(e) {
+ this.updateHitCountEl_();
+ }.bind(this));
+
+ this.filterEl_.addEventListener('focus', function(e) {
+ this.updateHitCountEl_();
+ }.bind(this));
+
+ // Attach everything.
+ this.appendChild(this.filterEl_);
+
+ this.appendChild(findPreviousBn);
+ this.appendChild(findNextBn);
+ this.appendChild(this.hitCountEl_);
+
+ this.updateHitCountEl_();
+ },
+
+ get controller() {
+ return this.controller_;
+ },
+
+ set controller(c) {
+ this.controller_ = c;
+ this.updateHitCountEl_();
+ },
+
+ focus: function() {
+ this.filterEl_.selectionStart = 0;
+ this.filterEl_.selectionEnd = this.filterEl_.value.length;
+ this.filterEl_.focus();
+ },
+
+ updateHitCountEl_: function() {
+ if (!this.controller || document.activeElement != this.filterEl_) {
+ this.hitCountEl_.textContent = '';
+ return;
+ }
+ var i = this.controller.currentHitIndex;
+ var n = this.controller.filterHits.length;
+ if (n == 0)
+ this.hitCountEl_.textContent = '0 of 0';
+ else
+ this.hitCountEl_.textContent = (i + 1) + ' of ' + n;
+ }
+ };
+
+ function TimelineFindController() {
+ this.timeline_ = undefined;
+ this.model_ = undefined;
+ this.filterText_ = '';
+ this.filterHitsDirty_ = true;
+ this.currentHitIndex_ = 0;
+ };
+
+ TimelineFindController.prototype = {
+ __proto__: Object.prototype,
+
+ get timeline() {
+ return this.timeline_;
+ },
+
+ set timeline(t) {
+ this.timeline_ = t;
+ this.filterHitsDirty_ = true;
+ },
+
+ get filterText() {
+ return this.filterText_;
+ },
+
+ set filterText(f) {
+ if (f == this.filterText_)
+ return;
+ this.filterText_ = f;
+ this.filterHitsDirty_ = true;
+ this.findNext();
+ },
+
+ get filterHits() {
+ if (this.filterHitsDirty_) {
+ this.filterHitsDirty_ = false;
+ if (this.timeline_) {
+ var filter = new tracing.TimelineFilter(this.filterText);
+ this.filterHits_ = this.timeline.findAllObjectsMatchingFilter(filter);
+ this.currentHitIndex_ = this.filterHits_.length - 1;
+ } else {
+ this.filterHits_ = [];
+ this.currentHitIndex_ = 0;
+ }
+ }
+ return this.filterHits_;
+ },
+
+ get currentHitIndex() {
+ return this.currentHitIndex_;
+ },
+
+ find_: function(dir) {
+ if (!this.timeline)
+ return;
+
+ var N = this.filterHits.length;
+ this.currentHitIndex_ = this.currentHitIndex_ + dir;
+
+ if (this.currentHitIndex_ < 0) this.currentHitIndex_ = N - 1;
+ if (this.currentHitIndex_ >= N) this.currentHitIndex_ = 0;
+
+ if (this.currentHitIndex_ < 0 || this.currentHitIndex_ >= N) {
+ this.timeline.selection = [];
+ return;
+ }
+
+ var hit = this.filterHits[this.currentHitIndex_];
+
+ // We allow the zoom level to change on the first hit level. But, when
+ // then cycling through subsequent changes, restrict it to panning.
+ var zoomAllowed = this.currentHitIndex_ == 0;
+ this.timeline.setSelectionAndMakeVisible([hit], zoomAllowed);
+ },
+
+ findNext: function() {
+ this.find_(1);
+ },
+
+ findPrevious: function() {
+ this.find_(-1);
+ },
+ };
+
+ /**
* TimelineView
* @constructor
* @extends {HTMLDivElement}
@@ -48,6 +231,19 @@ cr.define('tracing', function() {
decorate: function() {
this.classList.add('timeline-view');
+ // Create individual elements.
+ this.titleEl_ = document.createElement('span');
+ this.titleEl_.textContent = 'Tracing: ';
+
+ this.controlDiv_ = document.createElement('div');
+ this.controlDiv_.className = 'control';
+
+ this.leftControlsEl_ = document.createElement('div');
+ this.rightControlsEl_ = document.createElement('div');
+
+ var spacingEl = document.createElement('div');
+ spacingEl.className = 'spacer';
+
this.timelineContainer_ = document.createElement('div');
this.timelineContainer_.className = 'timeline-container';
@@ -57,11 +253,42 @@ cr.define('tracing', function() {
this.summaryEl_ = document.createElement('pre');
this.summaryEl_.className = 'summary';
- summaryContainer_.appendChild(this.summaryEl_);
+ this.findCtl_ = new TimelineFindControl();
+ this.findCtl_.controller = new TimelineFindController();
+
+ // Connect everything up.
+ this.rightControls.appendChild(this.findCtl_);
+ this.controlDiv_.appendChild(this.titleEl_);
+ this.controlDiv_.appendChild(this.leftControlsEl_);
+ this.controlDiv_.appendChild(spacingEl);
+ this.controlDiv_.appendChild(this.rightControlsEl_);
+ this.appendChild(this.controlDiv_);
+
this.appendChild(this.timelineContainer_);
+
+ summaryContainer_.appendChild(this.summaryEl_);
this.appendChild(summaryContainer_);
+ // Bookkeeping.
this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this);
+ document.addEventListener('keypress', this.onKeypress_.bind(this), true);
+ },
+
+ get leftControls() {
+ return this.leftControlsEl_;
+ },
+
+ get rightControls() {
+ return this.rightControlsEl_;
+ },
+
+ get title() {
+ return this.titleEl_.textContent.substring(
+ this.titleEl_.textContent.length - 2);
+ },
+
+ set title(text) {
+ this.titleEl_.textContent = text + ':';
},
set traceData(traceData) {
@@ -84,13 +311,17 @@ cr.define('tracing', function() {
this.timeline_.detach();
this.timeline_ = new tracing.Timeline();
this.timeline_.model = this.timelineModel_;
- this.timeline_.focusElement = this.parentElement;
+ this.timeline_.focusElement =
+ this.focusElement_ ? this.focusElement_ : this.parentElement;
this.timelineContainer_.appendChild(this.timeline_);
this.timeline_.addEventListener('selectionChange',
this.onSelectionChangedBoundToThis_);
+
+ this.findCtl_.controller.timeline = this.timeline_;
this.onSelectionChanged_();
} else {
- this.timeline_ = null;
+ this.timeline_ = undefined;
+ this.findCtl_.controller.timeline = undefined;
}
},
@@ -98,6 +329,76 @@ cr.define('tracing', function() {
return this.timeline_;
},
+ /**
+ * Sets the element whose focus state will determine whether
+ * to respond to keybaord input.
+ */
+ set focusElement(value) {
+ this.focusElement_ = value;
+ if (this.timeline_)
+ this.timeline_.focusElement = value;
+ },
+
+ /**
+ * @return {Element} The element whose focused state determines
+ * whether to respond to keyboard inputs.
+ * Defaults to the parent element.
+ */
+ get focusElement() {
+ if (this.focusElement_)
+ return this.focusElement_;
+ return this.parentElement;
+ },
+
+ /**
+ * @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;
+ },
+
+ get listenToKeys_() {
+ if (!this.isAttachedToDocument_)
+ return;
+ if (!this.focusElement_)
+ return true;
+ if (this.focusElement.tabIndex >= 0)
+ return document.activeElement == this.focusElement;
+ return true;
+ },
+
+ onKeypress_: function(e) {
+ if (!this.listenToKeys_)
+ return;
+
+ if (event.keyCode == 47) {
+ this.findCtl_.focus();
+ event.preventDefault();
+ return;
+ }
+ },
+
+ beginFind: function() {
+ if (this.findInProgress_)
+ return;
+ this.findInProgress_ = true;
+ var dlg = TimelineFindControl();
+ dlg.controller = new TimelineFindController();
+ dlg.controller.timeline = this.timeline;
+ dlg.visible = true;
+ dlg.addEventListener('close', function() {
+ this.findInProgress_ = false;
+ }.bind(this));
+ dlg.addEventListener('findNext', function() {
+ });
+ dlg.addEventListener('findPrevious', function() {
+ });
+ },
+
onSelectionChanged_: function(e) {
var timeline = this.timeline_;
var selection = timeline.selection;
@@ -193,6 +494,8 @@ cr.define('tracing', function() {
};
return {
+ TimelineFindControl: TimelineFindControl,
+ TimelineFindController: TimelineFindController,
TimelineView: TimelineView
};
});
diff --git a/chrome/browser/resources/tracing/timeline_view_test.html b/chrome/browser/resources/tracing/timeline_view_test.html
new file mode 100644
index 0000000..d24cb7f
--- /dev/null
+++ b/chrome/browser/resources/tracing/timeline_view_test.html
@@ -0,0 +1,257 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Copyright (c) 2012 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<head i18n-values="dir:textdirection;">
+<title>TimelineView tests</title>
+<link rel="stylesheet" href="overlay.css">
+<link rel="stylesheet" href="timeline.css">
+<link rel="stylesheet" href="timeline_view.css">
+<link rel="stylesheet" href="../shared/css/tabs.css">
+<script src="http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js"></script>
+<script src="../shared/js/cr.js"></script>
+<script src="../shared/js/cr/event_target.js"></script>
+<script src="../shared/js/cr/ui.js"></script>
+<script src="../shared/js/cr/ui/tabs.js"></script>
+<script src="overlay.js"></script>
+<script src="measuring_stick.js"></script>
+<script src="profiling_view.js"></script>
+<script src="timeline_view.js"></script>
+<script src="timeline_model.js"></script>
+<script src="linux_perf_importer.js"></script>
+<script src="trace_event_importer.js"></script>
+<script src="timeline.js"></script>
+<script src="timeline_track.js"></script>
+<script src="sorted_array_utils.js"></script>
+<script src="fast_rect_renderer.js"></script>
+<script src="test_utils.js"></script>
+<script>
+ goog.require('goog.testing.jsunit');
+</script>
+<style>
+ .timeline-view {
+ border: 1px solid black;
+ margin: 10px;
+ }
+ .timeline-find-dialog {
+ border: 1px solid black;
+ margin: 10px;
+ }
+</style>
+</head>
+<body>
+ <script>
+ 'use strict';
+
+ /*
+ * Just enough of the Timeline to support the tests below.
+ */
+ var FakeTimeline = cr.ui.define('div');
+
+ FakeTimeline.prototype = {
+ __proto__: HTMLDivElement.prototype,
+
+ decorate: function() {
+ this.findAllObjectsMatchingFilterReturnValue = [];
+
+ this.selection = [];
+ this.keyHelp = "<keyHelp>";
+
+ // Put some simple UI in for testing purposes.
+ var noteEl = document.createElement('div');
+ noteEl.textContent = "FakeTimeline:";
+ this.appendChild(noteEl);
+
+ this.statusEl_ = document.createElement('div');
+ this.appendChild(this.statusEl_);
+ this.refresh_();
+ },
+
+ refresh_: function() {
+ var status;
+ if (this.model)
+ status = "model=set";
+ else
+ status = "model=undefined";
+ this.statusEl_.textContent = status;
+ },
+
+ setSelectionAndMakeVisible: function(selection, zoomAllowed) {
+ this.selection = selection;
+ },
+
+ findAllObjectsMatchingFilter: function(filter) {
+ return this.findAllObjectsMatchingFilterReturnValue;
+ }
+ };
+
+ /*
+ * This test just instantiates a TimelineView and adds it to the DOM
+ * to help with non-unittest UI work.
+ */
+ function testInstantiateTimelineView() {
+ var events = [
+ {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
+ {name: 'b', args: {}, pid: 52, ts: 629, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'b', args: {}, pid: 52, ts: 631, cat: 'foo', tid: 53, ph: 'E'},
+ {name: 'a', args: {}, pid: 52, ts: 640, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'a', args: {}, pid: 52, ts: 700, cat: 'foo', tid: 53, ph: 'E'},
+ {name: 'a', args: {}, pid: 52, ts: 710, cat: 'foo', tid: 53, ph: 'B'},
+ {name: 'a', args: {}, pid: 52, ts: 740, cat: 'foo', tid: 53, ph: 'E'}
+ ];
+ var model = new tracing.TimelineModel();
+ model.importEvents(events);
+ var view = new tracing.TimelineView();
+ view.model = model;
+ view.tabIndex = 0;
+ view.focusElement = view;
+
+ document.body.appendChild(view);
+ }
+
+ /*
+ * This test just instantiates a FindDialog and adds it to the DOM
+ * to help with non-unittest UI work.
+ */
+ function testInstantiateTimelineFindControl() {
+ var ctl = new tracing.TimelineFindControl();
+ var didFindPrevious = false;
+ var didFindNext = false;
+ ctl.controller = {
+ findNext: function() {
+ didFindNext = true;
+ },
+
+ findPrevious: function() {
+ didFindPrevious = true;
+ },
+
+ filterHits: [],
+
+ currentHitIndex: 0,
+ }
+ document.body.appendChild(ctl);
+ ctl.querySelector('input').focus();
+ ctl.querySelector('input').blur();
+
+ ctl.querySelector('.find-previous').click();
+ assertTrue(didFindPrevious);
+ ctl.querySelector('.find-next').click();
+ assertTrue(didFindNext);
+ }
+
+ function testFindControllerNoTimeline() {
+ var controller = new tracing.TimelineFindController();
+ controller.findNext();
+ controller.findPrevious();
+ }
+
+ function testFindControllerEmptyHit() {
+ var timeline = new FakeTimeline();
+ var controller = new tracing.TimelineFindController();
+ controller.timeline = timeline;
+
+ timeline.selection = [];
+ controller.findNext();
+ assertArrayEquals([], timeline.selection);
+ controller.findPrevious();
+ assertArrayEquals([], timeline.selection);
+ }
+
+ function testFindControllerOneHit() {
+ var timeline = new FakeTimeline();
+ var controller = new tracing.TimelineFindController();
+ controller.timeline = timeline;
+
+ timeline.findAllObjectsMatchingFilterReturnValue = [1];
+ controller.findNext();
+ assertArrayEquals([1], timeline.selection);
+ controller.findNext();
+ assertArrayEquals([1], timeline.selection);
+ controller.findPrevious();
+ assertArrayEquals([1], timeline.selection);
+ }
+
+ function testFindControllerMultipleHits() {
+ var timeline = new FakeTimeline();
+ var controller = new tracing.TimelineFindController();
+ controller.timeline = timeline;
+
+ timeline.findAllObjectsMatchingFilterReturnValue = [1,2,3];
+
+ // Loop through hits then when we wrap, try moving backward.
+ controller.findNext();
+ assertArrayEquals([1], timeline.selection);
+ controller.findNext();
+ assertArrayEquals([2], timeline.selection);
+ controller.findNext();
+ assertArrayEquals([3], timeline.selection);
+ controller.findNext();
+ assertArrayEquals([1], timeline.selection);
+ controller.findPrevious();
+ assertArrayEquals([3], timeline.selection);
+ controller.findPrevious();
+ assertArrayEquals([2], timeline.selection);
+ }
+
+ function testFindControllerChangeFilterAfterNext() {
+ var timeline = new FakeTimeline();
+ var controller = new tracing.TimelineFindController();
+ controller.timeline = timeline;
+
+ timeline.findAllObjectsMatchingFilterReturnValue = [1,2,3];
+
+ // Loop through hits then when we wrap, try moving backward.
+ controller.findNext();
+ timeline.findAllObjectsMatchingFilterReturnValue = [4];
+ controller.filterText = "asdfsf";
+ controller.findNext();
+ assertArrayEquals([4], timeline.selection);
+ }
+
+ function testFindControllerSelectsFirstItemImmediately() {
+ var timeline = new FakeTimeline();
+ var controller = new tracing.TimelineFindController();
+ controller.timeline = timeline;
+ timeline.findAllObjectsMatchingFilterReturnValue = [1,2,3];
+ controller.filterText = "asdfsf";
+ assertArrayEquals([1], timeline.selection);
+ controller.findNext();
+ assertArrayEquals([2], timeline.selection);
+ }
+
+ function testFindControllerWithRealTimeline() {
+ var model = new tracing.TimelineModel();
+ var p1 = model.getOrCreateProcess(1);
+ var t1 = p1.getOrCreateThread(1);
+ t1.subRows[0].push(new tracing.TimelineThreadSlice('a', 0, 1, {}, 3));
+
+ var timeline = new tracing.Timeline();
+ timeline.model = model;
+
+ var controller = new tracing.TimelineFindController();
+ controller.timeline = timeline;
+
+ // Test find with no filterText.
+ controller.findNext();
+
+ // Test find with filter txt.
+ controller.filterText = 'a';
+ controller.findNext();
+ assertEquals(1, timeline.selection.length);
+ assertEquals(t1.subRows[0][0], timeline.selection[0].slice);
+
+ controller.filterText = 'xxx';
+ controller.findNext();
+ assertEquals(0, timeline.selection.length);
+ controller.findNext();
+ assertEquals(0, timeline.selection.length);
+ }
+
+ </script>
+</body>
+</html>