Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / net_internals / bandwidth_view.js
blob067151c22c0e12bb3a51ed13f93dd4efec14533c
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
3 // found in the LICENSE file.
5 /** This view displays summary statistics on bandwidth usage. */
6 var BandwidthView = (function() {
7   'use strict';
9   // We inherit from DivView.
10   var superClass = DivView;
12   /**
13    * @constructor
14    */
15   function BandwidthView() {
16     assertFirstConstructorCall(BandwidthView);
18     // Call superclass's constructor.
19     superClass.call(this, BandwidthView.MAIN_BOX_ID);
21     g_browser.addSessionNetworkStatsObserver(this, true);
22     g_browser.addHistoricNetworkStatsObserver(this, true);
24     // Register to receive data reduction proxy info.
25     g_browser.addDataReductionProxyInfoObserver(this, true);
27     // Register to receive bad proxy info.
28     g_browser.addBadProxiesObserver(this, true);
30     this.sessionNetworkStats_ = null;
31     this.historicNetworkStats_ = null;
32   }
34   BandwidthView.TAB_ID = 'tab-handle-bandwidth';
35   BandwidthView.TAB_NAME = 'Bandwidth';
36   BandwidthView.TAB_HASH = '#bandwidth';
38   // IDs for special HTML elements in bandwidth_view.html
39   BandwidthView.MAIN_BOX_ID = 'bandwidth-view-tab-content';
40   BandwidthView.ENABLED_ID = 'data-reduction-proxy-enabled';
41   BandwidthView.PROXY_CONFIG_ID = 'data-reduction-proxy-config';
42   BandwidthView.PROBE_STATUS_ID = 'data-reduction-proxy-probe-status';
43   BandwidthView.BYPASS_STATE_CONTAINER_ID =
44       'data-reduction-proxy-bypass-state-container';
45   BandwidthView.BYPASS_STATE_ID = 'data-reduction-proxy-bypass-state-details';
46   BandwidthView.EVENTS_TBODY_ID = 'data-reduction-proxy-view-events-tbody';
47   BandwidthView.EVENTS_UL = 'data-reduction-proxy-view-events-list';
48   BandwidthView.STATS_BOX_ID = 'bandwidth-stats-table';
50   cr.addSingletonGetter(BandwidthView);
52   BandwidthView.prototype = {
53     // Inherit the superclass's methods.
54     __proto__: superClass.prototype,
56     data_reduction_proxy_config_: null,
57     last_bypass_: null,
58     bad_proxy_config_: null,
60     onLoadLogFinish: function(data) {
61       return this.onBadProxiesChanged(data.badProxies) &&
62           this.onDataReductionProxyInfoChanged(data.dataReductionProxyInfo) &&
63           (this.onSessionNetworkStatsChanged(data.sessionNetworkStats) ||
64               this.onHistoricNetworkStatsChanged(data.historicNetworkStats));
65     },
67     /**
68      * Retains information on bandwidth usage this session.
69      */
70     onSessionNetworkStatsChanged: function(sessionNetworkStats) {
71       this.sessionNetworkStats_ = sessionNetworkStats;
72       return this.updateBandwidthUsageTable_();
73     },
75     /**
76      * Displays information on bandwidth usage this session and over the
77      * browser's lifetime.
78      */
79     onHistoricNetworkStatsChanged: function(historicNetworkStats) {
80       this.historicNetworkStats_ = historicNetworkStats;
81       return this.updateBandwidthUsageTable_();
82     },
84     /**
85      * Updates the UI based on receiving changes in information about the
86      * data reduction proxy summary.
87      */
88     onDataReductionProxyInfoChanged: function(info) {
89       $(BandwidthView.EVENTS_TBODY_ID).innerHTML = '';
91       if (!info)
92         return false;
94       if (info.enabled) {
95         $(BandwidthView.ENABLED_ID).innerText = 'Enabled';
96         $(BandwidthView.PROBE_STATUS_ID).innerText =
97             info.probe != null ? info.probe : 'N/A';
98         this.last_bypass_ = info.last_bypass;
99         this.data_reduction_proxy_config_ = info.proxy_config.params;
100       } else {
101         $(BandwidthView.ENABLED_ID).innerText = 'Disabled';
102         $(BandwidthView.PROBE_STATUS_ID).innerText = 'N/A';
103         this.data_reduction_proxy_config_ = null;
104       }
106       this.updateDataReductionProxyConfig_();
108       for (var eventIndex = info.events.length - 1; eventIndex >= 0;
109           --eventIndex) {
110         var event = info.events[eventIndex];
111         var headerRow = addNode($(BandwidthView.EVENTS_TBODY_ID), 'tr');
112         var detailsRow = addNode($(BandwidthView.EVENTS_TBODY_ID), 'tr');
114         var timeCell = addNode(headerRow, 'td');
115         var actionCell = addNode(headerRow, 'td');
116         var detailsCell = addNode(detailsRow, 'td');
117         detailsCell.colSpan = 2;
118         detailsCell.className = 'data-reduction-proxy-view-events-details';
119         var eventTime = timeutil.convertTimeTicksToDate(event.time);
120         timeutil.addNodeWithDate(timeCell, eventTime);
121         this.buildEventRow_(event, actionCell, detailsCell);
122       }
124       return true;
125     },
127     /**
128      * Updates the UI based on receiving changes in information about bad
129      * proxy servers.
130      */
131     onBadProxiesChanged: function(badProxies) {
132       if (!badProxies)
133         return false;
135       var newBadProxies = [];
136       if (badProxies.length == 0) {
137         this.last_bypass_ = null;
138       } else {
139         for (var i = 0; i < badProxies.length; ++i) {
140           var entry = badProxies[i];
141           newBadProxies[entry.proxy_uri] = entry.bad_until;
142         }
143       }
144       this.bad_proxy_config_ = newBadProxies;
145       this.updateDataReductionProxyConfig_();
147       return true;
148     },
150     /**
151      * Update the bandwidth usage table.  Returns false on failure.
152      */
153     updateBandwidthUsageTable_: function() {
154       var sessionNetworkStats = this.sessionNetworkStats_;
155       var historicNetworkStats = this.historicNetworkStats_;
156       if (!sessionNetworkStats || !historicNetworkStats)
157         return false;
159       var sessionOriginal = sessionNetworkStats.session_original_content_length;
160       var sessionReceived = sessionNetworkStats.session_received_content_length;
161       var historicOriginal =
162           historicNetworkStats.historic_original_content_length;
163       var historicReceived =
164           historicNetworkStats.historic_received_content_length;
166       var rows = [];
167       rows.push({
168           title: 'Original (KB)',
169           sessionValue: bytesToRoundedKilobytes_(sessionOriginal),
170           historicValue: bytesToRoundedKilobytes_(historicOriginal)
171       });
172       rows.push({
173           title: 'Received (KB)',
174           sessionValue: bytesToRoundedKilobytes_(sessionReceived),
175           historicValue: bytesToRoundedKilobytes_(historicReceived)
176       });
177       rows.push({
178           title: 'Savings (KB)',
179           sessionValue:
180               bytesToRoundedKilobytes_(sessionOriginal - sessionReceived),
181           historicValue:
182               bytesToRoundedKilobytes_(historicOriginal - historicReceived)
183       });
184       rows.push({
185           title: 'Savings (%)',
186           sessionValue: getPercentSavings_(sessionOriginal, sessionReceived),
187           historicValue: getPercentSavings_(historicOriginal,
188                                             historicReceived)
189       });
191       var input = new JsEvalContext({rows: rows});
192       jstProcess(input, $(BandwidthView.STATS_BOX_ID));
193       return true;
194     },
196     /**
197      * Renders a Data Reduction Proxy event into the event tbody
198      */
199     buildEventRow_: function(event, actionCell, detailsCell) {
200       if (event.type == EventType.DATA_REDUCTION_PROXY_ENABLED &&
201           event.params.enabled == 0) {
202         addTextNode(actionCell, 'DISABLED');
203       } else {
204         var actionText =
205             EventTypeNames[event.type].replace('DATA_REDUCTION_PROXY_', '');
206         if (event.phase == EventPhase.PHASE_BEGIN ||
207             event.phase == EventPhase.PHASE_END) {
208           actionText = actionText + ' (' +
209               getKeyWithValue(EventPhase, event.phase)
210                   .replace('PHASE_', '') + ')';
211         }
213         addTextNode(actionCell, actionText);
214         this.createEventTable_(event.params, detailsCell);
215       }
216     },
218     /**
219      * Updates the data reduction proxy summary block.
220      */
221     updateDataReductionProxyConfig_: function() {
222       $(BandwidthView.PROXY_CONFIG_ID).innerHTML = '';
223       $(BandwidthView.BYPASS_STATE_ID).innerHTML = '';
224       setNodeDisplay($(BandwidthView.BYPASS_STATE_CONTAINER_ID), false);
226       if (this.data_reduction_proxy_config_) {
227         var hasBypassedProxy = false;
228         var now = timeutil.getCurrentTimeTicks();
230         if (this.last_bypass_ &&
231             this.hasTimePassedLogTime_(+this.last_bypass_.params.expiration)) {
232           // Best effort on iterating the config to search for a bad proxy.
233           // A server could exist in a string member of
234           // data_reduction_proxy_config_ or within an array of servers in an
235           // array member of data_reduction_proxy_config_. As such, search
236           // through all string members and string arrays.
237           for (var key in this.data_reduction_proxy_config_) {
238             var value = this.data_reduction_proxy_config_[key];
239             if (typeof value == 'string') {
240               if (this.isMarkedAsBad_(value)) {
241                 hasBypassedProxy = true;
242                 break;
243               }
244             } else if (value instanceof Array) {
245               for (var index = 1; index < value.length; index++) {
246                 if (this.isMarkedAsBad_(value[index])) {
247                   hasBypassedProxy = true;
248                 }
249               }
251               if (hasBypassedProxy) {
252                 break;
253               }
254             }
255           }
256         }
258         if (hasBypassedProxy) {
259           this.createEventTable_(this.last_bypass_.params,
260                                  $(BandwidthView.BYPASS_STATE_ID));
261         }
263         this.createEventTable_(this.data_reduction_proxy_config_,
264                                $(BandwidthView.PROXY_CONFIG_ID));
265         setNodeDisplay($(BandwidthView.BYPASS_STATE_CONTAINER_ID),
266                        hasBypassedProxy);
267       }
268     },
270     /**
271      * Checks to see if a proxy server is in marked as bad.
272      */
273     isMarkedAsBad_: function(proxy) {
274       for (var entry in this.bad_proxy_config_) {
275         if (entry == proxy &&
276             this.hasTimePassedLogTime_(this.bad_proxy_config_[entry])) {
277           return true;
278         }
279       }
281       return false;
282     },
284     /**
285      * Checks to see if a given time in ticks has passed the time of the
286      * the log. For real time viewing, this is "now", but for loaded logs, it
287      * is the time at which the logs were taken.
288      */
289     hasTimePassedLogTime_: function(timeTicks) {
290       var logTime;
291       if (MainView.isViewingLoadedLog() && ClientInfo.numericDate) {
292         logTime = ClientInfo.numericDate;
293       } else {
294         logTime = timeutil.getCurrentTime();
295       }
297       return timeutil.convertTimeTicksToTime(timeTicks) > logTime;
298     },
300     /**
301      * Creates a table of the object obj. Certain keys are special cased for
302      * ease of readability.
303      */
304     createEventTable_: function(obj, parentNode) {
305       if (Object.keys(obj).length > 0) {
306         var tableNode = addNode(parentNode, 'table');
307         tableNode.className = 'borderless-table';
308         for (var key in obj) {
309           var value = obj[key];
310           if (value != null && value.toString() != '') {
311             if (key == 'net_error') {
312               if (value == 0) {
313                 value = 'OK';
314               } else {
315                 value = netErrorToString(value);
316               }
317             } else if (key == 'bypass_type') {
318               value = getKeyWithValue(DataReductionProxyBypassEventType, value);
319             } else if (key == 'bypass_action_type') {
320               value = getKeyWithValue(DataReductionProxyBypassActionType,
321                                       value);
322             } else if (key == 'expiration') {
323               value = timeutil.convertTimeTicksToDate(value);
324             }
325             var tableRow = addNode(tableNode, 'tr');
326             addNodeWithText(tableRow, 'td', key);
327             addNodeWithText(tableRow, 'td', value);
328           }
329         }
330       }
331     }
332   };
334   /**
335    * Converts bytes to kilobytes rounded to one decimal place.
336    */
337   function bytesToRoundedKilobytes_(val) {
338     return (val / 1024).toFixed(1);
339   }
341   /**
342    * Returns bandwidth savings as a percent rounded to one decimal place.
343    */
344   function getPercentSavings_(original, received) {
345     if (original > 0) {
346       return ((original - received) * 100 / original).toFixed(1);
347     }
348     return '0.0';
349   }
351   return BandwidthView;
352 })();