Serving version 1 of the web app. All code has been copied from the
master branch to this gh-pages branch:

https://chromium.googlesource.com/external/github.com/catapult-project/catapult/+/master/netlog_viewer/

BUG=chromium:472699
NOTRY=true
NOPRESUBMIT=true

Review-Url: https://codereview.chromium.org/2154753002
diff --git a/netlog_viewer/main.js b/netlog_viewer/main.js
new file mode 100644
index 0000000..84ed15c
--- /dev/null
+++ b/netlog_viewer/main.js
@@ -0,0 +1,394 @@
+// 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.
+
+/**
+ * Dictionary of constants (Initialized soon after loading by data from browser,
+ * updated on load log).  The *Types dictionaries map strings to numeric IDs,
+ * while the *TypeNames are the other way around.
+ */
+var EventType = null;
+var EventTypeNames = null;
+var EventPhase = null;
+var EventSourceType = null;
+var EventSourceTypeNames = null;
+var ClientInfo = null;
+var NetError = null;
+var QuicError = null;
+var QuicRstStreamError = null;
+var LoadFlag = null;
+var CertStatusFlag = null;
+var LoadState = null;
+var AddressFamily = null;
+var SdchProblemCode = null;
+var DataReductionProxyBypassEventType = null;
+
+/**
+ * Dictionary of all constants, used for saving log files.
+ */
+var Constants = null;
+
+/**
+ * Object to communicate between the renderer and the browser.
+ * @type {!BrowserBridge}
+ */
+var g_browser = null;
+
+/**
+ * This class is the root view object of the page.  It owns all the other
+ * views, and manages switching between them.  It is also responsible for
+ * initializing the views and the BrowserBridge.
+ */
+var MainView = (function() {
+  'use strict';
+
+  // We inherit from WindowView
+  var superClass = WindowView;
+
+  /**
+   * Main entry point. Called once the page has loaded.
+   *  @constructor
+   */
+  function MainView() {
+    assertFirstConstructorCall(MainView);
+
+    if (hasTouchScreen())
+      document.body.classList.add('touch');
+
+    // This must be initialized before the tabs, so they can register as
+    // observers.
+    g_browser = BrowserBridge.getInstance();
+
+    // This must be the first constants observer, so other constants observers
+    // can safely use the globals, rather than depending on walking through
+    // the constants themselves.
+    g_browser.addConstantsObserver(new ConstantsObserver());
+
+    // Create the tab switcher.
+    this.initTabs_();
+
+    // Cut out a small vertical strip at the top of the window, to display
+    // a high level status (i.e. if we are capturing events, or displaying a
+    // log file). Below it we will position the main tabs and their content
+    // area.
+    this.topBarView_ = TopBarView.getInstance(this);
+    var verticalSplitView = new VerticalSplitView(
+        this.topBarView_, this.tabSwitcher_);
+
+    superClass.call(this, verticalSplitView);
+
+    // Trigger initial layout.
+    this.resetGeometry();
+
+    window.onhashchange = this.onUrlHashChange_.bind(this);
+
+    // Select the initial view based on the current URL.
+    window.onhashchange();
+
+    // No log file loaded yet so set the status bar to that state.
+    this.topBarView_.switchToSubView('loaded').setFileName(
+        'No log to display.');
+
+    // TODO(rayraymond): Follow-up is to completely remove all code from
+    // g_browser that interacts with sending/receiving messages from
+    // browser.
+    g_browser.disable();
+  }
+
+  cr.addSingletonGetter(MainView);
+
+  // Tracks if we're viewing a loaded log file, so views can behave
+  // appropriately.  Global so safe to call during construction.
+  var isViewingLoadedLog = false;
+
+  MainView.isViewingLoadedLog = function() {
+    return isViewingLoadedLog;
+  };
+
+  MainView.prototype = {
+    // Inherit the superclass's methods.
+    __proto__: superClass.prototype,
+
+    // This is exposed both so the log import/export code can enumerate all the
+    // tabs, and for testing.
+    tabSwitcher: function() {
+      return this.tabSwitcher_;
+    },
+
+    /**
+     * Prevents receiving/sending events to/from the browser, so loaded data
+     * will not be mixed with current Chrome state.  Also hides any interactive
+     * HTML elements that send messages to the browser.  Cannot be undone
+     * without reloading the page.  Must be called before passing loaded data
+     * to the individual views.
+     *
+     * @param {string} opt_fileName The name of the log file that has been
+     *     loaded, if we're loading a log file.
+     */
+    onLoadLog: function(opt_fileName) {
+      isViewingLoadedLog = true;
+
+      this.stopCapturing();
+      if (opt_fileName != undefined) {
+        // If there's a file name, a log file was loaded, so swap out the status
+        // bar to indicate we're no longer capturing events.
+        this.topBarView_.switchToSubView('loaded').setFileName(opt_fileName);
+        SourceTracker.getInstance().setPrivacyStripping(false);
+      }
+    },
+
+    switchToViewOnlyMode: function() {
+      // Since this won't be dumped to a file, we don't want to remove
+      // cookies and credentials.
+      log_util.createLogDumpAsync('', log_util.loadLogFile, false);
+    },
+
+    stopCapturing: function() {
+      g_browser.disable();
+      document.styleSheets[0].insertRule(
+          '.hide-when-not-capturing { display: none; }', 0);
+    },
+
+    initTabs_: function() {
+      this.tabIdToHash_ = {};
+      this.hashToTabId_ = {};
+
+      this.tabSwitcher_ = new TabSwitcherView(this.onTabSwitched_.bind(this));
+
+      // Helper function to add a tab given the class for a view singleton.
+      var addTab = function(viewClass) {
+        var tabId = viewClass.TAB_ID;
+        var tabHash = viewClass.TAB_HASH;
+        var tabName = viewClass.TAB_NAME;
+        var view = viewClass.getInstance();
+
+        if (!tabId || !view || !tabHash || !tabName) {
+          throw Error('Invalid view class for tab');
+        }
+
+        if (tabHash.charAt(0) != '#') {
+          throw Error('Tab hashes must start with a #');
+        }
+
+        this.tabSwitcher_.addTab(tabId, view, tabName, tabHash);
+        this.tabIdToHash_[tabId] = tabHash;
+        this.hashToTabId_[tabHash] = tabId;
+      }.bind(this);
+
+      // Populate the main tabs.  Even tabs that don't contain information for
+      // the running OS should be created, so they can load log dumps from other
+      // OSes.
+      addTab(ImportView);
+      addTab(ProxyView);
+      addTab(EventsView);
+      addTab(TimelineView);
+      addTab(DnsView);
+      addTab(SocketsView);
+      addTab(AltSvcView);
+      addTab(SpdyView);
+      addTab(QuicView);
+      addTab(SdchView);
+      addTab(HttpCacheView);
+      // TODO(rayraymond): Re-enable, Modules, Bandwidth, and Prerender tabs.
+      // addTab(ModulesView);
+      // addTab(BandwidthView);
+      // addTab(PrerenderView);
+      addTab(CrosView);
+
+      this.tabSwitcher_.showTabLink(CrosView.TAB_ID, cr.isChromeOS);
+    },
+
+    /**
+     * This function is called by the tab switcher when the current tab has been
+     * changed. It will update the current URL to reflect the new active tab,
+     * so the back can be used to return to previous view.
+     */
+    onTabSwitched_: function(oldTabId, newTabId) {
+      // Update data needed by newly active tab, as it may be
+      // significantly out of date.
+      if (g_browser)
+        g_browser.checkForUpdatedInfo();
+
+      // Change the URL to match the new tab.
+
+      var newTabHash = this.tabIdToHash_[newTabId];
+      var parsed = parseUrlHash_(window.location.hash);
+      if (parsed.tabHash != newTabHash) {
+        window.location.hash = newTabHash;
+      }
+    },
+
+    onUrlHashChange_: function() {
+      var parsed = parseUrlHash_(window.location.hash);
+
+      if (!parsed)
+        return;
+
+      if (!parsed.tabHash) {
+        // Default to the import tab.
+        parsed.tabHash = ImportView.TAB_HASH;
+      }
+
+      var tabId = this.hashToTabId_[parsed.tabHash];
+
+      if (tabId) {
+        this.tabSwitcher_.switchToTab(tabId);
+        if (parsed.parameters) {
+          var view = this.tabSwitcher_.getTabView(tabId);
+          view.setParameters(parsed.parameters);
+        }
+      }
+    },
+
+  };
+
+  /**
+   * Takes the current hash in form of "#tab&param1=value1&param2=value2&..."
+   * and parses it into a dictionary.
+   *
+   * Parameters and values are decoded with decodeURIComponent().
+   */
+  function parseUrlHash_(hash) {
+    var parameters = hash.split('&');
+
+    var tabHash = parameters[0];
+    if (tabHash == '' || tabHash == '#') {
+      tabHash = undefined;
+    }
+
+    // Split each string except the first around the '='.
+    var paramDict = null;
+    for (var i = 1; i < parameters.length; i++) {
+      var paramStrings = parameters[i].split('=');
+      if (paramStrings.length != 2)
+        continue;
+      if (paramDict == null)
+        paramDict = {};
+      var key = decodeURIComponent(paramStrings[0]);
+      var value = decodeURIComponent(paramStrings[1]);
+      paramDict[key] = value;
+    }
+
+    return {tabHash: tabHash, parameters: paramDict};
+  }
+
+  return MainView;
+})();
+
+function ConstantsObserver() {}
+
+/**
+ * Loads all constants from |constants|.  On failure, global dictionaries are
+ * not modifed.
+ * @param {Object} receivedConstants The map of received constants.
+ */
+ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) {
+  if (!areValidConstants(receivedConstants))
+    return;
+
+  Constants = receivedConstants;
+
+  EventType = Constants.logEventTypes;
+  EventTypeNames = makeInverseMap(EventType);
+  EventPhase = Constants.logEventPhase;
+  EventSourceType = Constants.logSourceType;
+  EventSourceTypeNames = makeInverseMap(EventSourceType);
+  ClientInfo = Constants.clientInfo;
+  LoadFlag = Constants.loadFlag;
+  NetError = Constants.netError;
+  QuicError = Constants.quicError;
+  QuicRstStreamError = Constants.quicRstStreamError;
+  AddressFamily = Constants.addressFamily;
+  LoadState = Constants.loadState;
+  SdchProblemCode = Constants.sdchProblemCode;
+  DataReductionProxyBypassEventType =
+      Constants.dataReductionProxyBypassEventType;
+  DataReductionProxyBypassActionType =
+      Constants.dataReductionProxyBypassActionType;
+  // certStatusFlag may not be present when loading old log Files
+  if (typeof(Constants.certStatusFlag) == 'object')
+    CertStatusFlag = Constants.certStatusFlag;
+  else
+    CertStatusFlag = {};
+
+  timeutil.setTimeTickOffset(Constants.timeTickOffset);
+};
+
+/**
+ * Returns true if it's given a valid-looking constants object.
+ * @param {Object} receivedConstants The received map of constants.
+ * @return {boolean} True if the |receivedConstants| object appears valid.
+ */
+function areValidConstants(receivedConstants) {
+  return typeof(receivedConstants) == 'object' &&
+         typeof(receivedConstants.logEventTypes) == 'object' &&
+         typeof(receivedConstants.clientInfo) == 'object' &&
+         typeof(receivedConstants.logEventPhase) == 'object' &&
+         typeof(receivedConstants.logSourceType) == 'object' &&
+         typeof(receivedConstants.loadFlag) == 'object' &&
+         typeof(receivedConstants.netError) == 'object' &&
+         typeof(receivedConstants.addressFamily) == 'object' &&
+         typeof(receivedConstants.timeTickOffset) == 'string' &&
+         typeof(receivedConstants.logFormatVersion) == 'number';
+}
+
+/**
+ * Returns the name for netError.
+ *
+ * Example: netErrorToString(-105) should return
+ * "ERR_NAME_NOT_RESOLVED".
+ * @param {number} netError The net error code.
+ * @return {string} The name of the given error.
+ */
+function netErrorToString(netError) {
+  return getKeyWithValue(NetError, netError);
+}
+
+/**
+ * Returns the name for quicError.
+ *
+ * Example: quicErrorToString(25) should return
+ * "TIMED_OUT".
+ * @param {number} quicError The QUIC error code.
+ * @return {string} The name of the given error.
+ */
+function quicErrorToString(quicError) {
+  return getKeyWithValue(QuicError, quicError);
+}
+
+/**
+ * Returns the name for quicRstStreamError.
+ *
+ * Example: quicRstStreamErrorToString(3) should return
+ * "BAD_APPLICATION_PAYLOAD".
+ * @param {number} quicRstStreamError The QUIC RST_STREAM error code.
+ * @return {string} The name of the given error.
+ */
+function quicRstStreamErrorToString(quicRstStreamError) {
+  return getKeyWithValue(QuicRstStreamError, quicRstStreamError);
+}
+
+/**
+ * Returns a string representation of |family|.
+ * @param {number} family An AddressFamily
+ * @return {string} A representation of the given family.
+ */
+function addressFamilyToString(family) {
+  var str = getKeyWithValue(AddressFamily, family);
+  // All the address family start with ADDRESS_FAMILY_*.
+  // Strip that prefix since it is redundant and only clutters the output.
+  return str.replace(/^ADDRESS_FAMILY_/, '');
+}
+
+/**
+ * Returns the name for sdchProblemCode.
+ *
+ * Example: sdchProblemCodeToString(5) should return
+ * "DECODE_BODY_ERROR".
+ * @param {number} sdchProblemCode The SDCH problem code.
+ * @return {string} The name of the given problem code.
+ */
+function sdchProblemCodeToString(sdchProblemCode) {
+  return getKeyWithValue(SdchProblemCode, sdchProblemCode);
+}
+