Home | History | Annotate | Download | only in net_internals
      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.
      4 
      5 /**
      6  * Dictionary of constants (Initialized soon after loading by data from browser,
      7  * updated on load log).  The *Types dictionaries map strings to numeric IDs,
      8  * while the *TypeNames are the other way around.
      9  */
     10 var EventType = null;
     11 var EventTypeNames = null;
     12 var EventPhase = null;
     13 var EventSourceType = null;
     14 var EventSourceTypeNames = null;
     15 var LogLevelType = null;
     16 var ClientInfo = null;
     17 var NetError = null;
     18 var QuicError = null;
     19 var QuicRstStreamError = null;
     20 var LoadFlag = null;
     21 var CertStatusFlag = null;
     22 var LoadState = null;
     23 var AddressFamily = null;
     24 
     25 /**
     26  * Dictionary of all constants, used for saving log files.
     27  */
     28 var Constants = null;
     29 
     30 /**
     31  * Object to communicate between the renderer and the browser.
     32  * @type {!BrowserBridge}
     33  */
     34 var g_browser = null;
     35 
     36 /**
     37  * This class is the root view object of the page.  It owns all the other
     38  * views, and manages switching between them.  It is also responsible for
     39  * initializing the views and the BrowserBridge.
     40  */
     41 var MainView = (function() {
     42   'use strict';
     43 
     44   // We inherit from WindowView
     45   var superClass = WindowView;
     46 
     47   /**
     48    * Main entry point. Called once the page has loaded.
     49    *  @constructor
     50    */
     51   function MainView() {
     52     assertFirstConstructorCall(MainView);
     53 
     54     if (hasTouchScreen())
     55       document.body.classList.add('touch');
     56 
     57     // This must be initialized before the tabs, so they can register as
     58     // observers.
     59     g_browser = BrowserBridge.getInstance();
     60 
     61     // This must be the first constants observer, so other constants observers
     62     // can safely use the globals, rather than depending on walking through
     63     // the constants themselves.
     64     g_browser.addConstantsObserver(new ConstantsObserver());
     65 
     66     // Create the tab switcher.
     67     this.initTabs_();
     68 
     69     // Cut out a small vertical strip at the top of the window, to display
     70     // a high level status (i.e. if we are capturing events, or displaying a
     71     // log file). Below it we will position the main tabs and their content
     72     // area.
     73     this.topBarView_ = TopBarView.getInstance(this);
     74     var verticalSplitView = new VerticalSplitView(
     75         this.topBarView_, this.tabSwitcher_);
     76 
     77     superClass.call(this, verticalSplitView);
     78 
     79     // Trigger initial layout.
     80     this.resetGeometry();
     81 
     82     window.onhashchange = this.onUrlHashChange_.bind(this);
     83 
     84     // Select the initial view based on the current URL.
     85     window.onhashchange();
     86 
     87     // Tell the browser that we are ready to start receiving log events.
     88     this.topBarView_.switchToSubView('capture');
     89     g_browser.sendReady();
     90   }
     91 
     92   cr.addSingletonGetter(MainView);
     93 
     94   // Tracks if we're viewing a loaded log file, so views can behave
     95   // appropriately.  Global so safe to call during construction.
     96   var isViewingLoadedLog = false;
     97 
     98   MainView.isViewingLoadedLog = function() {
     99     return isViewingLoadedLog;
    100   };
    101 
    102   MainView.prototype = {
    103     // Inherit the superclass's methods.
    104     __proto__: superClass.prototype,
    105 
    106     // This is exposed both so the log import/export code can enumerate all the
    107     // tabs, and for testing.
    108     tabSwitcher: function() {
    109       return this.tabSwitcher_;
    110     },
    111 
    112     /**
    113      * Prevents receiving/sending events to/from the browser, so loaded data
    114      * will not be mixed with current Chrome state.  Also hides any interactive
    115      * HTML elements that send messages to the browser.  Cannot be undone
    116      * without reloading the page.  Must be called before passing loaded data
    117      * to the individual views.
    118      *
    119      * @param {string} opt_fileName The name of the log file that has been
    120      *     loaded, if we're loading a log file.
    121      */
    122     onLoadLog: function(opt_fileName) {
    123       isViewingLoadedLog = true;
    124 
    125       this.stopCapturing();
    126       if (opt_fileName != undefined) {
    127         // If there's a file name, a log file was loaded, so swap out the status
    128         // bar to indicate we're no longer capturing events.  Also disable
    129         // hiding cookies, so if the log dump has them, they'll be displayed.
    130         this.topBarView_.switchToSubView('loaded').setFileName(opt_fileName);
    131         $(ExportView.PRIVACY_STRIPPING_CHECKBOX_ID).checked = false;
    132         SourceTracker.getInstance().setPrivacyStripping(false);
    133       } else {
    134         // Otherwise, the "Stop Capturing" button was presumably pressed.
    135         // Don't disable hiding cookies, so created log dumps won't have them,
    136         // unless the user toggles the option.
    137         this.topBarView_.switchToSubView('halted');
    138       }
    139     },
    140 
    141     switchToViewOnlyMode: function() {
    142       // Since this won't be dumped to a file, we don't want to remove
    143       // cookies and credentials.
    144       log_util.createLogDumpAsync('', log_util.loadLogFile, false);
    145     },
    146 
    147     stopCapturing: function() {
    148       g_browser.disable();
    149       document.styleSheets[0].insertRule(
    150           '.hide-when-not-capturing { display: none; }', 0);
    151     },
    152 
    153     initTabs_: function() {
    154       this.tabIdToHash_ = {};
    155       this.hashToTabId_ = {};
    156 
    157       this.tabSwitcher_ = new TabSwitcherView(
    158           $(TopBarView.TAB_DROPDOWN_MENU_ID),
    159           this.onTabSwitched_.bind(this));
    160 
    161       // Helper function to add a tab given the class for a view singleton.
    162       var addTab = function(viewClass) {
    163         var tabId = viewClass.TAB_ID;
    164         var tabHash = viewClass.TAB_HASH;
    165         var tabName = viewClass.TAB_NAME;
    166         var view = viewClass.getInstance();
    167 
    168         if (!tabId || !view || !tabHash || !tabName) {
    169           throw Error('Invalid view class for tab');
    170         }
    171 
    172         if (tabHash.charAt(0) != '#') {
    173           throw Error('Tab hashes must start with a #');
    174         }
    175 
    176         this.tabSwitcher_.addTab(tabId, view, tabName);
    177         this.tabIdToHash_[tabId] = tabHash;
    178         this.hashToTabId_[tabHash] = tabId;
    179       }.bind(this);
    180 
    181       // Populate the main tabs.  Even tabs that don't contain information for
    182       // the running OS should be created, so they can load log dumps from other
    183       // OSes.
    184       addTab(CaptureView);
    185       addTab(ExportView);
    186       addTab(ImportView);
    187       addTab(ProxyView);
    188       addTab(EventsView);
    189       addTab(WaterfallView);
    190       addTab(TimelineView);
    191       addTab(DnsView);
    192       addTab(SocketsView);
    193       addTab(SpdyView);
    194       addTab(QuicView);
    195       addTab(HttpCacheView);
    196       addTab(ModulesView);
    197       addTab(TestView);
    198       addTab(CrosLogVisualizerView);
    199       addTab(HSTSView);
    200       addTab(LogsView);
    201       addTab(BandwidthView);
    202       addTab(PrerenderView);
    203       addTab(CrosView);
    204 
    205       this.tabSwitcher_.showMenuItem(LogsView.TAB_ID, cr.isChromeOS);
    206       this.tabSwitcher_.showMenuItem(CrosView.TAB_ID, cr.isChromeOS);
    207       this.tabSwitcher_.showMenuItem(CrosLogVisualizerView.TAB_ID,
    208                                      cr.isChromeOS);
    209     },
    210 
    211     /**
    212      * This function is called by the tab switcher when the current tab has been
    213      * changed. It will update the current URL to reflect the new active tab,
    214      * so the back can be used to return to previous view.
    215      */
    216     onTabSwitched_: function(oldTabId, newTabId) {
    217       // Update data needed by newly active tab, as it may be
    218       // significantly out of date.
    219       if (g_browser)
    220         g_browser.checkForUpdatedInfo();
    221 
    222       // Change the URL to match the new tab.
    223 
    224       var newTabHash = this.tabIdToHash_[newTabId];
    225       var parsed = parseUrlHash_(window.location.hash);
    226       if (parsed.tabHash != newTabHash) {
    227         window.location.hash = newTabHash;
    228       }
    229     },
    230 
    231     onUrlHashChange_: function() {
    232       var parsed = parseUrlHash_(window.location.hash);
    233 
    234       if (!parsed)
    235         return;
    236 
    237       if (!parsed.tabHash) {
    238         // Default to the export tab.
    239         parsed.tabHash = ExportView.TAB_HASH;
    240       }
    241 
    242       var tabId = this.hashToTabId_[parsed.tabHash];
    243 
    244       if (tabId) {
    245         this.tabSwitcher_.switchToTab(tabId);
    246         if (parsed.parameters) {
    247           var view = this.tabSwitcher_.getTabView(tabId);
    248           view.setParameters(parsed.parameters);
    249         }
    250       }
    251     },
    252 
    253   };
    254 
    255   /**
    256    * Takes the current hash in form of "#tab&param1=value1&param2=value2&..."
    257    * and parses it into a dictionary.
    258    *
    259    * Parameters and values are decoded with decodeURIComponent().
    260    */
    261   function parseUrlHash_(hash) {
    262     var parameters = hash.split('&');
    263 
    264     var tabHash = parameters[0];
    265     if (tabHash == '' || tabHash == '#') {
    266       tabHash = undefined;
    267     }
    268 
    269     // Split each string except the first around the '='.
    270     var paramDict = null;
    271     for (var i = 1; i < parameters.length; i++) {
    272       var paramStrings = parameters[i].split('=');
    273       if (paramStrings.length != 2)
    274         continue;
    275       if (paramDict == null)
    276         paramDict = {};
    277       var key = decodeURIComponent(paramStrings[0]);
    278       var value = decodeURIComponent(paramStrings[1]);
    279       paramDict[key] = value;
    280     }
    281 
    282     return {tabHash: tabHash, parameters: paramDict};
    283   }
    284 
    285   return MainView;
    286 })();
    287 
    288 function ConstantsObserver() {}
    289 
    290 /**
    291  * Loads all constants from |constants|.  On failure, global dictionaries are
    292  * not modifed.
    293  * @param {Object} receivedConstants The map of received constants.
    294  */
    295 ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) {
    296   if (!areValidConstants(receivedConstants))
    297     return;
    298 
    299   Constants = receivedConstants;
    300 
    301   EventType = Constants.logEventTypes;
    302   EventTypeNames = makeInverseMap(EventType);
    303   EventPhase = Constants.logEventPhase;
    304   EventSourceType = Constants.logSourceType;
    305   EventSourceTypeNames = makeInverseMap(EventSourceType);
    306   LogLevelType = Constants.logLevelType;
    307   ClientInfo = Constants.clientInfo;
    308   LoadFlag = Constants.loadFlag;
    309   NetError = Constants.netError;
    310   QuicError = Constants.quicError;
    311   QuicRstStreamError = Constants.quicRstStreamError;
    312   AddressFamily = Constants.addressFamily;
    313   LoadState = Constants.loadState;
    314   // certStatusFlag may not be present when loading old log Files
    315   if (typeof(Constants.certStatusFlag) == 'object')
    316     CertStatusFlag = Constants.certStatusFlag;
    317   else
    318     CertStatusFlag = {};
    319 
    320   timeutil.setTimeTickOffset(Constants.timeTickOffset);
    321 };
    322 
    323 /**
    324  * Returns true if it's given a valid-looking constants object.
    325  * @param {Object} receivedConstants The received map of constants.
    326  * @return {boolean} True if the |receivedConstants| object appears valid.
    327  */
    328 function areValidConstants(receivedConstants) {
    329   return typeof(receivedConstants) == 'object' &&
    330          typeof(receivedConstants.logEventTypes) == 'object' &&
    331          typeof(receivedConstants.clientInfo) == 'object' &&
    332          typeof(receivedConstants.logEventPhase) == 'object' &&
    333          typeof(receivedConstants.logSourceType) == 'object' &&
    334          typeof(receivedConstants.logLevelType) == 'object' &&
    335          typeof(receivedConstants.loadFlag) == 'object' &&
    336          typeof(receivedConstants.netError) == 'object' &&
    337          typeof(receivedConstants.addressFamily) == 'object' &&
    338          typeof(receivedConstants.timeTickOffset) == 'string' &&
    339          typeof(receivedConstants.logFormatVersion) == 'number';
    340 }
    341 
    342 /**
    343  * Returns the name for netError.
    344  *
    345  * Example: netErrorToString(-105) should return
    346  * "ERR_NAME_NOT_RESOLVED".
    347  * @param {number} netError The net error code.
    348  * @return {string} The name of the given error.
    349  */
    350 function netErrorToString(netError) {
    351   return getKeyWithValue(NetError, netError);
    352 }
    353 
    354 /**
    355  * Returns the name for quicError.
    356  *
    357  * Example: quicErrorToString(25) should return
    358  * "TIMED_OUT".
    359  * @param {number} quicError The QUIC error code.
    360  * @return {string} The name of the given error.
    361  */
    362 function quicErrorToString(quicError) {
    363   return getKeyWithValue(QuicError, quicError);
    364 }
    365 
    366 /**
    367  * Returns the name for quicRstStreamError.
    368  *
    369  * Example: quicRstStreamErrorToString(3) should return
    370  * "BAD_APPLICATION_PAYLOAD".
    371  * @param {number} quicRstStreamError The QUIC RST_STREAM error code.
    372  * @return {string} The name of the given error.
    373  */
    374 function quicRstStreamErrorToString(quicRstStreamError) {
    375   return getKeyWithValue(QuicRstStreamError, quicRstStreamError);
    376 }
    377 
    378 /**
    379  * Returns a string representation of |family|.
    380  * @param {number} family An AddressFamily
    381  * @return {string} A representation of the given family.
    382  */
    383 function addressFamilyToString(family) {
    384   var str = getKeyWithValue(AddressFamily, family);
    385   // All the address family start with ADDRESS_FAMILY_*.
    386   // Strip that prefix since it is redundant and only clutters the output.
    387   return str.replace(/^ADDRESS_FAMILY_/, '');
    388 }
    389