Index: netlog_viewer/main.js |
diff --git a/netlog_viewer/main.js b/netlog_viewer/main.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..84ed15c23b32382bd2a6298d166ba9587b9f763b |
--- /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¶m1=value1¶m2=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); |
+} |
+ |