Fix infinite recursion on hiding panel when created during fullscreen mode.
[chromium-blink-merge.git] / chrome / browser / resources / net_internals / main.js
blob78d18b8efe06cb7edb81e7fda975b66719ab2028
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.
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 LoadState = null;
22 var AddressFamily = null;
24 /**
25 * Dictionary of all constants, used for saving log files.
27 var Constants = null;
29 /**
30 * Object to communicate between the renderer and the browser.
31 * @type {!BrowserBridge}
33 var g_browser = null;
35 /**
36 * This class is the root view object of the page. It owns all the other
37 * views, and manages switching between them. It is also responsible for
38 * initializing the views and the BrowserBridge.
40 var MainView = (function() {
41 'use strict';
43 // We inherit from WindowView
44 var superClass = WindowView;
46 /**
47 * Main entry point. Called once the page has loaded.
48 * @constructor
50 function MainView() {
51 assertFirstConstructorCall(MainView);
53 if (hasTouchScreen())
54 document.body.classList.add('touch');
56 // This must be initialized before the tabs, so they can register as
57 // observers.
58 g_browser = BrowserBridge.getInstance();
60 // This must be the first constants observer, so other constants observers
61 // can safely use the globals, rather than depending on walking through
62 // the constants themselves.
63 g_browser.addConstantsObserver(new ConstantsObserver());
65 // Create the tab switcher.
66 this.initTabs_();
68 // Cut out a small vertical strip at the top of the window, to display
69 // a high level status (i.e. if we are capturing events, or displaying a
70 // log file). Below it we will position the main tabs and their content
71 // area.
72 this.topBarView_ = TopBarView.getInstance(this);
73 var verticalSplitView = new VerticalSplitView(
74 this.topBarView_, this.tabSwitcher_);
76 superClass.call(this, verticalSplitView);
78 // Trigger initial layout.
79 this.resetGeometry();
81 window.onhashchange = this.onUrlHashChange_.bind(this);
83 // Select the initial view based on the current URL.
84 window.onhashchange();
86 // Tell the browser that we are ready to start receiving log events.
87 this.topBarView_.switchToSubView('capture');
88 g_browser.sendReady();
91 cr.addSingletonGetter(MainView);
93 // Tracks if we're viewing a loaded log file, so views can behave
94 // appropriately. Global so safe to call during construction.
95 var isViewingLoadedLog = false;
97 MainView.isViewingLoadedLog = function() {
98 return isViewingLoadedLog;
101 MainView.prototype = {
102 // Inherit the superclass's methods.
103 __proto__: superClass.prototype,
105 // This is exposed both so the log import/export code can enumerate all the
106 // tabs, and for testing.
107 tabSwitcher: function() {
108 return this.tabSwitcher_;
112 * Prevents receiving/sending events to/from the browser, so loaded data
113 * will not be mixed with current Chrome state. Also hides any interactive
114 * HTML elements that send messages to the browser. Cannot be undone
115 * without reloading the page. Must be called before passing loaded data
116 * to the individual views.
118 * @param {string} opt_fileName The name of the log file that has been
119 * loaded, if we're loading a log file.
121 onLoadLog: function(opt_fileName) {
122 isViewingLoadedLog = true;
124 this.stopCapturing();
125 if (opt_fileName != undefined) {
126 // If there's a file name, a log file was loaded, so swap out the status
127 // bar to indicate we're no longer capturing events. Also disable
128 // hiding cookies, so if the log dump has them, they'll be displayed.
129 this.topBarView_.switchToSubView('loaded').setFileName(opt_fileName);
130 $(ExportView.PRIVACY_STRIPPING_CHECKBOX_ID).checked = false;
131 SourceTracker.getInstance().setPrivacyStripping(false);
132 } else {
133 // Otherwise, the "Stop Capturing" button was presumably pressed.
134 // Don't disable hiding cookies, so created log dumps won't have them,
135 // unless the user toggles the option.
136 this.topBarView_.switchToSubView('halted');
140 switchToViewOnlyMode: function() {
141 // Since this won't be dumped to a file, we don't want to remove
142 // cookies and credentials.
143 log_util.createLogDumpAsync('', log_util.loadLogFile, false);
146 stopCapturing: function() {
147 g_browser.disable();
148 document.styleSheets[0].insertRule(
149 '.hide-when-not-capturing { display: none; }', 0);
152 initTabs_: function() {
153 this.tabIdToHash_ = {};
154 this.hashToTabId_ = {};
156 this.tabSwitcher_ = new TabSwitcherView(
157 $(TopBarView.TAB_DROPDOWN_MENU_ID),
158 this.onTabSwitched_.bind(this));
160 // Helper function to add a tab given the class for a view singleton.
161 var addTab = function(viewClass) {
162 var tabId = viewClass.TAB_ID;
163 var tabHash = viewClass.TAB_HASH;
164 var tabName = viewClass.TAB_NAME;
165 var view = viewClass.getInstance();
167 if (!tabId || !view || !tabHash || !tabName) {
168 throw Error('Invalid view class for tab');
171 if (tabHash.charAt(0) != '#') {
172 throw Error('Tab hashes must start with a #');
175 this.tabSwitcher_.addTab(tabId, view, tabName);
176 this.tabIdToHash_[tabId] = tabHash;
177 this.hashToTabId_[tabHash] = tabId;
178 }.bind(this);
180 // Populate the main tabs. Even tabs that don't contain information for
181 // the running OS should be created, so they can load log dumps from other
182 // OSes.
183 addTab(CaptureView);
184 addTab(ExportView);
185 addTab(ImportView);
186 addTab(ProxyView);
187 addTab(EventsView);
188 addTab(WaterfallView);
189 addTab(TimelineView);
190 addTab(DnsView);
191 addTab(SocketsView);
192 addTab(SpdyView);
193 addTab(QuicView);
194 addTab(HttpPipelineView);
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);
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);
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.
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();
222 // Change the URL to match the new tab.
224 var newTabHash = this.tabIdToHash_[newTabId];
225 var parsed = parseUrlHash_(window.location.hash);
226 if (parsed.tabHash != newTabHash) {
227 window.location.hash = newTabHash;
231 onUrlHashChange_: function() {
232 var parsed = parseUrlHash_(window.location.hash);
234 if (!parsed)
235 return;
237 if (!parsed.tabHash) {
238 // Default to the export tab.
239 parsed.tabHash = ExportView.TAB_HASH;
242 var tabId = this.hashToTabId_[parsed.tabHash];
244 if (tabId) {
245 this.tabSwitcher_.switchToTab(tabId);
246 if (parsed.parameters) {
247 var view = this.tabSwitcher_.getTabView(tabId);
248 view.setParameters(parsed.parameters);
256 * Takes the current hash in form of "#tab&param1=value1&param2=value2&..."
257 * and parses it into a dictionary.
259 * Parameters and values are decoded with decodeURIComponent().
261 function parseUrlHash_(hash) {
262 var parameters = hash.split('&');
264 var tabHash = parameters[0];
265 if (tabHash == '' || tabHash == '#') {
266 tabHash = undefined;
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;
282 return {tabHash: tabHash, parameters: paramDict};
285 return MainView;
286 })();
288 function ConstantsObserver() {}
291 * Loads all constants from |constants|. On failure, global dictionaries are
292 * not modifed.
293 * @param {Object} receivedConstants The map of received constants.
295 ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) {
296 if (!areValidConstants(receivedConstants))
297 return;
299 Constants = receivedConstants;
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;
315 timeutil.setTimeTickOffset(Constants.timeTickOffset);
319 * Returns true if it's given a valid-looking constants object.
320 * @param {Object} receivedConstants The received map of constants.
321 * @return {boolean} True if the |receivedConstants| object appears valid.
323 function areValidConstants(receivedConstants) {
324 return typeof(receivedConstants) == 'object' &&
325 typeof(receivedConstants.logEventTypes) == 'object' &&
326 typeof(receivedConstants.clientInfo) == 'object' &&
327 typeof(receivedConstants.logEventPhase) == 'object' &&
328 typeof(receivedConstants.logSourceType) == 'object' &&
329 typeof(receivedConstants.logLevelType) == 'object' &&
330 typeof(receivedConstants.loadFlag) == 'object' &&
331 typeof(receivedConstants.netError) == 'object' &&
332 typeof(receivedConstants.addressFamily) == 'object' &&
333 typeof(receivedConstants.timeTickOffset) == 'string' &&
334 typeof(receivedConstants.logFormatVersion) == 'number';
338 * Returns the name for netError.
340 * Example: netErrorToString(-105) should return
341 * "ERR_NAME_NOT_RESOLVED".
342 * @param {number} netError The net error code.
343 * @return {string} The name of the given error.
345 function netErrorToString(netError) {
346 var str = getKeyWithValue(NetError, netError);
347 if (str == '?')
348 return str;
349 return 'ERR_' + str;
353 * Returns the name for quicError.
355 * Example: quicErrorToString(25) should return
356 * "TIMED_OUT".
357 * @param {number} quicError The QUIC error code.
358 * @return {string} The name of the given error.
360 function quicErrorToString(quicError) {
361 return getKeyWithValue(QuicError, quicError);
365 * Returns the name for quicRstStreamError.
367 * Example: quicRstStreamErrorToString(3) should return
368 * "BAD_APPLICATION_PAYLOAD".
369 * @param {number} quicRstStreamError The QUIC RST_STREAM error code.
370 * @return {string} The name of the given error.
372 function quicRstStreamErrorToString(quicRstStreamError) {
373 return getKeyWithValue(QuicRstStreamError, quicRstStreamError);
377 * Returns a string representation of |family|.
378 * @param {number} family An AddressFamily
379 * @return {string} A representation of the given family.
381 function addressFamilyToString(family) {
382 var str = getKeyWithValue(AddressFamily, family);
383 // All the address family start with ADDRESS_FAMILY_*.
384 // Strip that prefix since it is redundant and only clutters the output.
385 return str.replace(/^ADDRESS_FAMILY_/, '');