diff options
Diffstat (limited to 'chrome/browser/resources/net_internals/main.js')
-rw-r--r-- | chrome/browser/resources/net_internals/main.js | 1065 |
1 files changed, 262 insertions, 803 deletions
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js index c53bf2e..4d7c5d9 100644 --- a/chrome/browser/resources/net_internals/main.js +++ b/chrome/browser/resources/net_internals/main.js @@ -14,9 +14,6 @@ var NetError = null; var LoadFlag = null; var AddressFamily = null; -// Dictionary of all constants, used for saving log files. -var Constants = null; - /** * Object to communicate between the renderer and the browser. * @type {!BrowserBridge} @@ -24,14 +21,42 @@ var Constants = null; var g_browser = null; /** + * The browser gives us times in terms of "time ticks" in milliseconds. + * This function converts the tick count to a Date() object. + * + * @param {String} timeTicks. + * @returns {Date} The time that |timeTicks| represents. + */ +var convertTimeTicksToDate; + +/** + * Called to create a new log dump. Must not be called once a dump has been + * loaded. Once a log dump has been created, |callback| is passed the dumped + * text as a string. + */ +var createLogDumpAsync; + +/** + * Loads a log dump from the string |logFileContents|, which can be either a + * full net-internals dump, or a NetLog dump only. Returns a string containing + * a log of the load. + */ +var loadLogFile; + +// Start of annonymous namespace. +(function() { + +var categoryTabSwitcher; + +/** * Main entry point. called once the page has loaded. */ -function onLoaded() { +onLoaded = function() { g_browser = new BrowserBridge(); // Create a view which lets you tab between the different sub-views. // This view is a left (resizable) navigation bar. - var categoryTabSwitcher = new TabSwitcherView(); + categoryTabSwitcher = new TabSwitcherView(); var tabSwitcherSplitView = new ResizableVerticalSplitView( new DivView('categoryTabHandles'), categoryTabSwitcher, @@ -43,8 +68,6 @@ function onLoaded() { // name rather than using a fixed width. tabSwitcherSplitView.setLeftSplit(150); - g_browser.setTabSwitcher(categoryTabSwitcher); - // Populate the main tabs. Even tabs that don't contain information for the // running OS should be created, so they can load log dumps from other // OSes. @@ -56,13 +79,13 @@ function onLoaded() { categoryTabSwitcher.addTab('httpCacheTab', new HttpCacheView(), false, true); categoryTabSwitcher.addTab('dataTab', new DataView(), false, true); categoryTabSwitcher.addTab('serviceProvidersTab', new ServiceProvidersView(), - false, g_browser.isPlatformWindows()); + false, cr.isWindows); categoryTabSwitcher.addTab('testTab', new TestView(), false, true); categoryTabSwitcher.addTab('hstsTab', new HSTSView(), false, true); categoryTabSwitcher.addTab('httpThrottlingTab', new HttpThrottlingView(), false, true); categoryTabSwitcher.addTab('logsTab', new LogsView(), false, - g_browser.isChromeOS()); + cr.isChromeOS); categoryTabSwitcher.addTab('prerenderTab', new PrerenderView(), false, true); // Build a map from the anchor name of each tab handle to its "tab ID". @@ -76,8 +99,7 @@ function onLoaded() { // Default the empty hash to the data tab. anchorMap['#'] = anchorMap[''] = 'dataTab'; - window.onhashchange = onUrlHashChange.bind(null, anchorMap, - categoryTabSwitcher); + window.onhashchange = onUrlHashChange.bind(null, anchorMap); // Cut out a small vertical strip at the top of the window, to display // a high level status (i.e. if we are capturing events, or displaying a @@ -94,75 +116,11 @@ function onLoaded() { // Select the initial view based on the current URL. window.onhashchange(); + g_browser.addConstantsObserver(new ConstantsObserver()); + // Tell the browser that we are ready to start receiving log events. g_browser.sendReady(); -} - -/** - * This class provides a "bridge" for communicating between the javascript and - * the browser. - * - * @constructor - */ -function BrowserBridge() { - // List of observers for various bits of browser state. - this.logObservers_ = []; - this.connectionTestsObservers_ = []; - this.hstsObservers_ = []; - this.httpThrottlingObservers_ = []; - - // This is set to true when a log file is being viewed to block all - // communication with the browser. - this.isViewingLogFile_ = false; - - this.pollableDataHelpers_ = {}; - this.pollableDataHelpers_.proxySettings = - new PollableDataHelper('onProxySettingsChanged', - this.sendGetProxySettings.bind(this)); - this.pollableDataHelpers_.badProxies = - new PollableDataHelper('onBadProxiesChanged', - this.sendGetBadProxies.bind(this)); - this.pollableDataHelpers_.httpCacheInfo = - new PollableDataHelper('onHttpCacheInfoChanged', - this.sendGetHttpCacheInfo.bind(this)); - this.pollableDataHelpers_.hostResolverInfo = - new PollableDataHelper('onHostResolverInfoChanged', - this.sendGetHostResolverInfo.bind(this)); - this.pollableDataHelpers_.socketPoolInfo = - new PollableDataHelper('onSocketPoolInfoChanged', - this.sendGetSocketPoolInfo.bind(this)); - this.pollableDataHelpers_.spdySessionInfo = - new PollableDataHelper('onSpdySessionInfoChanged', - this.sendGetSpdySessionInfo.bind(this)); - this.pollableDataHelpers_.spdyStatus = - new PollableDataHelper('onSpdyStatusChanged', - this.sendGetSpdyStatus.bind(this)); - this.pollableDataHelpers_.spdyAlternateProtocolMappings = - new PollableDataHelper('onSpdyAlternateProtocolMappingsChanged', - this.sendGetSpdyAlternateProtocolMappings.bind( - this)); - if (this.isPlatformWindows()) { - this.pollableDataHelpers_.serviceProviders = - new PollableDataHelper('onServiceProvidersChanged', - this.sendGetServiceProviders.bind(this)); - } - this.pollableDataHelpers_.prerenderInfo = - new PollableDataHelper('onPrerenderInfoChanged', - this.sendGetPrerenderInfo.bind(this)); - - // Cache of the data received. - this.numPassivelyCapturedEvents_ = 0; - this.capturedEvents_ = []; - - // Next unique id to be assigned to a log entry without a source. - // Needed to simplify deletion, identify associated GUI elements, etc. - this.nextSourcelessEventId_ = -1; - - // True when cookies and authentication information should be removed from - // displayed events. When true, such information should be hidden from - // all pages. - this.enableSecurityStripping_ = true; -} +}; /* * Takes the current hash in form of "#tab¶m1=value1¶m2=value2&...". @@ -172,7 +130,7 @@ function BrowserBridge() { * * Parameters and values are decoded with decodeURIComponent(). */ -function onUrlHashChange(anchorMap, categoryTabSwitcher) { +function onUrlHashChange(anchorMap) { var parameters = window.location.hash.split('&'); var tabId = anchorMap[parameters[0]]; @@ -196,329 +154,6 @@ function onUrlHashChange(anchorMap, categoryTabSwitcher) { } /** - * Returns true if |constants| appears to be a valid constants object. - */ -BrowserBridge.prototype.areValidConstants = function(constants) { - return typeof(constants) == 'object' && - typeof(constants.logEventTypes) == 'object' && - typeof(constants.clientInfo) == 'object' && - typeof(constants.logEventPhase) == 'object' && - typeof(constants.logSourceType) == 'object' && - typeof(constants.logLevelType) == 'object' && - typeof(constants.loadFlag) == 'object' && - typeof(constants.netError) == 'object' && - typeof(constants.addressFamily) == 'object' && - typeof(constants.timeTickOffset) == 'string' && - typeof(constants.logFormatVersion) == 'number'; -}; - -/** - * Attempts to load all constants from |constants|. Returns false if one or - * more entries are missing. On failure, global dictionaries are not modified. - */ -BrowserBridge.prototype.loadConstants = function(constants) { - if (!this.areValidConstants(constants)) - return false; - - LogEventType = constants.logEventTypes; - ClientInfo = constants.clientInfo; - LogEventPhase = constants.logEventPhase; - LogSourceType = constants.logSourceType; - LogLevelType = constants.logLevelType; - LoadFlag = constants.loadFlag; - NetError = constants.netError; - AddressFamily = constants.addressFamily; - this.timeTickOffset_ = constants.timeTickOffset; - - // Used for saving dumps. - Constants = constants; - - return true; -}; - -/** - * Delay in milliseconds between updates of certain browser information. - */ -BrowserBridge.POLL_INTERVAL_MS = 5000; - -//------------------------------------------------------------------------------ -// Messages sent to the browser -//------------------------------------------------------------------------------ - -/** - * Wraps |chrome.send|. Doesn't send anything when viewing a log file. - */ -BrowserBridge.prototype.send = function(value1, value2) { - if (!this.isViewingLogFile_) { - if (arguments.length == 1) { - chrome.send(value1); - } else if (arguments.length == 2) { - chrome.send(value1, value2); - } else { - throw 'Unsupported number of arguments.'; - } - } -}; - -BrowserBridge.prototype.sendReady = function() { - this.send('notifyReady'); - - // Some of the data we are interested is not currently exposed as a stream, - // so we will poll the browser to find out when it changes and then notify - // the observers. - window.setInterval(this.checkForUpdatedInfo.bind(this, false), - BrowserBridge.POLL_INTERVAL_MS); -}; - -BrowserBridge.prototype.isPlatformWindows = function() { - return /Win/.test(navigator.platform); -}; - -BrowserBridge.prototype.isChromeOS = function() { - return /CrOS/.test(navigator.userAgent); -}; - -BrowserBridge.prototype.sendGetProxySettings = function() { - // The browser will call receivedProxySettings on completion. - this.send('getProxySettings'); -}; - -BrowserBridge.prototype.sendReloadProxySettings = function() { - this.send('reloadProxySettings'); -}; - -BrowserBridge.prototype.sendGetBadProxies = function() { - // The browser will call receivedBadProxies on completion. - this.send('getBadProxies'); -}; - -BrowserBridge.prototype.sendGetHostResolverInfo = function() { - // The browser will call receivedHostResolverInfo on completion. - this.send('getHostResolverInfo'); -}; - -BrowserBridge.prototype.sendClearBadProxies = function() { - this.send('clearBadProxies'); -}; - -BrowserBridge.prototype.sendClearHostResolverCache = function() { - this.send('clearHostResolverCache'); -}; - -BrowserBridge.prototype.sendStartConnectionTests = function(url) { - this.send('startConnectionTests', [url]); -}; - -BrowserBridge.prototype.sendHSTSQuery = function(domain) { - this.send('hstsQuery', [domain]); -}; - -BrowserBridge.prototype.sendHSTSAdd = function(domain, - include_subdomains, - pins) { - this.send('hstsAdd', [domain, include_subdomains, pins]); -}; - -BrowserBridge.prototype.sendHSTSDelete = function(domain) { - this.send('hstsDelete', [domain]); -}; - -BrowserBridge.prototype.sendGetHttpCacheInfo = function() { - this.send('getHttpCacheInfo'); -}; - -BrowserBridge.prototype.sendGetSocketPoolInfo = function() { - this.send('getSocketPoolInfo'); -}; - -BrowserBridge.prototype.sendCloseIdleSockets = function() { - this.send('closeIdleSockets'); -}; - -BrowserBridge.prototype.sendFlushSocketPools = function() { - this.send('flushSocketPools'); -}; - -BrowserBridge.prototype.sendGetSpdySessionInfo = function() { - this.send('getSpdySessionInfo'); -}; - -BrowserBridge.prototype.sendGetSpdyStatus = function() { - this.send('getSpdyStatus'); -}; - -BrowserBridge.prototype.sendGetSpdyAlternateProtocolMappings = function() { - this.send('getSpdyAlternateProtocolMappings'); -}; - -BrowserBridge.prototype.sendGetServiceProviders = function() { - this.send('getServiceProviders'); -}; - -BrowserBridge.prototype.sendGetPrerenderInfo = function() { - this.send('getPrerenderInfo'); -}; - -BrowserBridge.prototype.enableIPv6 = function() { - this.send('enableIPv6'); -}; - -BrowserBridge.prototype.setLogLevel = function(logLevel) { - this.send('setLogLevel', ['' + logLevel]); -}; - -BrowserBridge.prototype.enableHttpThrottling = function(enable) { - this.send('enableHttpThrottling', [enable]); -}; - -BrowserBridge.prototype.refreshSystemLogs = function() { - this.send('refreshSystemLogs'); -}; - -BrowserBridge.prototype.getSystemLog = function(log_key, cellId) { - this.send('getSystemLog', [log_key, cellId]); -}; - -//------------------------------------------------------------------------------ -// Messages received from the browser. -//------------------------------------------------------------------------------ - -BrowserBridge.prototype.receive = function(command, params) { - // Does nothing if viewing a log file. - if (this.isViewingLogFile_) - return; - this[command](params); -}; - -BrowserBridge.prototype.receivedConstants = function(constants) { - this.logFormatVersion_ = constants.logFormatVersion; - - this.loadConstants(constants); -}; - -BrowserBridge.prototype.receivedLogEntries = function(logEntries) { - this.addLogEntries(logEntries); -}; - -BrowserBridge.prototype.receivedProxySettings = function(proxySettings) { - this.pollableDataHelpers_.proxySettings.update(proxySettings); -}; - -BrowserBridge.prototype.receivedBadProxies = function(badProxies) { - this.pollableDataHelpers_.badProxies.update(badProxies); -}; - -BrowserBridge.prototype.receivedHostResolverInfo = -function(hostResolverInfo) { - this.pollableDataHelpers_.hostResolverInfo.update(hostResolverInfo); -}; - -BrowserBridge.prototype.receivedSocketPoolInfo = function(socketPoolInfo) { - this.pollableDataHelpers_.socketPoolInfo.update(socketPoolInfo); -}; - -BrowserBridge.prototype.receivedSpdySessionInfo = function(spdySessionInfo) { - this.pollableDataHelpers_.spdySessionInfo.update(spdySessionInfo); -}; - -BrowserBridge.prototype.receivedSpdyStatus = function(spdyStatus) { - this.pollableDataHelpers_.spdyStatus.update(spdyStatus); -}; - -BrowserBridge.prototype.receivedSpdyAlternateProtocolMappings = - function(spdyAlternateProtocolMappings) { - this.pollableDataHelpers_.spdyAlternateProtocolMappings.update( - spdyAlternateProtocolMappings); -}; - -BrowserBridge.prototype.receivedServiceProviders = function(serviceProviders) { - this.pollableDataHelpers_.serviceProviders.update(serviceProviders); -}; - -BrowserBridge.prototype.receivedPassiveLogEntries = function(entries) { - // Due to an expected race condition, it is possible to receive actively - // captured log entries before the passively logged entries are received. - // - // When that happens, we create a copy of the actively logged entries, delete - // all entries, and, after handling all the passively logged entries, add back - // the deleted actively logged entries. - var earlyActivelyCapturedEvents = this.capturedEvents_.slice(0); - if (earlyActivelyCapturedEvents.length > 0) - this.deleteAllEvents(); - - this.numPassivelyCapturedEvents_ = entries.length; - for (var i = 0; i < entries.length; ++i) - entries[i].wasPassivelyCaptured = true; - this.receivedLogEntries(entries); - - // Add back early actively captured events, if any. - if (earlyActivelyCapturedEvents.length) - this.receivedLogEntries(earlyActivelyCapturedEvents); -}; - - -BrowserBridge.prototype.receivedStartConnectionTestSuite = function() { - for (var i = 0; i < this.connectionTestsObservers_.length; ++i) - this.connectionTestsObservers_[i].onStartedConnectionTestSuite(); -}; - -BrowserBridge.prototype.receivedStartConnectionTestExperiment = function( - experiment) { - for (var i = 0; i < this.connectionTestsObservers_.length; ++i) { - this.connectionTestsObservers_[i].onStartedConnectionTestExperiment( - experiment); - } -}; - -BrowserBridge.prototype.receivedCompletedConnectionTestExperiment = -function(info) { - for (var i = 0; i < this.connectionTestsObservers_.length; ++i) { - this.connectionTestsObservers_[i].onCompletedConnectionTestExperiment( - info.experiment, info.result); - } -}; - -BrowserBridge.prototype.receivedCompletedConnectionTestSuite = function() { - for (var i = 0; i < this.connectionTestsObservers_.length; ++i) - this.connectionTestsObservers_[i].onCompletedConnectionTestSuite(); -}; - -BrowserBridge.prototype.receivedHSTSResult = function(info) { - for (var i = 0; i < this.hstsObservers_.length; ++i) - this.hstsObservers_[i].onHSTSQueryResult(info); -}; - -BrowserBridge.prototype.receivedHttpCacheInfo = function(info) { - this.pollableDataHelpers_.httpCacheInfo.update(info); -}; - -BrowserBridge.prototype.receivedHttpThrottlingEnabledPrefChanged = function( - enabled) { - for (var i = 0; i < this.httpThrottlingObservers_.length; ++i) { - this.httpThrottlingObservers_[i].onHttpThrottlingEnabledPrefChanged( - enabled); - } -}; - -BrowserBridge.prototype.receivedPrerenderInfo = function(prerenderInfo) { - this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo); -}; - -//------------------------------------------------------------------------------ - -BrowserBridge.prototype.categoryTabSwitcher = function() { - return this.categoryTabSwitcher_; -}; - -BrowserBridge.prototype.logFormatVersion = function() { - return this.logFormatVersion_; -}; - -BrowserBridge.prototype.isViewingLogFile = function() { - return this.isViewingLogFile_; -}; - -/** * Prevents receiving/sending events to/from the browser, so loaded data will * not be mixed with current Chrome state. Also hides any interactive HTML * elements that send messages to the browser. Cannot be undone without @@ -526,460 +161,284 @@ BrowserBridge.prototype.isViewingLogFile = function() { * * @param {String} fileName The name of the log file that has been loaded. */ -BrowserBridge.prototype.onLoadLogFile = function(fileName) { - if (!this.isViewingLogFile_) { - this.isViewingLogFile_ = true; - this.setSecurityStripping(false); - - // Swap out the status bar to indicate we have loaded from a file. - setNodeDisplay($('statusViewForCapture'), false); - setNodeDisplay($('statusViewForFile'), true); - - // Indicate which file is being displayed. - $('statusViewDumpFileName').innerText = fileName; - - document.styleSheets[0].insertRule('.hideOnLoadLog { display: none; }'); - } -}; - -/** - * Sets the |categoryTabSwitcher_| of BrowserBridge. Since views depend on - * g_browser being initialized, have to have a BrowserBridge prior to tab - * construction. - */ -BrowserBridge.prototype.setTabSwitcher = function(categoryTabSwitcher) { - this.categoryTabSwitcher_ = categoryTabSwitcher; -}; - -/** - * Adds a listener of log entries. |observer| will be called back when new log - * data arrives, through: - * - * observer.onLogEntryAdded(logEntry) - */ -BrowserBridge.prototype.addLogObserver = function(observer) { - this.logObservers_.push(observer); -}; - -/** - * Adds a listener of the proxy settings. |observer| will be called back when - * data is received, through: - * - * observer.onProxySettingsChanged(proxySettings) - * - * |proxySettings| is a dictionary with (up to) two properties: - * - * "original" -- The settings that chrome was configured to use - * (i.e. system settings.) - * "effective" -- The "effective" proxy settings that chrome is using. - * (decides between the manual/automatic modes of the - * fetched settings). - * - * Each of these two configurations is formatted as a string, and may be - * omitted if not yet initialized. - * - * TODO(eroman): send a dictionary instead. - */ -BrowserBridge.prototype.addProxySettingsObserver = function(observer) { - this.pollableDataHelpers_.proxySettings.addObserver(observer); -}; +function onLoadLogFile(fileName) { + // Swap out the status bar to indicate we have loaded from a file. + setNodeDisplay($('statusViewForCapture'), false); + setNodeDisplay($('statusViewForFile'), true); -/** - * Adds a listener of the proxy settings. |observer| will be called back when - * data is received, through: - * - * observer.onBadProxiesChanged(badProxies) - * - * |badProxies| is an array, where each entry has the property: - * badProxies[i].proxy_uri: String identify the proxy. - * badProxies[i].bad_until: The time when the proxy stops being considered - * bad. Note the time is in time ticks. - */ -BrowserBridge.prototype.addBadProxiesObserver = function(observer) { - this.pollableDataHelpers_.badProxies.addObserver(observer); -}; + // Indicate which file is being displayed. + $('statusViewDumpFileName').innerText = fileName; -/** - * Adds a listener of the host resolver info. |observer| will be called back - * when data is received, through: - * - * observer.onHostResolverInfoChanged(hostResolverInfo) - */ -BrowserBridge.prototype.addHostResolverInfoObserver = function(observer) { - this.pollableDataHelpers_.hostResolverInfo.addObserver(observer); -}; - -/** - * Adds a listener of the socket pool. |observer| will be called back - * when data is received, through: - * - * observer.onSocketPoolInfoChanged(socketPoolInfo) - */ -BrowserBridge.prototype.addSocketPoolInfoObserver = function(observer) { - this.pollableDataHelpers_.socketPoolInfo.addObserver(observer); -}; + document.styleSheets[0].insertRule('.hideOnLoadLog { display: none; }'); -/** - * Adds a listener of the SPDY info. |observer| will be called back - * when data is received, through: - * - * observer.onSpdySessionInfoChanged(spdySessionInfo) - */ -BrowserBridge.prototype.addSpdySessionInfoObserver = function(observer) { - this.pollableDataHelpers_.spdySessionInfo.addObserver(observer); -}; - -/** - * Adds a listener of the SPDY status. |observer| will be called back - * when data is received, through: - * - * observer.onSpdyStatusChanged(spdyStatus) - */ -BrowserBridge.prototype.addSpdyStatusObserver = function(observer) { - this.pollableDataHelpers_.spdyStatus.addObserver(observer); -}; + g_browser.sourceTracker.setSecurityStripping(false); + g_browser.disable(); +} /** - * Adds a listener of the AlternateProtocolMappings. |observer| will be called - * back when data is received, through: - * - * observer.onSpdyAlternateProtocolMappingsChanged( - * spdyAlternateProtocolMappings) + * Returns true if |constants| appears to be a valid constants object. */ -BrowserBridge.prototype.addSpdyAlternateProtocolMappingsObserver = - function(observer) { - this.pollableDataHelpers_.spdyAlternateProtocolMappings.addObserver(observer); -}; +function areValidConstants(receivedConstants) { + return typeof(receivedConstants) == 'object' && + typeof(receivedConstants.logEventTypes) == 'object' && + typeof(receivedConstants.clientInfo) == 'object' && + typeof(receivedConstants.logEventPhase) == 'object' && + typeof(receivedConstants.logSourceType) == 'object' && + typeof(receivedConstants.logLevelType) == 'object' && + typeof(receivedConstants.loadFlag) == 'object' && + typeof(receivedConstants.netError) == 'object' && + typeof(receivedConstants.addressFamily) == 'object' && + typeof(receivedConstants.timeTickOffset) == 'string' && + typeof(receivedConstants.logFormatVersion) == 'number'; +} /** - * Adds a listener of the service providers info. |observer| will be called - * back when data is received, through: - * - * observer.onServiceProvidersChanged(serviceProviders) - * - * Will do nothing if on a platform other than Windows, as service providers are - * only present on Windows. + * Dictionary of all constants, used for saving log files. */ -BrowserBridge.prototype.addServiceProvidersObserver = function(observer) { - if (this.pollableDataHelpers_.serviceProviders) - this.pollableDataHelpers_.serviceProviders.addObserver(observer); -}; +var constants = null; /** - * Adds a listener for the progress of the connection tests. - * The observer will be called back with: - * - * observer.onStartedConnectionTestSuite(); - * observer.onStartedConnectionTestExperiment(experiment); - * observer.onCompletedConnectionTestExperiment(experiment, result); - * observer.onCompletedConnectionTestSuite(); + * Offset needed to convert event times to Date objects. */ -BrowserBridge.prototype.addConnectionTestsObserver = function(observer) { - this.connectionTestsObservers_.push(observer); -}; +var timeTickOffset = 0; -/** - * Adds a listener for the http cache info results. - * The observer will be called back with: - * - * observer.onHttpCacheInfoChanged(info); - */ -BrowserBridge.prototype.addHttpCacheInfoObserver = function(observer) { - this.pollableDataHelpers_.httpCacheInfo.addObserver(observer); -}; +function ConstantsObserver() {} /** - * Adds a listener for the results of HSTS (HTTPS Strict Transport Security) - * queries. The observer will be called back with: - * - * observer.onHSTSQueryResult(result); + * Attempts to load all constants from |constants|. Returns false if one or + * more entries are missing. On failure, global dictionaries are not modified. */ -BrowserBridge.prototype.addHSTSObserver = function(observer) { - this.hstsObservers_.push(observer); -}; +ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) { + if (!areValidConstants(receivedConstants)) + return false; -/** - * Adds a listener for HTTP throttling-related events. |observer| will be called - * back when HTTP throttling is enabled/disabled, through: - * - * observer.onHttpThrottlingEnabledPrefChanged(enabled); - */ -BrowserBridge.prototype.addHttpThrottlingObserver = function(observer) { - this.httpThrottlingObservers_.push(observer); -}; + constants = receivedConstants; -/** - * Adds a listener for updated prerender info events - * |observer| will be called back with: - * - * observer.onPrerenderInfoChanged(prerenderInfo); - */ -BrowserBridge.prototype.addPrerenderInfoObserver = function(observer) { - this.pollableDataHelpers_.prerenderInfo.addObserver(observer); -}; + LogEventType = constants.logEventTypes; + ClientInfo = constants.clientInfo; + LogEventPhase = constants.logEventPhase; + LogSourceType = constants.logSourceType; + LogLevelType = constants.logLevelType; + LoadFlag = constants.loadFlag; + NetError = constants.netError; + AddressFamily = constants.addressFamily; -/** - * The browser gives us times in terms of "time ticks" in milliseconds. - * This function converts the tick count to a Date() object. - * - * @param {String} timeTicks. - * @returns {Date} The time that |timeTicks| represents. - */ -BrowserBridge.prototype.convertTimeTicksToDate = function(timeTicks) { // Note that the subtraction by 0 is to cast to a number (probably a float // since the numbers are big). - var timeStampMs = (this.timeTickOffset_ - 0) + (timeTicks - 0); - var d = new Date(); - d.setTime(timeStampMs); - return d; -}; + timeTickOffset = constants.timeTickOffset - 0; -/** - * Returns a list of all captured events. - */ -BrowserBridge.prototype.getAllCapturedEvents = function() { - return this.capturedEvents_; -}; - -/** - * Returns the number of events that were captured while we were - * listening for events. - */ -BrowserBridge.prototype.getNumActivelyCapturedEvents = function() { - return this.capturedEvents_.length - this.numPassivelyCapturedEvents_; + return true; }; -/** - * Returns the number of events that were captured passively by the - * browser prior to when the net-internals page was started. - */ -BrowserBridge.prototype.getNumPassivelyCapturedEvents = function() { - return this.numPassivelyCapturedEvents_; +convertTimeTicksToDate = function(timeTicks) { + var timeStampMs = timeTickOffset + (timeTicks - 0); + return new Date(timeStampMs); }; /** - * Sends each entry to all log observers, and updates |capturedEvents_|. - * Also assigns unique ids to log entries without a source. + * Creates a new log dump. |events| is a list of all events, |polledData| is an + * object containing the results of each poll, |tabData| is an object containing + * data for individual tabs, and |date| is the time the dump was created, as a + * formatted string. + * Returns the new log dump as an object. |date| may be null. + * + * Log dumps are just JSON objects containing four values: + * |constants| needed to interpret the data. This also includes some browser + * state information + * |events| from the NetLog, + * |polledData| from each PollableDataHelper available on the source OS, + * |tabData| containing any tab-specific state that's not present in + * |polledData|. + * + * |polledData| and |tabData| may be empty objects, or may be missing data for + * tabs not present on the OS the log is from. */ -BrowserBridge.prototype.addLogEntries = function(logEntries) { - for (var e = 0; e < logEntries.length; ++e) { - var logEntry = logEntries[e]; - - // Assign unique ID, if needed. - if (logEntry.source.id == 0) { - logEntry.source.id = this.nextSourcelessEventId_; - --this.nextSourcelessEventId_; - } - this.capturedEvents_.push(logEntry); - for (var i = 0; i < this.logObservers_.length; ++i) - this.logObservers_[i].onLogEntryAdded(logEntry); - } -}; +function createLogDump(constants, events, polledData, tabData, date) { + if (g_browser.sourceTracker.getSecurityStripping()) + events = events.map(stripCookiesAndLoginInfo); -/** - * Deletes captured events with source IDs in |sourceIds|. - */ -BrowserBridge.prototype.deleteEventsBySourceId = function(sourceIds) { - var sourceIdDict = {}; - for (var i = 0; i < sourceIds.length; i++) - sourceIdDict[sourceIds[i]] = true; - - var newEventList = []; - for (var i = 0; i < this.capturedEvents_.length; ++i) { - var id = this.capturedEvents_[i].source.id; - if (id in sourceIdDict) { - if (this.capturedEvents_[i].wasPassivelyCaptured) - --this.numPassivelyCapturedEvents_; - continue; - } - newEventList.push(this.capturedEvents_[i]); - } - this.capturedEvents_ = newEventList; + var logDump = { + 'constants': constants, + 'events': events, + 'polledData': polledData, + 'tabData': tabData + }; - for (var i = 0; i < this.logObservers_.length; ++i) - this.logObservers_[i].onLogEntriesDeleted(sourceIds); -}; + // Not technically client info, but it's used at the same point in the code. + if (date && constants.clientInfo) + constants.clientInfo.date = date; -/** - * Deletes all captured events. - */ -BrowserBridge.prototype.deleteAllEvents = function() { - this.capturedEvents_ = []; - this.numPassivelyCapturedEvents_ = 0; - for (var i = 0; i < this.logObservers_.length; ++i) - this.logObservers_[i].onAllLogEntriesDeleted(); -}; + return logDump; +} /** - * Sets the value of |enableSecurityStripping_| and informs log observers - * of the change. + * Creates a full log dump using |polledData| and the return value of each tab's + * saveState function and passes it to |callback|. */ -BrowserBridge.prototype.setSecurityStripping = - function(enableSecurityStripping) { - this.enableSecurityStripping_ = enableSecurityStripping; - for (var i = 0; i < this.logObservers_.length; ++i) { - if (this.logObservers_[i].onSecurityStrippingChanged) - this.logObservers_[i].onSecurityStrippingChanged(); +function onUpdateAllCompleted(callback, polledData) { + // Gather any tab-specific state information. + var tabData = {}; + var tabIds = categoryTabSwitcher.getAllTabIds(); + for (var i = 0; i < tabIds.length; ++i) { + var view = categoryTabSwitcher.findTabById(tabIds[i]).contentView; + if (view.saveState) + tabData[tabIds[i]] = view.saveState(); } -}; - -/** - * Returns whether or not cookies and authentication information should be - * displayed for events that contain them. - */ -BrowserBridge.prototype.getSecurityStripping = function() { - return this.enableSecurityStripping_; -}; -/** - * Returns true if a log file is currently being viewed. - */ -BrowserBridge.prototype.isViewingLogFile = function() { - return this.isViewingLogFile_; -}; + var logDump = createLogDump(constants, + g_browser.sourceTracker.getAllCapturedEvents(), + polledData, + tabData, + (new Date()).toLocaleString()); + callback(JSON.stringify(logDump, null, ' ')); +} -/** - * If |force| is true, calls all startUpdate functions. Otherwise, just - * runs updates with active observers. - */ -BrowserBridge.prototype.checkForUpdatedInfo = function(force) { - for (name in this.pollableDataHelpers_) { - var helper = this.pollableDataHelpers_[name]; - if (force || helper.hasActiveObserver()) - helper.startUpdate(); +createLogDumpAsync = function(callback) { + g_browser.updateAllInfo(onUpdateAllCompleted.bind(null, callback)); +}; + +/** + * Loads a full log dump. Returns a string containing a log of the load. + * The process goes like this: + * 1) Load constants. If this fails, or the version number can't be handled, + * abort the load. If this step succeeds, the load cannot be aborted. + * 2) Clear all events. Any event observers are informed of the clear as + * normal. + * 3) Call onLoadLogStart(polledData, tabData) for each view with an + * onLoadLogStart function. This allows tabs to clear any extra state that + * would affect the next step. |polledData| contains the data polled for + * all helpers, but |tabData| contains only the data from that specific tab. + * 4) Add all events from the log file. + * 5) Call onLoadLogFinish(polledData, tabData) for each view with an + * onLoadLogFinish function. The arguments are the same as in step 3. If + * there is no onLoadLogFinish function, it throws an exception, or it + * returns false instead of true, the data dump is assumed to contain no + * valid data for the tab, so the tab is hidden. Otherwise, the tab is + * shown. + */ +function loadLogDump(logDump, fileName) { + // Perform minimal validity check, and abort if it fails. + if (typeof(logDump) != 'object') + return 'Load failed. Top level JSON data is not an object.'; + + // String listing text summary of load errors, if any. + var errorString = ''; + + if (!areValidConstants(logDump.constants)) + errorString += 'Invalid constants object.\n'; + if (typeof(logDump.events) != 'object') + errorString += 'NetLog events missing.\n'; + if (typeof(logDump.constants.logFormatVersion) != 'number') + errorString += 'Invalid version number.\n'; + + if (errorString.length > 0) + return 'Load failed:\n\n' + errorString; + + if (typeof(logDump.polledData) != 'object') + logDump.polledData = {}; + if (typeof(logDump.tabData) != 'object') + logDump.tabData = {}; + + if (logDump.constants.logFormatVersion != constants.logFormatVersion) { + return 'Unable to load different log version.' + + ' Found ' + logDump.constants.logFormatVersion + + ', Expected ' + constants.logFormatVersion; } -}; + if (!areValidConstants(logDump.constants)) + return 'Expected constants not found in log file.'; -/** - * Calls all startUpdate functions and, if |callback| is non-null, - * calls it with the results of all updates. - */ -BrowserBridge.prototype.updateAllInfo = function(callback) { - if (callback) - new UpdateAllObserver(callback, this.pollableDataHelpers_); - this.checkForUpdatedInfo(true); -}; + g_browser.receivedConstants(logDump.constants); -/** - * This is a helper class used by BrowserBridge, to keep track of: - * - the list of observers interested in some piece of data. - * - the last known value of that piece of data. - * - the name of the callback method to invoke on observers. - * - the update function. - * @constructor - */ -function PollableDataHelper(observerMethodName, startUpdateFunction) { - this.observerMethodName_ = observerMethodName; - this.startUpdate = startUpdateFunction; - this.observerInfos_ = []; -} + // Prevent communication with the browser. Once the constants have been + // loaded, it's safer to continue trying to load the log, even in the case of + // bad data. + onLoadLogFile(fileName); -PollableDataHelper.prototype.getObserverMethodName = function() { - return this.observerMethodName_; -}; + // Delete all events. This will also update all logObservers. + g_browser.sourceTracker.deleteAllSourceEntries(); -PollableDataHelper.prototype.isObserver = function(object) { - for (var i = 0; i < this.observerInfos_.length; ++i) { - if (this.observerInfos_[i].observer == object) - return true; + // Inform all the views that a log file is being loaded, and pass in + // view-specific saved state, if any. + var tabIds = categoryTabSwitcher.getAllTabIds(); + for (var i = 0; i < tabIds.length; ++i) { + var view = categoryTabSwitcher.findTabById(tabIds[i]).contentView; + view.onLoadLogStart(logDump.polledData, logDump.tabData[tabIds[i]]); } - return false; -}; - -/** - * This is a helper class used by PollableDataHelper, to keep track of - * each observer and whether or not it has received any data. The - * latter is used to make sure that new observers get sent data on the - * update following their creation. - * @constructor - */ -function ObserverInfo(observer) { - this.observer = observer; - this.hasReceivedData = false; -} - -PollableDataHelper.prototype.addObserver = function(observer) { - this.observerInfos_.push(new ObserverInfo(observer)); -}; -PollableDataHelper.prototype.removeObserver = function(observer) { - for (var i = 0; i < this.observerInfos_.length; ++i) { - if (this.observerInfos_[i].observer === observer) { - this.observerInfos_.splice(i, 1); - return; + // Check for validity of each log entry, and then add the ones that pass. + // Since the events are kept around, and we can't just hide a single view + // on a bad event, we have more error checking for them than other data. + var validPassiveEvents = []; + var validActiveEvents = []; + for (var eventIndex = 0; eventIndex < logDump.events.length; ++eventIndex) { + var event = logDump.events[eventIndex]; + if (typeof(event) == 'object' && typeof(event.source) == 'object' && + typeof(event.time) == 'string' && + getKeyWithValue(LogEventType, event.type) != '?' && + getKeyWithValue(LogSourceType, event.source.type) != '?' && + getKeyWithValue(LogEventPhase, event.phase) != '?') { + if (event.wasPassivelyCaptured) { + validPassiveEvents.push(event); + } else { + validActiveEvents.push(event); + } } } -}; - -/** - * Helper function to handle calling all the observers, but ONLY if the data has - * actually changed since last time or the observer has yet to receive any data. - * This is used for data we received from browser on an update loop. - */ -PollableDataHelper.prototype.update = function(data) { - var prevData = this.currentData_; - var changed = false; - - // If the data hasn't changed since last time, will only need to notify - // observers that have not yet received any data. - if (!prevData || JSON.stringify(prevData) != JSON.stringify(data)) { - changed = true; - this.currentData_ = data; + g_browser.sourceTracker.onReceivedPassiveLogEntries(validPassiveEvents); + g_browser.sourceTracker.onReceivedLogEntries(validActiveEvents); + + var numInvalidEvents = logDump.events.length + - validPassiveEvents.length + - validActiveEvents.length; + if (numInvalidEvents > 0) { + errorString += 'Unable to load ' + numInvalidEvents + + ' events, due to invalid data.\n\n'; } - // Notify the observers of the change, as needed. - for (var i = 0; i < this.observerInfos_.length; ++i) { - var observerInfo = this.observerInfos_[i]; - if (changed || !observerInfo.hasReceivedData) { - observerInfo.observer[this.observerMethodName_](this.currentData_); - observerInfo.hasReceivedData = true; + // Update all views with data from the file. Show only those views which + // successfully load the data. + for (var i = 0; i < tabIds.length; ++i) { + var view = categoryTabSwitcher.findTabById(tabIds[i]).contentView; + var showView = false; + // The try block eliminates the need for checking every single value before + // trying to access it. + try { + if (view.onLoadLogFinish(logDump.polledData, + logDump.tabData[tabIds[i]])) { + showView = true; + } + } catch (error) { } + categoryTabSwitcher.showTabHandleNode(tabIds[i], showView); } -}; -/** - * Returns true if one of the observers actively wants the data - * (i.e. is visible). - */ -PollableDataHelper.prototype.hasActiveObserver = function() { - for (var i = 0; i < this.observerInfos_.length; ++i) { - if (this.observerInfos_[i].observer.isActive()) - return true; - } - return false; -}; + return errorString + 'Log loaded.'; +} -/** - * This is a helper class used by BrowserBridge to send data to - * a callback once data from all polls has been received. - * - * It works by keeping track of how many polling functions have - * yet to receive data, and recording the data as it it received. - * - * @constructor - */ -function UpdateAllObserver(callback, pollableDataHelpers) { - this.callback_ = callback; - this.observingCount_ = 0; - this.updatedData_ = {}; - - for (name in pollableDataHelpers) { - ++this.observingCount_; - var helper = pollableDataHelpers[name]; - helper.addObserver(this); - this[helper.getObserverMethodName()] = - this.onDataReceived_.bind(this, helper, name); +loadLogFile = function(logFileContents, fileName) { + // Try and parse the log dump as a single JSON string. If this succeeds, + // it's most likely a full log dump. Otherwise, it may be a dump created by + // --log-net-log. + var parsedDump = null; + try { + parsedDump = JSON.parse(logFileContents); + } catch (error) { + try { + // We may have a --log-net-log=blah log dump. If so, remove the comma + // after the final good entry, and add the necessary close brackets. + var end = Math.max(logFileContents.lastIndexOf(',\n'), + logFileContents.lastIndexOf(',\r')); + if (end != -1) + parsedDump = JSON.parse(logFileContents.substring(0, end) + ']}'); + } + catch (error) { + } } -} -UpdateAllObserver.prototype.isActive = function() { - return true; + if (!parsedDump) + return 'Unable to parse log dump as JSON file.'; + return loadLogDump(parsedDump, fileName); }; -UpdateAllObserver.prototype.onDataReceived_ = function(helper, name, data) { - helper.removeObserver(this); - --this.observingCount_; - this.updatedData_[name] = data; - if (this.observingCount_ == 0) - this.callback_(this.updatedData_); -}; +// End of anonymous namespace. +})(); |