blob: 2aeb39e509ad94ec121a760b32d0b3b824595e2e [file] [log] [blame]
rayraymond25243712016-07-15 08:02:51 -07001// 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.
4
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 */
10var EventType = null;
11var EventTypeNames = null;
12var EventPhase = null;
13var EventSourceType = null;
14var EventSourceTypeNames = null;
15var ClientInfo = null;
16var NetError = null;
17var QuicError = null;
18var QuicRstStreamError = null;
19var LoadFlag = null;
20var CertStatusFlag = null;
21var LoadState = null;
22var AddressFamily = null;
23var SdchProblemCode = null;
24var DataReductionProxyBypassEventType = null;
25
26/**
27 * Dictionary of all constants, used for saving log files.
28 */
29var Constants = null;
30
31/**
32 * Object to communicate between the renderer and the browser.
33 * @type {!BrowserBridge}
34 */
35var g_browser = null;
36
37/**
38 * This class is the root view object of the page. It owns all the other
39 * views, and manages switching between them. It is also responsible for
40 * initializing the views and the BrowserBridge.
41 */
42var MainView = (function() {
43 'use strict';
44
45 // We inherit from WindowView
46 var superClass = WindowView;
47
48 /**
49 * Main entry point. Called once the page has loaded.
50 * @constructor
51 */
52 function MainView() {
53 assertFirstConstructorCall(MainView);
54
55 if (hasTouchScreen())
56 document.body.classList.add('touch');
57
58 // This must be initialized before the tabs, so they can register as
59 // observers.
60 g_browser = BrowserBridge.getInstance();
61
62 // This must be the first constants observer, so other constants observers
63 // can safely use the globals, rather than depending on walking through
64 // the constants themselves.
65 g_browser.addConstantsObserver(new ConstantsObserver());
66
67 // Create the tab switcher.
68 this.initTabs_();
69
70 // Cut out a small vertical strip at the top of the window, to display
rayraymond7965c152016-07-26 10:44:54 -070071 // a high level status (i.e. if we are displaying a log file or not).
72 // Below it we will position the main tabs and their content area.
rayraymond25243712016-07-15 08:02:51 -070073 this.topBarView_ = TopBarView.getInstance(this);
74 var verticalSplitView = new VerticalSplitView(
75 this.topBarView_, this.tabSwitcher_);
76
77 superClass.call(this, verticalSplitView);
78
79 // Trigger initial layout.
80 this.resetGeometry();
81
82 window.onhashchange = this.onUrlHashChange_.bind(this);
83
84 // Select the initial view based on the current URL.
85 window.onhashchange();
86
87 // No log file loaded yet so set the status bar to that state.
88 this.topBarView_.switchToSubView('loaded').setFileName(
89 'No log to display.');
90
91 // TODO(rayraymond): Follow-up is to completely remove all code from
92 // g_browser that interacts with sending/receiving messages from
93 // browser.
94 g_browser.disable();
95 }
96
97 cr.addSingletonGetter(MainView);
98
99 // Tracks if we're viewing a loaded log file, so views can behave
100 // appropriately. Global so safe to call during construction.
101 var isViewingLoadedLog = false;
102
103 MainView.isViewingLoadedLog = function() {
104 return isViewingLoadedLog;
105 };
106
107 MainView.prototype = {
108 // Inherit the superclass's methods.
109 __proto__: superClass.prototype,
110
111 // This is exposed both so the log import/export code can enumerate all the
112 // tabs, and for testing.
113 tabSwitcher: function() {
114 return this.tabSwitcher_;
115 },
116
117 /**
118 * Prevents receiving/sending events to/from the browser, so loaded data
119 * will not be mixed with current Chrome state. Also hides any interactive
120 * HTML elements that send messages to the browser. Cannot be undone
121 * without reloading the page. Must be called before passing loaded data
122 * to the individual views.
123 *
124 * @param {string} opt_fileName The name of the log file that has been
125 * loaded, if we're loading a log file.
126 */
127 onLoadLog: function(opt_fileName) {
128 isViewingLoadedLog = true;
129
rayraymond25243712016-07-15 08:02:51 -0700130 if (opt_fileName != undefined) {
rayraymond7965c152016-07-26 10:44:54 -0700131 // If there's a file name, a log file was loaded, so display the
132 // file's name in the status bar.
rayraymond25243712016-07-15 08:02:51 -0700133 this.topBarView_.switchToSubView('loaded').setFileName(opt_fileName);
134 SourceTracker.getInstance().setPrivacyStripping(false);
135 }
136 },
137
138 switchToViewOnlyMode: function() {
139 // Since this won't be dumped to a file, we don't want to remove
140 // cookies and credentials.
141 log_util.createLogDumpAsync('', log_util.loadLogFile, false);
142 },
143
rayraymond25243712016-07-15 08:02:51 -0700144 initTabs_: function() {
145 this.tabIdToHash_ = {};
146 this.hashToTabId_ = {};
147
148 this.tabSwitcher_ = new TabSwitcherView(this.onTabSwitched_.bind(this));
149
150 // Helper function to add a tab given the class for a view singleton.
151 var addTab = function(viewClass) {
152 var tabId = viewClass.TAB_ID;
153 var tabHash = viewClass.TAB_HASH;
154 var tabName = viewClass.TAB_NAME;
155 var view = viewClass.getInstance();
156
157 if (!tabId || !view || !tabHash || !tabName) {
158 throw Error('Invalid view class for tab');
159 }
160
161 if (tabHash.charAt(0) != '#') {
162 throw Error('Tab hashes must start with a #');
163 }
164
165 this.tabSwitcher_.addTab(tabId, view, tabName, tabHash);
166 this.tabIdToHash_[tabId] = tabHash;
167 this.hashToTabId_[tabHash] = tabId;
168 }.bind(this);
169
170 // Populate the main tabs. Even tabs that don't contain information for
171 // the running OS should be created, so they can load log dumps from other
172 // OSes.
173 addTab(ImportView);
174 addTab(ProxyView);
175 addTab(EventsView);
176 addTab(TimelineView);
177 addTab(DnsView);
178 addTab(SocketsView);
179 addTab(AltSvcView);
180 addTab(SpdyView);
181 addTab(QuicView);
182 addTab(SdchView);
183 addTab(HttpCacheView);
rayraymond7965c152016-07-26 10:44:54 -0700184 addTab(ModulesView);
185 addTab(BandwidthView);
186 addTab(PrerenderView);
rayraymond25243712016-07-15 08:02:51 -0700187 addTab(CrosView);
188
rayraymond7965c152016-07-26 10:44:54 -0700189 // Tab links start off hidden (besides import) since a log file has not
190 // been loaded yet. This must be done after all the tabs are added so
191 // that the width of the tab-list div is correctly styled.
192 for (var tabId in this.tabSwitcher_.getAllTabViews()) {
193 if (tabId != ImportView.TAB_ID) {
194 this.tabSwitcher_.showTabLink(tabId, false);
195 }
196 }
197
rayraymond25243712016-07-15 08:02:51 -0700198 this.tabSwitcher_.showTabLink(CrosView.TAB_ID, cr.isChromeOS);
199 },
200
201 /**
202 * This function is called by the tab switcher when the current tab has been
203 * changed. It will update the current URL to reflect the new active tab,
204 * so the back can be used to return to previous view.
205 */
206 onTabSwitched_: function(oldTabId, newTabId) {
207 // Update data needed by newly active tab, as it may be
208 // significantly out of date.
209 if (g_browser)
210 g_browser.checkForUpdatedInfo();
211
212 // Change the URL to match the new tab.
213
214 var newTabHash = this.tabIdToHash_[newTabId];
215 var parsed = parseUrlHash_(window.location.hash);
216 if (parsed.tabHash != newTabHash) {
217 window.location.hash = newTabHash;
218 }
219 },
220
221 onUrlHashChange_: function() {
222 var parsed = parseUrlHash_(window.location.hash);
223
224 if (!parsed)
225 return;
226
227 if (!parsed.tabHash) {
228 // Default to the import tab.
229 parsed.tabHash = ImportView.TAB_HASH;
230 }
231
232 var tabId = this.hashToTabId_[parsed.tabHash];
233
234 if (tabId) {
235 this.tabSwitcher_.switchToTab(tabId);
236 if (parsed.parameters) {
237 var view = this.tabSwitcher_.getTabView(tabId);
238 view.setParameters(parsed.parameters);
239 }
240 }
241 },
242
243 };
244
245 /**
246 * Takes the current hash in form of "#tab&param1=value1&param2=value2&..."
247 * and parses it into a dictionary.
248 *
249 * Parameters and values are decoded with decodeURIComponent().
250 */
251 function parseUrlHash_(hash) {
252 var parameters = hash.split('&');
253
254 var tabHash = parameters[0];
255 if (tabHash == '' || tabHash == '#') {
256 tabHash = undefined;
257 }
258
259 // Split each string except the first around the '='.
260 var paramDict = null;
261 for (var i = 1; i < parameters.length; i++) {
262 var paramStrings = parameters[i].split('=');
263 if (paramStrings.length != 2)
264 continue;
265 if (paramDict == null)
266 paramDict = {};
267 var key = decodeURIComponent(paramStrings[0]);
268 var value = decodeURIComponent(paramStrings[1]);
269 paramDict[key] = value;
270 }
271
272 return {tabHash: tabHash, parameters: paramDict};
273 }
274
275 return MainView;
276})();
277
278function ConstantsObserver() {}
279
280/**
281 * Loads all constants from |constants|. On failure, global dictionaries are
282 * not modifed.
283 * @param {Object} receivedConstants The map of received constants.
284 */
285ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) {
286 if (!areValidConstants(receivedConstants))
287 return;
288
289 Constants = receivedConstants;
290
291 EventType = Constants.logEventTypes;
292 EventTypeNames = makeInverseMap(EventType);
293 EventPhase = Constants.logEventPhase;
294 EventSourceType = Constants.logSourceType;
295 EventSourceTypeNames = makeInverseMap(EventSourceType);
296 ClientInfo = Constants.clientInfo;
297 LoadFlag = Constants.loadFlag;
298 NetError = Constants.netError;
299 QuicError = Constants.quicError;
300 QuicRstStreamError = Constants.quicRstStreamError;
301 AddressFamily = Constants.addressFamily;
302 LoadState = Constants.loadState;
303 SdchProblemCode = Constants.sdchProblemCode;
304 DataReductionProxyBypassEventType =
305 Constants.dataReductionProxyBypassEventType;
306 DataReductionProxyBypassActionType =
307 Constants.dataReductionProxyBypassActionType;
308 // certStatusFlag may not be present when loading old log Files
309 if (typeof(Constants.certStatusFlag) == 'object')
310 CertStatusFlag = Constants.certStatusFlag;
311 else
312 CertStatusFlag = {};
313
314 timeutil.setTimeTickOffset(Constants.timeTickOffset);
315};
316
317/**
318 * Returns true if it's given a valid-looking constants object.
319 * @param {Object} receivedConstants The received map of constants.
320 * @return {boolean} True if the |receivedConstants| object appears valid.
321 */
322function areValidConstants(receivedConstants) {
323 return typeof(receivedConstants) == 'object' &&
324 typeof(receivedConstants.logEventTypes) == 'object' &&
325 typeof(receivedConstants.clientInfo) == 'object' &&
326 typeof(receivedConstants.logEventPhase) == 'object' &&
327 typeof(receivedConstants.logSourceType) == 'object' &&
328 typeof(receivedConstants.loadFlag) == 'object' &&
329 typeof(receivedConstants.netError) == 'object' &&
330 typeof(receivedConstants.addressFamily) == 'object' &&
331 typeof(receivedConstants.timeTickOffset) == 'string' &&
332 typeof(receivedConstants.logFormatVersion) == 'number';
333}
334
335/**
336 * Returns the name for netError.
337 *
338 * Example: netErrorToString(-105) should return
339 * "ERR_NAME_NOT_RESOLVED".
340 * @param {number} netError The net error code.
341 * @return {string} The name of the given error.
342 */
343function netErrorToString(netError) {
344 return getKeyWithValue(NetError, netError);
345}
346
347/**
348 * Returns the name for quicError.
349 *
350 * Example: quicErrorToString(25) should return
351 * "TIMED_OUT".
352 * @param {number} quicError The QUIC error code.
353 * @return {string} The name of the given error.
354 */
355function quicErrorToString(quicError) {
356 return getKeyWithValue(QuicError, quicError);
357}
358
359/**
360 * Returns the name for quicRstStreamError.
361 *
362 * Example: quicRstStreamErrorToString(3) should return
363 * "BAD_APPLICATION_PAYLOAD".
364 * @param {number} quicRstStreamError The QUIC RST_STREAM error code.
365 * @return {string} The name of the given error.
366 */
367function quicRstStreamErrorToString(quicRstStreamError) {
368 return getKeyWithValue(QuicRstStreamError, quicRstStreamError);
369}
370
371/**
372 * Returns a string representation of |family|.
373 * @param {number} family An AddressFamily
374 * @return {string} A representation of the given family.
375 */
376function addressFamilyToString(family) {
377 var str = getKeyWithValue(AddressFamily, family);
378 // All the address family start with ADDRESS_FAMILY_*.
379 // Strip that prefix since it is redundant and only clutters the output.
380 return str.replace(/^ADDRESS_FAMILY_/, '');
381}
382
383/**
384 * Returns the name for sdchProblemCode.
385 *
386 * Example: sdchProblemCodeToString(5) should return
387 * "DECODE_BODY_ERROR".
388 * @param {number} sdchProblemCode The SDCH problem code.
389 * @return {string} The name of the given problem code.
390 */
391function sdchProblemCodeToString(sdchProblemCode) {
392 return getKeyWithValue(SdchProblemCode, sdchProblemCode);
393}
394