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.
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.
11 var EventTypeNames = null;
12 var EventPhase = null;
13 var EventSourceType = null;
14 var EventSourceTypeNames = null;
15 var LogLevelType = null;
16 var ClientInfo = null;
19 var QuicRstStreamError = null;
21 var CertStatusFlag = null;
23 var AddressFamily = null;
24 var SdchProblemCode = null;
25 var DataReductionProxyBypassEventType = null;
28 * Dictionary of all constants, used for saving log files.
33 * Object to communicate between the renderer and the browser.
34 * @type {!BrowserBridge}
39 * This class is the root view object of the page. It owns all the other
40 * views, and manages switching between them. It is also responsible for
41 * initializing the views and the BrowserBridge.
43 var MainView = (function() {
46 // We inherit from WindowView
47 var superClass = WindowView;
50 * Main entry point. Called once the page has loaded.
54 assertFirstConstructorCall(MainView);
57 document.body.classList.add('touch');
59 // This must be initialized before the tabs, so they can register as
61 g_browser = BrowserBridge.getInstance();
63 // This must be the first constants observer, so other constants observers
64 // can safely use the globals, rather than depending on walking through
65 // the constants themselves.
66 g_browser.addConstantsObserver(new ConstantsObserver());
68 // Create the tab switcher.
71 // Cut out a small vertical strip at the top of the window, to display
72 // a high level status (i.e. if we are capturing events, or displaying a
73 // log file). Below it we will position the main tabs and their content
75 this.topBarView_ = TopBarView.getInstance(this);
76 var verticalSplitView = new VerticalSplitView(
77 this.topBarView_, this.tabSwitcher_);
79 superClass.call(this, verticalSplitView);
81 // Trigger initial layout.
84 window.onhashchange = this.onUrlHashChange_.bind(this);
86 // Select the initial view based on the current URL.
87 window.onhashchange();
89 // Tell the browser that we are ready to start receiving log events.
90 this.topBarView_.switchToSubView('capture');
91 g_browser.sendReady();
94 cr.addSingletonGetter(MainView);
96 // Tracks if we're viewing a loaded log file, so views can behave
97 // appropriately. Global so safe to call during construction.
98 var isViewingLoadedLog = false;
100 MainView.isViewingLoadedLog = function() {
101 return isViewingLoadedLog;
104 MainView.prototype = {
105 // Inherit the superclass's methods.
106 __proto__: superClass.prototype,
108 // This is exposed both so the log import/export code can enumerate all the
109 // tabs, and for testing.
110 tabSwitcher: function() {
111 return this.tabSwitcher_;
115 * Prevents receiving/sending events to/from the browser, so loaded data
116 * will not be mixed with current Chrome state. Also hides any interactive
117 * HTML elements that send messages to the browser. Cannot be undone
118 * without reloading the page. Must be called before passing loaded data
119 * to the individual views.
121 * @param {string} opt_fileName The name of the log file that has been
122 * loaded, if we're loading a log file.
124 onLoadLog: function(opt_fileName) {
125 isViewingLoadedLog = true;
127 this.stopCapturing();
128 if (opt_fileName != undefined) {
129 // If there's a file name, a log file was loaded, so swap out the status
130 // bar to indicate we're no longer capturing events. Also disable
131 // hiding cookies, so if the log dump has them, they'll be displayed.
132 this.topBarView_.switchToSubView('loaded').setFileName(opt_fileName);
133 $(ExportView.PRIVACY_STRIPPING_CHECKBOX_ID).checked = false;
134 SourceTracker.getInstance().setPrivacyStripping(false);
136 // Otherwise, the "Stop Capturing" button was presumably pressed.
137 // Don't disable hiding cookies, so created log dumps won't have them,
138 // unless the user toggles the option.
139 this.topBarView_.switchToSubView('halted');
143 switchToViewOnlyMode: function() {
144 // Since this won't be dumped to a file, we don't want to remove
145 // cookies and credentials.
146 log_util.createLogDumpAsync('', log_util.loadLogFile, false);
149 stopCapturing: function() {
151 document.styleSheets[0].insertRule(
152 '.hide-when-not-capturing { display: none; }', 0);
155 initTabs_: function() {
156 this.tabIdToHash_ = {};
157 this.hashToTabId_ = {};
159 this.tabSwitcher_ = new TabSwitcherView(
160 $(TopBarView.TAB_DROPDOWN_MENU_ID),
161 this.onTabSwitched_.bind(this));
163 // Helper function to add a tab given the class for a view singleton.
164 var addTab = function(viewClass) {
165 var tabId = viewClass.TAB_ID;
166 var tabHash = viewClass.TAB_HASH;
167 var tabName = viewClass.TAB_NAME;
168 var view = viewClass.getInstance();
170 if (!tabId || !view || !tabHash || !tabName) {
171 throw Error('Invalid view class for tab');
174 if (tabHash.charAt(0) != '#') {
175 throw Error('Tab hashes must start with a #');
178 this.tabSwitcher_.addTab(tabId, view, tabName);
179 this.tabIdToHash_[tabId] = tabHash;
180 this.hashToTabId_[tabHash] = tabId;
183 // Populate the main tabs. Even tabs that don't contain information for
184 // the running OS should be created, so they can load log dumps from other
191 addTab(WaterfallView);
192 addTab(TimelineView);
198 addTab(HttpCacheView);
202 addTab(BandwidthView);
203 addTab(PrerenderView);
206 this.tabSwitcher_.showMenuItem(CrosView.TAB_ID, cr.isChromeOS);
210 * This function is called by the tab switcher when the current tab has been
211 * changed. It will update the current URL to reflect the new active tab,
212 * so the back can be used to return to previous view.
214 onTabSwitched_: function(oldTabId, newTabId) {
215 // Update data needed by newly active tab, as it may be
216 // significantly out of date.
218 g_browser.checkForUpdatedInfo();
220 // Change the URL to match the new tab.
222 var newTabHash = this.tabIdToHash_[newTabId];
223 var parsed = parseUrlHash_(window.location.hash);
224 if (parsed.tabHash != newTabHash) {
225 window.location.hash = newTabHash;
229 onUrlHashChange_: function() {
230 var parsed = parseUrlHash_(window.location.hash);
235 if (!parsed.tabHash) {
236 // Default to the export tab.
237 parsed.tabHash = ExportView.TAB_HASH;
240 var tabId = this.hashToTabId_[parsed.tabHash];
243 this.tabSwitcher_.switchToTab(tabId);
244 if (parsed.parameters) {
245 var view = this.tabSwitcher_.getTabView(tabId);
246 view.setParameters(parsed.parameters);
254 * Takes the current hash in form of "#tab¶m1=value1¶m2=value2&..."
255 * and parses it into a dictionary.
257 * Parameters and values are decoded with decodeURIComponent().
259 function parseUrlHash_(hash) {
260 var parameters = hash.split('&');
262 var tabHash = parameters[0];
263 if (tabHash == '' || tabHash == '#') {
267 // Split each string except the first around the '='.
268 var paramDict = null;
269 for (var i = 1; i < parameters.length; i++) {
270 var paramStrings = parameters[i].split('=');
271 if (paramStrings.length != 2)
273 if (paramDict == null)
275 var key = decodeURIComponent(paramStrings[0]);
276 var value = decodeURIComponent(paramStrings[1]);
277 paramDict[key] = value;
280 return {tabHash: tabHash, parameters: paramDict};
286 function ConstantsObserver() {}
289 * Loads all constants from |constants|. On failure, global dictionaries are
291 * @param {Object} receivedConstants The map of received constants.
293 ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) {
294 if (!areValidConstants(receivedConstants))
297 Constants = receivedConstants;
299 EventType = Constants.logEventTypes;
300 EventTypeNames = makeInverseMap(EventType);
301 EventPhase = Constants.logEventPhase;
302 EventSourceType = Constants.logSourceType;
303 EventSourceTypeNames = makeInverseMap(EventSourceType);
304 LogLevelType = Constants.logLevelType;
305 ClientInfo = Constants.clientInfo;
306 LoadFlag = Constants.loadFlag;
307 NetError = Constants.netError;
308 QuicError = Constants.quicError;
309 QuicRstStreamError = Constants.quicRstStreamError;
310 AddressFamily = Constants.addressFamily;
311 LoadState = Constants.loadState;
312 SdchProblemCode = Constants.sdchProblemCode;
313 DataReductionProxyBypassEventType =
314 Constants.dataReductionProxyBypassEventType;
315 // certStatusFlag may not be present when loading old log Files
316 if (typeof(Constants.certStatusFlag) == 'object')
317 CertStatusFlag = Constants.certStatusFlag;
321 timeutil.setTimeTickOffset(Constants.timeTickOffset);
325 * Returns true if it's given a valid-looking constants object.
326 * @param {Object} receivedConstants The received map of constants.
327 * @return {boolean} True if the |receivedConstants| object appears valid.
329 function areValidConstants(receivedConstants) {
330 return typeof(receivedConstants) == 'object' &&
331 typeof(receivedConstants.logEventTypes) == 'object' &&
332 typeof(receivedConstants.clientInfo) == 'object' &&
333 typeof(receivedConstants.logEventPhase) == 'object' &&
334 typeof(receivedConstants.logSourceType) == 'object' &&
335 typeof(receivedConstants.logLevelType) == 'object' &&
336 typeof(receivedConstants.loadFlag) == 'object' &&
337 typeof(receivedConstants.netError) == 'object' &&
338 typeof(receivedConstants.addressFamily) == 'object' &&
339 typeof(receivedConstants.timeTickOffset) == 'string' &&
340 typeof(receivedConstants.logFormatVersion) == 'number';
344 * Returns the name for netError.
346 * Example: netErrorToString(-105) should return
347 * "ERR_NAME_NOT_RESOLVED".
348 * @param {number} netError The net error code.
349 * @return {string} The name of the given error.
351 function netErrorToString(netError) {
352 return getKeyWithValue(NetError, netError);
356 * Returns the name for quicError.
358 * Example: quicErrorToString(25) should return
360 * @param {number} quicError The QUIC error code.
361 * @return {string} The name of the given error.
363 function quicErrorToString(quicError) {
364 return getKeyWithValue(QuicError, quicError);
368 * Returns the name for quicRstStreamError.
370 * Example: quicRstStreamErrorToString(3) should return
371 * "BAD_APPLICATION_PAYLOAD".
372 * @param {number} quicRstStreamError The QUIC RST_STREAM error code.
373 * @return {string} The name of the given error.
375 function quicRstStreamErrorToString(quicRstStreamError) {
376 return getKeyWithValue(QuicRstStreamError, quicRstStreamError);
380 * Returns a string representation of |family|.
381 * @param {number} family An AddressFamily
382 * @return {string} A representation of the given family.
384 function addressFamilyToString(family) {
385 var str = getKeyWithValue(AddressFamily, family);
386 // All the address family start with ADDRESS_FAMILY_*.
387 // Strip that prefix since it is redundant and only clutters the output.
388 return str.replace(/^ADDRESS_FAMILY_/, '');
392 * Returns the name for sdchProblemCode.
394 * Example: sdchProblemCodeToString(5) should return
395 * "DECODE_BODY_ERROR".
396 * @param {number} sdchProblemCode The SDCH problem code.
397 * @return {string} The name of the given problem code.
399 function sdchProblemCodeToString(sdchProblemCode) {
400 return getKeyWithValue(SdchProblemCode, sdchProblemCode);