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;
26 * Dictionary of all constants, used for saving log files.
31 * Object to communicate between the renderer and the browser.
32 * @type {!BrowserBridge}
37 * This class is the root view object of the page. It owns all the other
38 * views, and manages switching between them. It is also responsible for
39 * initializing the views and the BrowserBridge.
41 var MainView
= (function() {
44 // We inherit from WindowView
45 var superClass
= WindowView
;
48 * Main entry point. Called once the page has loaded.
52 assertFirstConstructorCall(MainView
);
55 document
.body
.classList
.add('touch');
57 // This must be initialized before the tabs, so they can register as
59 g_browser
= BrowserBridge
.getInstance();
61 // This must be the first constants observer, so other constants observers
62 // can safely use the globals, rather than depending on walking through
63 // the constants themselves.
64 g_browser
.addConstantsObserver(new ConstantsObserver());
66 // Create the tab switcher.
69 // Cut out a small vertical strip at the top of the window, to display
70 // a high level status (i.e. if we are capturing events, or displaying a
71 // log file). Below it we will position the main tabs and their content
73 this.topBarView_
= TopBarView
.getInstance(this);
74 var verticalSplitView
= new VerticalSplitView(
75 this.topBarView_
, this.tabSwitcher_
);
77 superClass
.call(this, verticalSplitView
);
79 // Trigger initial layout.
82 window
.onhashchange
= this.onUrlHashChange_
.bind(this);
84 // Select the initial view based on the current URL.
85 window
.onhashchange();
87 // Tell the browser that we are ready to start receiving log events.
88 this.topBarView_
.switchToSubView('capture');
89 g_browser
.sendReady();
92 cr
.addSingletonGetter(MainView
);
94 // Tracks if we're viewing a loaded log file, so views can behave
95 // appropriately. Global so safe to call during construction.
96 var isViewingLoadedLog
= false;
98 MainView
.isViewingLoadedLog = function() {
99 return isViewingLoadedLog
;
102 MainView
.prototype = {
103 // Inherit the superclass's methods.
104 __proto__
: superClass
.prototype,
106 // This is exposed both so the log import/export code can enumerate all the
107 // tabs, and for testing.
108 tabSwitcher: function() {
109 return this.tabSwitcher_
;
113 * Prevents receiving/sending events to/from the browser, so loaded data
114 * will not be mixed with current Chrome state. Also hides any interactive
115 * HTML elements that send messages to the browser. Cannot be undone
116 * without reloading the page. Must be called before passing loaded data
117 * to the individual views.
119 * @param {string} opt_fileName The name of the log file that has been
120 * loaded, if we're loading a log file.
122 onLoadLog: function(opt_fileName
) {
123 isViewingLoadedLog
= true;
125 this.stopCapturing();
126 if (opt_fileName
!= undefined) {
127 // If there's a file name, a log file was loaded, so swap out the status
128 // bar to indicate we're no longer capturing events. Also disable
129 // hiding cookies, so if the log dump has them, they'll be displayed.
130 this.topBarView_
.switchToSubView('loaded').setFileName(opt_fileName
);
131 $(ExportView
.PRIVACY_STRIPPING_CHECKBOX_ID
).checked
= false;
132 SourceTracker
.getInstance().setPrivacyStripping(false);
134 // Otherwise, the "Stop Capturing" button was presumably pressed.
135 // Don't disable hiding cookies, so created log dumps won't have them,
136 // unless the user toggles the option.
137 this.topBarView_
.switchToSubView('halted');
141 switchToViewOnlyMode: function() {
142 // Since this won't be dumped to a file, we don't want to remove
143 // cookies and credentials.
144 log_util
.createLogDumpAsync('', log_util
.loadLogFile
, false);
147 stopCapturing: function() {
149 document
.styleSheets
[0].insertRule(
150 '.hide-when-not-capturing { display: none; }', 0);
153 initTabs_: function() {
154 this.tabIdToHash_
= {};
155 this.hashToTabId_
= {};
157 this.tabSwitcher_
= new TabSwitcherView(
158 $(TopBarView
.TAB_DROPDOWN_MENU_ID
),
159 this.onTabSwitched_
.bind(this));
161 // Helper function to add a tab given the class for a view singleton.
162 var addTab = function(viewClass
) {
163 var tabId
= viewClass
.TAB_ID
;
164 var tabHash
= viewClass
.TAB_HASH
;
165 var tabName
= viewClass
.TAB_NAME
;
166 var view
= viewClass
.getInstance();
168 if (!tabId
|| !view
|| !tabHash
|| !tabName
) {
169 throw Error('Invalid view class for tab');
172 if (tabHash
.charAt(0) != '#') {
173 throw Error('Tab hashes must start with a #');
176 this.tabSwitcher_
.addTab(tabId
, view
, tabName
);
177 this.tabIdToHash_
[tabId
] = tabHash
;
178 this.hashToTabId_
[tabHash
] = tabId
;
181 // Populate the main tabs. Even tabs that don't contain information for
182 // the running OS should be created, so they can load log dumps from other
189 addTab(WaterfallView
);
190 addTab(TimelineView
);
195 addTab(HttpCacheView
);
198 addTab(CrosLogVisualizerView
);
201 addTab(BandwidthView
);
202 addTab(PrerenderView
);
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
,
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.
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
);
237 if (!parsed
.tabHash
) {
238 // Default to the export tab.
239 parsed
.tabHash
= ExportView
.TAB_HASH
;
242 var tabId
= this.hashToTabId_
[parsed
.tabHash
];
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¶m1=value1¶m2=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
== '#') {
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)
275 if (paramDict
== null)
277 var key
= decodeURIComponent(paramStrings
[0]);
278 var value
= decodeURIComponent(paramStrings
[1]);
279 paramDict
[key
] = value
;
282 return {tabHash
: tabHash
, parameters
: paramDict
};
288 function ConstantsObserver() {}
291 * Loads all constants from |constants|. On failure, global dictionaries are
293 * @param {Object} receivedConstants The map of received constants.
295 ConstantsObserver
.prototype.onReceivedConstants = function(receivedConstants
) {
296 if (!areValidConstants(receivedConstants
))
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
;
314 // certStatusFlag may not be present when loading old log Files
315 if (typeof(Constants
.certStatusFlag
) == 'object')
316 CertStatusFlag
= Constants
.certStatusFlag
;
320 timeutil
.setTimeTickOffset(Constants
.timeTickOffset
);
324 * Returns true if it's given a valid-looking constants object.
325 * @param {Object} receivedConstants The received map of constants.
326 * @return {boolean} True if the |receivedConstants| object appears valid.
328 function areValidConstants(receivedConstants
) {
329 return typeof(receivedConstants
) == 'object' &&
330 typeof(receivedConstants
.logEventTypes
) == 'object' &&
331 typeof(receivedConstants
.clientInfo
) == 'object' &&
332 typeof(receivedConstants
.logEventPhase
) == 'object' &&
333 typeof(receivedConstants
.logSourceType
) == 'object' &&
334 typeof(receivedConstants
.logLevelType
) == 'object' &&
335 typeof(receivedConstants
.loadFlag
) == 'object' &&
336 typeof(receivedConstants
.netError
) == 'object' &&
337 typeof(receivedConstants
.addressFamily
) == 'object' &&
338 typeof(receivedConstants
.timeTickOffset
) == 'string' &&
339 typeof(receivedConstants
.logFormatVersion
) == 'number';
343 * Returns the name for netError.
345 * Example: netErrorToString(-105) should return
346 * "ERR_NAME_NOT_RESOLVED".
347 * @param {number} netError The net error code.
348 * @return {string} The name of the given error.
350 function netErrorToString(netError
) {
351 return getKeyWithValue(NetError
, netError
);
355 * Returns the name for quicError.
357 * Example: quicErrorToString(25) should return
359 * @param {number} quicError The QUIC error code.
360 * @return {string} The name of the given error.
362 function quicErrorToString(quicError
) {
363 return getKeyWithValue(QuicError
, quicError
);
367 * Returns the name for quicRstStreamError.
369 * Example: quicRstStreamErrorToString(3) should return
370 * "BAD_APPLICATION_PAYLOAD".
371 * @param {number} quicRstStreamError The QUIC RST_STREAM error code.
372 * @return {string} The name of the given error.
374 function quicRstStreamErrorToString(quicRstStreamError
) {
375 return getKeyWithValue(QuicRstStreamError
, quicRstStreamError
);
379 * Returns a string representation of |family|.
380 * @param {number} family An AddressFamily
381 * @return {string} A representation of the given family.
383 function addressFamilyToString(family
) {
384 var str
= getKeyWithValue(AddressFamily
, family
);
385 // All the address family start with ADDRESS_FAMILY_*.
386 // Strip that prefix since it is redundant and only clutters the output.
387 return str
.replace(/^ADDRESS_FAMILY_/, '');