| // 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 LogLevelType = null; |
| var ClientInfo = null; |
| var NetError = null; |
| var LoadFlag = null; |
| var AddressFamily = 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 HorizontalSplitView |
| var superClass = HorizontalSplitView; |
| |
| /** |
| * 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()); |
| |
| // This view is a left navigation bar. |
| this.categoryTabSwitcher_ = new TabSwitcherView(); |
| var tabs = this.categoryTabSwitcher_; |
| |
| // Call superclass's constructor, initializing the view which lets you tab |
| // between the different sub-views. |
| superClass.call(this, |
| new DivView(MainView.CATEGORY_TAB_HANDLES_ID), |
| tabs); |
| |
| // 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. |
| tabs.addTab(CaptureView.TAB_HANDLE_ID, CaptureView.getInstance(), |
| false, true); |
| tabs.addTab(ExportView.TAB_HANDLE_ID, ExportView.getInstance(), |
| false, true); |
| tabs.addTab(ImportView.TAB_HANDLE_ID, ImportView.getInstance(), |
| false, true); |
| tabs.addTab(ProxyView.TAB_HANDLE_ID, ProxyView.getInstance(), |
| false, true); |
| tabs.addTab(EventsView.TAB_HANDLE_ID, EventsView.getInstance(), |
| false, true); |
| tabs.addTab(TimelineView.TAB_HANDLE_ID, TimelineView.getInstance(), |
| false, true); |
| tabs.addTab(DnsView.TAB_HANDLE_ID, DnsView.getInstance(), |
| false, true); |
| tabs.addTab(SocketsView.TAB_HANDLE_ID, SocketsView.getInstance(), |
| false, true); |
| tabs.addTab(SpdyView.TAB_HANDLE_ID, SpdyView.getInstance(), false, true); |
| tabs.addTab(HttpPipelineView.TAB_HANDLE_ID, HttpPipelineView.getInstance(), |
| false, true); |
| tabs.addTab(HttpCacheView.TAB_HANDLE_ID, HttpCacheView.getInstance(), |
| false, true); |
| tabs.addTab(ServiceProvidersView.TAB_HANDLE_ID, |
| ServiceProvidersView.getInstance(), false, cr.isWindows); |
| tabs.addTab(TestView.TAB_HANDLE_ID, TestView.getInstance(), false, true); |
| tabs.addTab(HSTSView.TAB_HANDLE_ID, HSTSView.getInstance(), false, true); |
| tabs.addTab(LogsView.TAB_HANDLE_ID, LogsView.getInstance(), |
| false, cr.isChromeOS); |
| tabs.addTab(PrerenderView.TAB_HANDLE_ID, PrerenderView.getInstance(), |
| false, true); |
| tabs.addTab(CrosView.TAB_HANDLE_ID, CrosView.getInstance(), |
| false, cr.isChromeOS); |
| |
| // Build a map from the anchor name of each tab handle to its "tab ID". |
| // We will consider navigations to the #hash as a switch tab request. |
| var anchorMap = {}; |
| var tabIds = tabs.getAllTabIds(); |
| for (var i = 0; i < tabIds.length; ++i) { |
| var aNode = $(tabIds[i]); |
| anchorMap[aNode.hash] = tabIds[i]; |
| } |
| // Default the empty hash to the data tab. |
| anchorMap['#'] = anchorMap[''] = ExportView.TAB_HANDLE_ID; |
| |
| window.onhashchange = onUrlHashChange.bind(null, tabs, anchorMap); |
| |
| // 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.statusView_ = StatusView.getInstance(this); |
| var verticalSplitView = new VerticalSplitView(this.statusView_, this); |
| this.statusView_.setLayoutParent(verticalSplitView); |
| var windowView = new WindowView(verticalSplitView); |
| |
| // Trigger initial layout. |
| windowView.resetGeometry(); |
| |
| // Select the initial view based on the current URL. |
| window.onhashchange(); |
| |
| // Tell the browser that we are ready to start receiving log events. |
| g_browser.sendReady(); |
| } |
| |
| // IDs for special HTML elements in index.html |
| MainView.CATEGORY_TAB_HANDLES_ID = 'category-tab-handles'; |
| |
| 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. |
| categoryTabSwitcher: function() { |
| return this.categoryTabSwitcher_; |
| }, |
| |
| /** |
| * 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. Also disable |
| // hiding cookies, so if the log dump has them, they'll be displayed. |
| this.statusView_.switchToSubView('loaded').setFileName(opt_fileName); |
| SourceTracker.getInstance().setPrivacyStripping(false); |
| } else { |
| // Otherwise, the "Stop Capturing" button was presumably pressed. |
| // Don't disable hiding cookies, so created log dumps won't have them, |
| // unless the user toggles the option. |
| this.statusView_.switchToSubView('halted'); |
| } |
| }, |
| |
| 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; }'); |
| } |
| }; |
| |
| /** |
| * Takes the current hash in form of "#tab¶m1=value1¶m2=value2&...". |
| * Puts the parameters in an object, and passes the resulting object to |
| * |categoryTabSwitcher|. Uses tab and |anchorMap| to find a tab ID, |
| * which it also passes to the tab switcher. |
| * |
| * Parameters and values are decoded with decodeURIComponent(). |
| */ |
| function onUrlHashChange(categoryTabSwitcher, anchorMap) { |
| var parameters = window.location.hash.split('&'); |
| |
| var tabId = anchorMap[parameters[0]]; |
| if (!tabId) |
| return; |
| |
| // 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; |
| } |
| |
| categoryTabSwitcher.switchToTab(tabId, 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); |
| LogLevelType = Constants.logLevelType; |
| ClientInfo = Constants.clientInfo; |
| LoadFlag = Constants.loadFlag; |
| NetError = Constants.netError; |
| AddressFamily = Constants.addressFamily; |
| |
| 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.logLevelType) == '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) would return |
| * "ERR_NAME_NOT_RESOLVED". |
| * @param {number} netError The net error code. |
| * @return {string} The name of the given error. |
| */ |
| function netErrorToString(netError) { |
| var str = getKeyWithValue(NetError, netError); |
| if (str == '?') |
| return str; |
| return 'ERR_' + str; |
| } |
| |
| /** |
| * 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_/, ''); |
| } |