• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
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 LogLevelType = null;
16var ClientInfo = null;
17var NetError = null;
18var QuicError = null;
19var QuicRstStreamError = null;
20var LoadFlag = null;
21var CertStatusFlag = null;
22var LoadState = null;
23var AddressFamily = null;
24
25/**
26 * Dictionary of all constants, used for saving log files.
27 */
28var Constants = null;
29
30/**
31 * Object to communicate between the renderer and the browser.
32 * @type {!BrowserBridge}
33 */
34var g_browser = null;
35
36/**
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.
40 */
41var MainView = (function() {
42  'use strict';
43
44  // We inherit from WindowView
45  var superClass = WindowView;
46
47  /**
48   * Main entry point. Called once the page has loaded.
49   *  @constructor
50   */
51  function MainView() {
52    assertFirstConstructorCall(MainView);
53
54    if (hasTouchScreen())
55      document.body.classList.add('touch');
56
57    // This must be initialized before the tabs, so they can register as
58    // observers.
59    g_browser = BrowserBridge.getInstance();
60
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());
65
66    // Create the tab switcher.
67    this.initTabs_();
68
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
72    // area.
73    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    // Tell the browser that we are ready to start receiving log events.
88    this.topBarView_.switchToSubView('capture');
89    g_browser.sendReady();
90  }
91
92  cr.addSingletonGetter(MainView);
93
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;
97
98  MainView.isViewingLoadedLog = function() {
99    return isViewingLoadedLog;
100  };
101
102  MainView.prototype = {
103    // Inherit the superclass's methods.
104    __proto__: superClass.prototype,
105
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_;
110    },
111
112    /**
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.
118     *
119     * @param {string} opt_fileName The name of the log file that has been
120     *     loaded, if we're loading a log file.
121     */
122    onLoadLog: function(opt_fileName) {
123      isViewingLoadedLog = true;
124
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);
133      } else {
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');
138      }
139    },
140
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);
145    },
146
147    stopCapturing: function() {
148      g_browser.disable();
149      document.styleSheets[0].insertRule(
150          '.hide-when-not-capturing { display: none; }', 0);
151    },
152
153    initTabs_: function() {
154      this.tabIdToHash_ = {};
155      this.hashToTabId_ = {};
156
157      this.tabSwitcher_ = new TabSwitcherView(
158          $(TopBarView.TAB_DROPDOWN_MENU_ID),
159          this.onTabSwitched_.bind(this));
160
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();
167
168        if (!tabId || !view || !tabHash || !tabName) {
169          throw Error('Invalid view class for tab');
170        }
171
172        if (tabHash.charAt(0) != '#') {
173          throw Error('Tab hashes must start with a #');
174        }
175
176        this.tabSwitcher_.addTab(tabId, view, tabName);
177        this.tabIdToHash_[tabId] = tabHash;
178        this.hashToTabId_[tabHash] = tabId;
179      }.bind(this);
180
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
183      // OSes.
184      addTab(CaptureView);
185      addTab(ExportView);
186      addTab(ImportView);
187      addTab(ProxyView);
188      addTab(EventsView);
189      addTab(WaterfallView);
190      addTab(TimelineView);
191      addTab(DnsView);
192      addTab(SocketsView);
193      addTab(SpdyView);
194      addTab(QuicView);
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);
204
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);
209    },
210
211    /**
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.
215     */
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();
221
222      // Change the URL to match the new tab.
223
224      var newTabHash = this.tabIdToHash_[newTabId];
225      var parsed = parseUrlHash_(window.location.hash);
226      if (parsed.tabHash != newTabHash) {
227        window.location.hash = newTabHash;
228      }
229    },
230
231    onUrlHashChange_: function() {
232      var parsed = parseUrlHash_(window.location.hash);
233
234      if (!parsed)
235        return;
236
237      if (!parsed.tabHash) {
238        // Default to the export tab.
239        parsed.tabHash = ExportView.TAB_HASH;
240      }
241
242      var tabId = this.hashToTabId_[parsed.tabHash];
243
244      if (tabId) {
245        this.tabSwitcher_.switchToTab(tabId);
246        if (parsed.parameters) {
247          var view = this.tabSwitcher_.getTabView(tabId);
248          view.setParameters(parsed.parameters);
249        }
250      }
251    },
252
253  };
254
255  /**
256   * Takes the current hash in form of "#tab&param1=value1&param2=value2&..."
257   * and parses it into a dictionary.
258   *
259   * Parameters and values are decoded with decodeURIComponent().
260   */
261  function parseUrlHash_(hash) {
262    var parameters = hash.split('&');
263
264    var tabHash = parameters[0];
265    if (tabHash == '' || tabHash == '#') {
266      tabHash = undefined;
267    }
268
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;
280    }
281
282    return {tabHash: tabHash, parameters: paramDict};
283  }
284
285  return MainView;
286})();
287
288function ConstantsObserver() {}
289
290/**
291 * Loads all constants from |constants|.  On failure, global dictionaries are
292 * not modifed.
293 * @param {Object} receivedConstants The map of received constants.
294 */
295ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) {
296  if (!areValidConstants(receivedConstants))
297    return;
298
299  Constants = receivedConstants;
300
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;
317  else
318    CertStatusFlag = {};
319
320  timeutil.setTimeTickOffset(Constants.timeTickOffset);
321};
322
323/**
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.
327 */
328function 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';
340}
341
342/**
343 * Returns the name for netError.
344 *
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.
349 */
350function netErrorToString(netError) {
351  var str = getKeyWithValue(NetError, netError);
352  if (str == '?')
353    return str;
354  return 'ERR_' + str;
355}
356
357/**
358 * Returns the name for quicError.
359 *
360 * Example: quicErrorToString(25) should return
361 * "TIMED_OUT".
362 * @param {number} quicError The QUIC error code.
363 * @return {string} The name of the given error.
364 */
365function quicErrorToString(quicError) {
366  return getKeyWithValue(QuicError, quicError);
367}
368
369/**
370 * Returns the name for quicRstStreamError.
371 *
372 * Example: quicRstStreamErrorToString(3) should return
373 * "BAD_APPLICATION_PAYLOAD".
374 * @param {number} quicRstStreamError The QUIC RST_STREAM error code.
375 * @return {string} The name of the given error.
376 */
377function quicRstStreamErrorToString(quicRstStreamError) {
378  return getKeyWithValue(QuicRstStreamError, quicRstStreamError);
379}
380
381/**
382 * Returns a string representation of |family|.
383 * @param {number} family An AddressFamily
384 * @return {string} A representation of the given family.
385 */
386function addressFamilyToString(family) {
387  var str = getKeyWithValue(AddressFamily, family);
388  // All the address family start with ADDRESS_FAMILY_*.
389  // Strip that prefix since it is redundant and only clutters the output.
390  return str.replace(/^ADDRESS_FAMILY_/, '');
391}
392