cros: Remove default pinned apps trial.
[chromium-blink-merge.git] / chrome / browser / resources / net_internals / waterfall_view.js
blob5c78ea5dc24067d3e2f51097dd6490082c6ca42a
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // TODO(viona): Write a README document/instructions.
7 /** This view displays the event waterfall. */
8 var WaterfallView = (function() {
9   'use strict';
11   // We inherit from DivView.
12   var superClass = DivView;
14   /**
15    * @constructor
16    */
17   function WaterfallView() {
18     assertFirstConstructorCall(WaterfallView);
20     // Call superclass's constructor.
21     superClass.call(this, WaterfallView.MAIN_BOX_ID);
23     SourceTracker.getInstance().addSourceEntryObserver(this);
25     // For adjusting the range of view.
26     $(WaterfallView.SCALE_ID).addEventListener(
27         'click', this.setStartEndTimes_.bind(this), true);
29     $(WaterfallView.MAIN_BOX_ID).addEventListener(
30         'mousewheel', this.scrollToZoom_.bind(this), true);
32     $(WaterfallView.MAIN_BOX_ID).addEventListener(
33         'scroll', this.scrollInfoTable_.bind(this), true);
35     this.initializeSourceList_();
37     window.onload = this.scrollInfoTable_();
38   }
40   WaterfallView.TAB_ID = 'tab-handle-waterfall';
41   WaterfallView.TAB_NAME = 'Waterfall';
42   WaterfallView.TAB_HASH = '#waterfall';
44   // IDs for special HTML elements in events_waterfall_view.html.
45   WaterfallView.MAIN_BOX_ID = 'waterfall-view-tab-content';
46   WaterfallView.BAR_TABLE_ID = 'waterfall-view-time-bar-table';
47   WaterfallView.BAR_TBODY_ID = 'waterfall-view-time-bar-tbody';
48   WaterfallView.SCALE_ID = 'waterfall-view-adjust-to-window';
49   WaterfallView.TIME_SCALE_HEADER_ID = 'waterfall-view-time-scale-labels';
50   WaterfallView.TIME_RANGE_ID = 'waterfall-view-time-range-submit';
51   WaterfallView.START_TIME_ID = 'waterfall-view-start-input';
52   WaterfallView.END_TIME_ID = 'waterfall-view-end-input';
53   WaterfallView.INFO_TABLE_ID = 'waterfall-view-information-table';
54   WaterfallView.INFO_TBODY_ID = 'waterfall-view-information-tbody';
55   WaterfallView.CONTROLS_ID = 'waterfall-view-controls';
56   WaterfallView.ID_HEADER_ID = 'waterfall-view-id-header';
57   WaterfallView.URL_HEADER_ID = 'waterfall-view-url-header';
59   // The number of units mouse wheel deltas increase for each tick of the
60   // wheel.
61   var MOUSE_WHEEL_UNITS_PER_CLICK = 120;
63   // Amount we zoom for one vertical tick of the mouse wheel, as a ratio.
64   var MOUSE_WHEEL_ZOOM_RATE = 1.25;
65   // Amount we scroll for one horizontal tick of the mouse wheel, in pixels.
66   var MOUSE_WHEEL_SCROLL_RATE = MOUSE_WHEEL_UNITS_PER_CLICK;
68   cr.addSingletonGetter(WaterfallView);
70   WaterfallView.prototype = {
71     // Inherit the superclass's methods.
72     __proto__: superClass.prototype,
74     /**
75      * Creates new WaterfallRows for URL Requests when the sourceEntries are
76      * updated if they do not already exist.
77      * Updates pre-existing WaterfallRows that correspond to updated sources.
78      */
79     onSourceEntriesUpdated: function(sourceEntries) {
80       if (this.startTime_ == null && sourceEntries.length > 0) {
81         var logEntries = sourceEntries[0].getLogEntries();
82         this.startTime_ = timeutil.convertTimeTicksToTime(logEntries[0].time);
83         // Initial scale factor.
84         this.scaleFactor_ = 0.1;
85       }
86       for (var i = 0; i < sourceEntries.length; ++i) {
87         var sourceEntry = sourceEntries[i];
88         var id = sourceEntry.getSourceId();
89         if (sourceEntry.getSourceType() == EventSourceType.URL_REQUEST) {
90           var row = this.sourceIdToRowMap_[id];
91           if (!row) {
92             var newRow = new WaterfallRow(this, sourceEntry);
93             this.sourceIdToRowMap_[id] = newRow;
94           } else {
95             row.onSourceUpdated();
96           }
97         }
98       }
99       this.scrollInfoTable_();
100       this.positionBarTable_();
101       this.updateTimeScale_(this.scaleFactor_);
102     },
104     onAllSourceEntriesDeleted: function() {
105       this.initializeSourceList_();
106     },
108     onLoadLogFinish: function(data) {
109       return true;
110     },
112     getScaleFactor: function() {
113       return this.scaleFactor_;
114     },
116     getStartTime: function() {
117       return this.startTime_;
118     },
120     setGeometry: function(left, top, width, height) {
121       superClass.prototype.setGeometry.call(this, left, top, width, height);
122       this.scrollInfoTable_();
123     },
125     show: function(isVisible) {
126       superClass.prototype.show.call(this, isVisible);
127       if (isVisible) {
128         this.scrollInfoTable_();
129       }
130     },
132     /**
133      * Initializes the list of source entries.  If source entries are already
134      * being displayed, removes them all in the process.
135      */
136     initializeSourceList_: function() {
137       this.sourceIdToRowMap_ = {};
138       $(WaterfallView.BAR_TBODY_ID).innerHTML = '';
139       $(WaterfallView.INFO_TBODY_ID).innerHTML = '';
140       this.startTime_ = null;
141       this.scaleFactor_ = null;
142     },
144     /**
145      * Changes scroll position of the window such that horizontally, everything
146      * within the specified range fits into the user's viewport.
147      */
148     adjustToWindow_: function(windowStart, windowEnd) {
149       var waterfallLeft = $(WaterfallView.INFO_TABLE_ID).offsetWidth +
150           $(WaterfallView.INFO_TABLE_ID).offsetLeft +
151           $(WaterfallView.ID_HEADER_ID).offsetWidth;
152       var maxWidth = $(WaterfallView.MAIN_BOX_ID).offsetWidth - waterfallLeft;
153       var totalDuration = 0;
154       if (windowEnd != -1) {
155         totalDuration = windowEnd - windowStart;
156       } else {
157         for (var id in this.sourceIdToRowMap_) {
158           var row = this.sourceIdToRowMap_[id];
159           var rowDuration = row.getEndTime() - this.startTime_;
160           if (totalDuration < rowDuration && !row.hide) {
161             totalDuration = rowDuration;
162           }
163         }
164       }
165       if (totalDuration <= 0) {
166         return;
167       }
168       this.scaleAll_(maxWidth / totalDuration);
169       $(WaterfallView.MAIN_BOX_ID).scrollLeft =
170           windowStart * this.scaleFactor_;
171     },
173     /** Updates the time tick indicators. */
174     updateTimeScale_: function(scaleFactor) {
175       var timePerTick = 1;
176       var minTickDistance = 20;
178       $(WaterfallView.TIME_SCALE_HEADER_ID).innerHTML = '';
180       // Holder provides environment to prevent wrapping.
181       var timeTickRow = addNode($(WaterfallView.TIME_SCALE_HEADER_ID), 'div');
182       timeTickRow.classList.add('waterfall-view-time-scale-row');
184       var availableWidth = $(WaterfallView.BAR_TBODY_ID).clientWidth;
185       var tickDistance = scaleFactor * timePerTick;
187       while (tickDistance < minTickDistance) {
188         timePerTick = timePerTick * 10;
189         tickDistance = scaleFactor * timePerTick;
190       }
192       var tickCount = availableWidth / tickDistance;
193       for (var i = 0; i < tickCount; ++i) {
194         var timeCell = addNode(timeTickRow, 'div');
195         setNodeWidth(timeCell, tickDistance);
196         timeCell.classList.add('waterfall-view-time-scale');
197         timeCell.title = i * timePerTick + ' to ' +
198            (i + 1) * timePerTick + ' ms';
199         // Red marker for every 5th bar.
200         if (i % 5 == 0) {
201           timeCell.classList.add('waterfall-view-time-scale-special');
202         }
203       }
204     },
206     /**
207      * Scales all existing rows by scaleFactor.
208      */
209     scaleAll_: function(scaleFactor) {
210       this.scaleFactor_ = scaleFactor;
211       for (var id in this.sourceIdToRowMap_) {
212         var row = this.sourceIdToRowMap_[id];
213         row.updateRow();
214       }
215       this.updateTimeScale_(scaleFactor);
216     },
218     scrollToZoom_: function(event) {
219       // To use scrolling to control zoom, hold down the alt key and scroll.
220       if ('wheelDelta' in event && event.altKey) {
221         event.preventDefault();
222         var zoomFactor = Math.pow(MOUSE_WHEEL_ZOOM_RATE,
223              event.wheelDeltaY / MOUSE_WHEEL_UNITS_PER_CLICK);
225         var waterfallLeft = $(WaterfallView.ID_HEADER_ID).offsetWidth +
226             $(WaterfallView.URL_HEADER_ID).offsetWidth;
227         var oldCursorPosition = event.pageX +
228             $(WaterfallView.MAIN_BOX_ID).scrollLeft;
229         var oldCursorPositionInTable = oldCursorPosition - waterfallLeft;
231         this.scaleAll_(this.scaleFactor_ * zoomFactor);
233         // Shifts the view when scrolling. newScroll could be less than 0 or
234         // more than the maximum scroll position, but both cases are handled
235         // by the inbuilt scrollLeft implementation.
236         var newScroll =
237             oldCursorPositionInTable * zoomFactor - event.pageX + waterfallLeft;
238         $(WaterfallView.MAIN_BOX_ID).scrollLeft = newScroll;
239       }
240     },
242     /**
243      * Positions the bar table such that it is in line with the right edge of
244      * the info table.
245      */
246     positionBarTable_: function() {
247       var offsetLeft = $(WaterfallView.INFO_TABLE_ID).offsetWidth +
248           $(WaterfallView.INFO_TABLE_ID).offsetLeft;
249       $(WaterfallView.BAR_TABLE_ID).style.left = offsetLeft + 'px';
250     },
252     /**
253      * Moves the info table when the page is scrolled vertically, ensuring that
254      * the correct information is displayed on the page, and that no elements
255      * are blocked unnecessarily.
256      */
257     scrollInfoTable_: function(event) {
258       $(WaterfallView.INFO_TABLE_ID).style.top =
259           $(WaterfallView.MAIN_BOX_ID).offsetTop +
260           $(WaterfallView.BAR_TABLE_ID).offsetTop -
261           $(WaterfallView.MAIN_BOX_ID).scrollTop + 'px';
263       if ($(WaterfallView.INFO_TABLE_ID).offsetHeight >
264           $(WaterfallView.MAIN_BOX_ID).clientHeight) {
265         var scroll = $(WaterfallView.MAIN_BOX_ID).scrollTop;
266         var bottomClip =
267             $(WaterfallView.MAIN_BOX_ID).clientHeight -
268             $(WaterfallView.BAR_TABLE_ID).offsetTop +
269             $(WaterfallView.MAIN_BOX_ID).scrollTop;
270         // Clips the information table such that it does not cover the scroll
271         // bars or the controls bar.
272         $(WaterfallView.INFO_TABLE_ID).style.clip = 'rect(' + scroll +
273             'px auto ' + bottomClip + 'px auto)';
274       }
275     },
277     /** Parses user input, then calls adjustToWindow to shift that into view. */
278     setStartEndTimes_: function() {
279       var windowStart = parseInt($(WaterfallView.START_TIME_ID).value);
280       var windowEnd = parseInt($(WaterfallView.END_TIME_ID).value);
281       if ($(WaterfallView.END_TIME_ID).value == '') {
282         windowEnd = -1;
283       }
284       if ($(WaterfallView.START_TIME_ID).value == '') {
285         windowStart = 0;
286       }
287       this.adjustToWindow_(windowStart, windowEnd);
288     },
291   };
293   return WaterfallView;
294 })();