Home | History | Annotate | Download | only in net_internals
      1 // Copyright (c) 2011 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.
      4 
      5 /**
      6  * This view displays options for importing/exporting the captured data. Its
      7  * primarily usefulness is to allow users to copy-paste their data in an easy
      8  * to read format for bug reports.
      9  *
     10  *   - Has a button to generate a text report.
     11  *
     12  *   - Shows how many events have been captured.
     13  *  @constructor
     14  */
     15 function DataView(mainBoxId,
     16                   outputTextBoxId,
     17                   exportTextButtonId,
     18                   securityStrippingCheckboxId,
     19                   byteLoggingCheckboxId,
     20                   passivelyCapturedCountId,
     21                   activelyCapturedCountId,
     22                   deleteAllId,
     23                   dumpDataDivId,
     24                   loadDataDivId,
     25                   loadLogFileId,
     26                   capturingTextSpanId,
     27                   loggingTextSpanId) {
     28   DivView.call(this, mainBoxId);
     29 
     30   this.textPre_ = document.getElementById(outputTextBoxId);
     31 
     32   var securityStrippingCheckbox =
     33       document.getElementById(securityStrippingCheckboxId);
     34   securityStrippingCheckbox.onclick =
     35       this.onSetSecurityStripping_.bind(this, securityStrippingCheckbox);
     36 
     37   var byteLoggingCheckbox = document.getElementById(byteLoggingCheckboxId);
     38   byteLoggingCheckbox.onclick =
     39       this.onSetByteLogging_.bind(this, byteLoggingCheckbox);
     40 
     41   var exportTextButton = document.getElementById(exportTextButtonId);
     42   exportTextButton.onclick = this.onExportToText_.bind(this);
     43 
     44   this.activelyCapturedCountBox_ =
     45       document.getElementById(activelyCapturedCountId);
     46   this.passivelyCapturedCountBox_ =
     47       document.getElementById(passivelyCapturedCountId);
     48   document.getElementById(deleteAllId).onclick =
     49       g_browser.deleteAllEvents.bind(g_browser);
     50 
     51   this.dumpDataDiv_ = document.getElementById(dumpDataDivId);
     52   this.loadDataDiv_ = document.getElementById(loadDataDivId);
     53   this.capturingTextSpan_ = document.getElementById(capturingTextSpanId);
     54   this.loggingTextSpan_ = document.getElementById(loggingTextSpanId);
     55 
     56   document.getElementById(loadLogFileId).onclick =
     57       g_browser.loadLogFile.bind(g_browser);
     58 
     59   this.updateEventCounts_();
     60   this.waitingForUpdate_ = false;
     61 
     62   g_browser.addLogObserver(this);
     63 }
     64 
     65 inherits(DataView, DivView);
     66 
     67 /**
     68  * Called whenever a new event is received.
     69  */
     70 DataView.prototype.onLogEntryAdded = function(logEntry) {
     71   this.updateEventCounts_();
     72 };
     73 
     74 /**
     75  * Called whenever some log events are deleted.  |sourceIds| lists
     76  * the source IDs of all deleted log entries.
     77  */
     78 DataView.prototype.onLogEntriesDeleted = function(sourceIds) {
     79   this.updateEventCounts_();
     80 };
     81 
     82 /**
     83  * Called whenever all log events are deleted.
     84  */
     85 DataView.prototype.onAllLogEntriesDeleted = function() {
     86   this.updateEventCounts_();
     87 };
     88 
     89 /**
     90  * Called when either a log file is loaded or when going back to actively
     91  * logging events.  In either case, called after clearing the old entries,
     92  * but before getting any new ones.
     93  */
     94 DataView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) {
     95   setNodeDisplay(this.dumpDataDiv_, !isViewingLogFile);
     96   setNodeDisplay(this.capturingTextSpan_, !isViewingLogFile);
     97   setNodeDisplay(this.loggingTextSpan_, isViewingLogFile);
     98   this.setText_('');
     99 };
    100 
    101 /**
    102  * Updates the counters showing how many events have been captured.
    103  */
    104 DataView.prototype.updateEventCounts_ = function() {
    105   this.activelyCapturedCountBox_.innerText =
    106       g_browser.getNumActivelyCapturedEvents()
    107   this.passivelyCapturedCountBox_.innerText =
    108       g_browser.getNumPassivelyCapturedEvents();
    109 };
    110 
    111 /**
    112  * Depending on the value of the checkbox, enables or disables logging of
    113  * actual bytes transferred.
    114  */
    115 DataView.prototype.onSetByteLogging_ = function(byteLoggingCheckbox) {
    116   if (byteLoggingCheckbox.checked) {
    117     g_browser.setLogLevel(LogLevelType.LOG_ALL);
    118   } else {
    119     g_browser.setLogLevel(LogLevelType.LOG_ALL_BUT_BYTES);
    120   }
    121 };
    122 
    123 /**
    124  * Depending on the value of the checkbox, enables or disables stripping
    125  * cookies and passwords from log dumps and displayed events.
    126  */
    127 DataView.prototype.onSetSecurityStripping_ =
    128     function(securityStrippingCheckbox) {
    129   g_browser.setSecurityStripping(securityStrippingCheckbox.checked);
    130 };
    131 
    132 /**
    133  * Clears displayed text when security stripping is toggled.
    134  */
    135 DataView.prototype.onSecurityStrippingChanged = function() {
    136   this.setText_('');
    137 }
    138 
    139 /**
    140  * If not already waiting for results from all updates, triggers all
    141  * updates and starts waiting for them to complete.
    142  */
    143 DataView.prototype.onExportToText_ = function() {
    144   if (this.waitingForUpdate_)
    145     return;
    146   this.waitingForUpdate = true;
    147   this.setText_('Generating...');
    148   g_browser.updateAllInfo(this.onUpdateAllCompleted.bind(this));
    149 };
    150 
    151 /**
    152  * Presents the captured data as formatted text.
    153  */
    154 DataView.prototype.onUpdateAllCompleted = function(data) {
    155   // It's possible for a log file to be loaded while a dump is being generated.
    156   // When that happens, don't display the log dump, to avoid any confusion.
    157   if (g_browser.isViewingLogFile())
    158     return;
    159   this.waitingForUpdate_ = false;
    160   var text = [];
    161 
    162   // Print some basic information about our environment.
    163   text.push('Data exported on: ' + (new Date()).toLocaleString());
    164   text.push('');
    165   text.push('Number of passively captured events: ' +
    166             g_browser.getNumPassivelyCapturedEvents());
    167   text.push('Number of actively captured events: ' +
    168             g_browser.getNumActivelyCapturedEvents());
    169   text.push('');
    170 
    171   text.push('Chrome version: ' + ClientInfo.version +
    172             ' (' + ClientInfo.official +
    173             ' ' + ClientInfo.cl +
    174             ') ' + ClientInfo.version_mod);
    175   // Third value in first set of parentheses in user-agent string.
    176   var platform = /\(.*?;.*?; (.*?);/.exec(navigator.userAgent);
    177   if (platform)
    178     text.push('Platform: ' + platform[1]);
    179   text.push('Command line: ' + ClientInfo.command_line);
    180 
    181   text.push('');
    182   var default_address_family = data.hostResolverInfo.default_address_family;
    183   text.push('Default address family: ' +
    184       getKeyWithValue(AddressFamily, default_address_family));
    185   if (default_address_family == AddressFamily.ADDRESS_FAMILY_IPV4)
    186     text.push('  (IPv6 disabled)');
    187 
    188   text.push('');
    189   text.push('----------------------------------------------');
    190   text.push(' Proxy settings (effective)');
    191   text.push('----------------------------------------------');
    192   text.push('');
    193 
    194   text.push(proxySettingsToString(data.proxySettings.effective));
    195 
    196   text.push('');
    197   text.push('----------------------------------------------');
    198   text.push(' Proxy settings (original)');
    199   text.push('----------------------------------------------');
    200   text.push('');
    201 
    202   text.push(proxySettingsToString(data.proxySettings.original));
    203 
    204   text.push('');
    205   text.push('----------------------------------------------');
    206   text.push(' Bad proxies cache');
    207   text.push('----------------------------------------------');
    208 
    209   var badProxiesList = data.badProxies;
    210   if (badProxiesList.length == 0) {
    211     text.push('');
    212     text.push('None');
    213   } else {
    214     for (var i = 0; i < badProxiesList.length; ++i) {
    215       var e = badProxiesList[i];
    216       text.push('');
    217       text.push('(' + (i+1) + ')');
    218       text.push('Proxy: ' + e.proxy_uri);
    219       text.push('Bad until: ' + this.formatExpirationTime_(e.bad_until));
    220     }
    221   }
    222 
    223   text.push('');
    224   text.push('----------------------------------------------');
    225   text.push(' Host resolver cache');
    226   text.push('----------------------------------------------');
    227   text.push('');
    228 
    229   var hostResolverCache = data.hostResolverInfo.cache;
    230 
    231   text.push('Capacity: ' + hostResolverCache.capacity);
    232   text.push('Time to live for successful resolves (ms): ' +
    233             hostResolverCache.ttl_success_ms);
    234   text.push('Time to live for failed resolves (ms): ' +
    235             hostResolverCache.ttl_failure_ms);
    236 
    237   if (hostResolverCache.entries.length > 0) {
    238     for (var i = 0; i < hostResolverCache.entries.length; ++i) {
    239       var e = hostResolverCache.entries[i];
    240 
    241       text.push('');
    242       text.push('(' + (i+1) + ')');
    243       text.push('Hostname: ' + e.hostname);
    244       text.push('Address family: ' +
    245                 getKeyWithValue(AddressFamily, e.address_family));
    246 
    247       if (e.error != undefined) {
    248          text.push('Error: ' + e.error);
    249       } else {
    250         for (var j = 0; j < e.addresses.length; ++j) {
    251           text.push('Address ' + (j + 1) + ': ' + e.addresses[j]);
    252         }
    253       }
    254 
    255       text.push('Valid until: ' + this.formatExpirationTime_(e.expiration));
    256       var expirationDate = g_browser.convertTimeTicksToDate(e.expiration);
    257       text.push('  (' + expirationDate.toLocaleString() + ')');
    258     }
    259   } else {
    260     text.push('');
    261     text.push('None');
    262   }
    263 
    264   text.push('');
    265   text.push('----------------------------------------------');
    266   text.push(' Events');
    267   text.push('----------------------------------------------');
    268   text.push('');
    269 
    270   this.appendEventsPrintedAsText_(text);
    271 
    272   text.push('');
    273   text.push('----------------------------------------------');
    274   text.push(' Http cache stats');
    275   text.push('----------------------------------------------');
    276   text.push('');
    277 
    278   var httpCacheStats = data.httpCacheInfo.stats;
    279   for (var statName in httpCacheStats)
    280     text.push(statName + ': ' + httpCacheStats[statName]);
    281 
    282   text.push('');
    283   text.push('----------------------------------------------');
    284   text.push(' Socket pools');
    285   text.push('----------------------------------------------');
    286   text.push('');
    287 
    288   this.appendSocketPoolsAsText_(text, data.socketPoolInfo);
    289 
    290   text.push('');
    291   text.push('----------------------------------------------');
    292   text.push(' SPDY Status');
    293   text.push('----------------------------------------------');
    294   text.push('');
    295 
    296   text.push('SPDY Enabled: ' + data.spdyStatus.spdy_enabled);
    297   text.push('Use Alternate Protocol: ' +
    298       data.spdyStatus.use_alternate_protocols);
    299   text.push('Force SPDY Always: ' + data.spdyStatus.force_spdy_always);
    300   text.push('Force SPDY Over SSL: ' + data.spdyStatus.force_spdy_over_ssl);
    301   text.push('Next Protocols: ' + data.spdyStatus.next_protos);
    302 
    303 
    304   text.push('');
    305   text.push('----------------------------------------------');
    306   text.push(' SPDY Sessions');
    307   text.push('----------------------------------------------');
    308   text.push('');
    309 
    310   if (data.spdySessionInfo == null || data.spdySessionInfo.length == 0) {
    311     text.push('None');
    312   } else {
    313     var spdyTablePrinter =
    314       SpdyView.createSessionTablePrinter(data.spdySessionInfo);
    315     text.push(spdyTablePrinter.toText(2));
    316   }
    317 
    318   text.push('');
    319   text.push('----------------------------------------------');
    320   text.push(' Alternate Protocol Mappings');
    321   text.push('----------------------------------------------');
    322   text.push('');
    323 
    324   if (data.spdyAlternateProtocolMappings == null ||
    325       data.spdyAlternateProtocolMappings.length == 0) {
    326     text.push('None');
    327   } else {
    328     var spdyTablePrinter =
    329       SpdyView.createAlternateProtocolMappingsTablePrinter(
    330           data.spdyAlternateProtocolMappings);
    331     text.push(spdyTablePrinter.toText(2));
    332   }
    333 
    334   if (g_browser.isPlatformWindows()) {
    335     text.push('');
    336     text.push('----------------------------------------------');
    337     text.push(' Winsock layered service providers');
    338     text.push('----------------------------------------------');
    339     text.push('');
    340 
    341     var serviceProviders = data.serviceProviders;
    342     var layeredServiceProviders = serviceProviders.service_providers;
    343     for (var i = 0; i < layeredServiceProviders.length; ++i) {
    344       var provider = layeredServiceProviders[i];
    345       text.push('name: ' + provider.name);
    346       text.push('version: ' + provider.version);
    347       text.push('type: ' +
    348                 ServiceProvidersView.getLayeredServiceProviderType(provider));
    349       text.push('socket_type: ' +
    350                 ServiceProvidersView.getSocketType(provider));
    351       text.push('socket_protocol: ' +
    352                 ServiceProvidersView.getProtocolType(provider));
    353       text.push('path: ' + provider.path);
    354       text.push('');
    355     }
    356 
    357     text.push('');
    358     text.push('----------------------------------------------');
    359     text.push(' Winsock namespace providers');
    360     text.push('----------------------------------------------');
    361     text.push('');
    362 
    363     var namespaceProviders = serviceProviders.namespace_providers;
    364     for (var i = 0; i < namespaceProviders.length; ++i) {
    365       var provider = namespaceProviders[i];
    366       text.push('name: ' + provider.name);
    367       text.push('version: ' + provider.version);
    368       text.push('type: ' +
    369                 ServiceProvidersView.getNamespaceProviderType(provider));
    370       text.push('active: ' + provider.active);
    371       text.push('');
    372     }
    373   }
    374 
    375   // Open a new window to display this text.
    376   this.setText_(text.join('\n'));
    377 
    378   this.selectText_();
    379 };
    380 
    381 DataView.prototype.appendEventsPrintedAsText_ = function(out) {
    382   var allEvents = g_browser.getAllCapturedEvents();
    383 
    384   // Group the events into buckets by source ID, and buckets by source type.
    385   var sourceIds = [];
    386   var sourceIdToEventList = {};
    387   var sourceTypeToSourceIdList = {};
    388 
    389   // Lists used for actual output.
    390   var eventLists = [];
    391 
    392   for (var i = 0; i < allEvents.length; ++i) {
    393     var e = allEvents[i];
    394     var eventList = sourceIdToEventList[e.source.id];
    395     if (!eventList) {
    396       eventList = [];
    397       eventLists.push(eventList);
    398       if (e.source.type != LogSourceType.NONE)
    399         sourceIdToEventList[e.source.id] = eventList;
    400 
    401       // Update sourceIds
    402       sourceIds.push(e.source.id);
    403 
    404       // Update the sourceTypeToSourceIdList list.
    405       var idList = sourceTypeToSourceIdList[e.source.type];
    406       if (!idList) {
    407         idList = [];
    408         sourceTypeToSourceIdList[e.source.type] = idList;
    409       }
    410       idList.push(e.source.id);
    411     }
    412     eventList.push(e);
    413   }
    414 
    415 
    416   // For each source or event without a source (ordered by when the first
    417   // output event for that source happened).
    418   for (var i = 0; i < eventLists.length; ++i) {
    419     var eventList = eventLists[i];
    420     var sourceId = eventList[0].source.id;
    421     var sourceType = eventList[0].source.type;
    422 
    423     var startDate = g_browser.convertTimeTicksToDate(eventList[0].time);
    424 
    425     out.push('------------------------------------------');
    426     out.push(getKeyWithValue(LogSourceType, sourceType) +
    427              ' (id=' + sourceId + ')' +
    428              '  [start=' + startDate.toLocaleString() + ']');
    429     out.push('------------------------------------------');
    430 
    431     out.push(PrintSourceEntriesAsText(eventList));
    432   }
    433 };
    434 
    435 DataView.prototype.appendSocketPoolsAsText_ = function(text, socketPoolInfo) {
    436   var socketPools = SocketPoolWrapper.createArrayFrom(socketPoolInfo);
    437   var tablePrinter = SocketPoolWrapper.createTablePrinter(socketPools);
    438   text.push(tablePrinter.toText(2));
    439 
    440   text.push('');
    441 
    442   for (var i = 0; i < socketPools.length; ++i) {
    443     if (socketPools[i].origPool.groups == undefined)
    444       continue;
    445     var groupTablePrinter = socketPools[i].createGroupTablePrinter();
    446     text.push(groupTablePrinter.toText(2));
    447   }
    448 };
    449 
    450 /**
    451  * Helper function to set this view's content to |text|.
    452  */
    453 DataView.prototype.setText_ = function(text) {
    454   this.textPre_.innerHTML = '';
    455   addTextNode(this.textPre_, text);
    456 };
    457 
    458 /**
    459  * Format a time ticks count as a timestamp.
    460  */
    461 DataView.prototype.formatExpirationTime_ = function(timeTicks) {
    462   var d = g_browser.convertTimeTicksToDate(timeTicks);
    463   var isExpired = d.getTime() < (new Date()).getTime();
    464   return 't=' + d.getTime() + (isExpired ? ' [EXPIRED]' : '');
    465 };
    466 
    467 /**
    468  * Select all text from log dump.
    469  */
    470 DataView.prototype.selectText_ = function() {
    471   var selection = window.getSelection();
    472   selection.removeAllRanges();
    473 
    474   var range = document.createRange();
    475   range.selectNodeContents(this.textPre_);
    476   selection.addRange(range);
    477 };
    478