| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| log_util = (function() { |
| 'use strict'; |
| |
| /** |
| * 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, |date| is the time the dump was |
| * created, as a formatted string, and |privacyStripping| is whether or not |
| * private information should be removed from the generated dump. |
| * |
| * Returns the new log dump as an object. Resulting object may have a null |
| * |numericDate|. |
| * |
| * TODO(eroman): Use javadoc notation for these parameters. |
| * |
| * Log dumps are just JSON objects containing five values: |
| * |
| * |userComments| User-provided notes describing what this dump file is |
| * about. |
| * |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. |
| */ |
| function createLogDump(userComments, constants, events, polledData, tabData, |
| numericDate, privacyStripping) { |
| if (privacyStripping) |
| events = events.map(stripCookiesAndLoginInfo); |
| |
| var logDump = { |
| 'userComments': userComments, |
| 'constants': constants, |
| 'events': events, |
| 'polledData': polledData, |
| 'tabData': tabData |
| }; |
| |
| // Not technically client info, but it's used at the same point in the code. |
| if (numericDate && constants.clientInfo) { |
| constants.clientInfo.numericDate = numericDate; |
| } |
| |
| return logDump; |
| } |
| |
| /** |
| * Returns a new log dump created using the polled data and date from the |
| * |oldLogDump|. The other parts of the log dump come from current |
| * net-internals state. |
| */ |
| function createUpdatedLogDump(userComments, oldLogDump, privacyStripping) { |
| var numericDate = null; |
| if (oldLogDump.constants.clientInfo && |
| oldLogDump.constants.clientInfo.numericDate) { |
| numericDate = oldLogDump.constants.clientInfo.numericDate; |
| } |
| var logDump = createLogDump( |
| userComments, |
| Constants, |
| EventsTracker.getInstance().getAllCapturedEvents(), |
| oldLogDump.polledData, |
| getTabData_(), |
| numericDate, |
| privacyStripping); |
| return JSON.stringify(logDump); |
| } |
| |
| /** |
| * Creates a full log dump using |polledData| and the return value of each |
| * tab's saveState function and passes it to |callback|. |
| */ |
| function onUpdateAllCompleted(userComments, callback, privacyStripping, |
| polledData) { |
| var logDump = createLogDump( |
| userComments, |
| Constants, |
| EventsTracker.getInstance().getAllCapturedEvents(), |
| polledData, |
| getTabData_(), |
| timeutil.getCurrentTime(), |
| privacyStripping); |
| callback(JSON.stringify(logDump)); |
| } |
| |
| /** |
| * 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. |
| */ |
| function createLogDumpAsync(userComments, callback, privacyStripping) { |
| g_browser.updateAllInfo( |
| onUpdateAllCompleted.bind(null, userComments, callback, |
| privacyStripping)); |
| } |
| |
| /** |
| * Gather any tab-specific state information prior to creating a log dump. |
| */ |
| function getTabData_() { |
| var tabData = {}; |
| var tabSwitcher = MainView.getInstance().tabSwitcher(); |
| var tabIdToView = tabSwitcher.getAllTabViews(); |
| for (var tabId in tabIdToView) { |
| var view = tabIdToView[tabId]; |
| if (view.saveState) |
| tabData[tabId] = view.saveState(); |
| } |
| } |
| |
| /** |
| * Loads a full log dump. Returns a string containing a log of the load. |
| * |opt_fileName| should always be given when loading from a file, instead of |
| * from a log dump generated in-memory. |
| * 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, opt_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; |
| } |
| |
| g_browser.receivedConstants(logDump.constants); |
| |
| // 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 validEvents = []; |
| var numDeprecatedPassiveEvents = 0; |
| 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' && |
| typeof EventTypeNames[event.type] == 'string' && |
| typeof EventSourceTypeNames[event.source.type] == 'string' && |
| getKeyWithValue(EventPhase, event.phase) != '?') { |
| if (event.wasPassivelyCaptured) { |
| // NOTE: Up until Chrome 18, log dumps included "passively captured" |
| // events. These are no longer supported, so skip past them |
| // to avoid confusing the rest of the code. |
| numDeprecatedPassiveEvents++; |
| continue; |
| } |
| validEvents.push(event); |
| } |
| } |
| |
| // Make sure the loaded log contained an export date. If not we will |
| // synthesize one. This can legitimately happen for dump files created |
| // via command line flag, or for older dump formats (before Chrome 17). |
| if (typeof logDump.constants.clientInfo.numericDate != 'number') { |
| errorString += 'The log file is missing clientInfo.numericDate.\n'; |
| |
| if (validEvents.length > 0) { |
| errorString += |
| 'Synthesizing export date as time of last event captured.\n'; |
| var lastEvent = validEvents[validEvents.length - 1]; |
| ClientInfo.numericDate = |
| timeutil.convertTimeTicksToDate(lastEvent.time).getTime(); |
| } else { |
| errorString += 'Can\'t guess export date!\n'; |
| ClientInfo.numericDate = 0; |
| } |
| } |
| |
| // 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. |
| MainView.getInstance().onLoadLog(opt_fileName); |
| |
| // Delete all events. This will also update all logObservers. |
| EventsTracker.getInstance().deleteAllLogEntries(); |
| |
| // Inform all the views that a log file is being loaded, and pass in |
| // view-specific saved state, if any. |
| var tabSwitcher = MainView.getInstance().tabSwitcher(); |
| var tabIdToView = tabSwitcher.getAllTabViews(); |
| for (var tabId in tabIdToView) { |
| var view = tabIdToView[tabId]; |
| view.onLoadLogStart(logDump.polledData, logDump.tabData[tabId]); |
| } |
| EventsTracker.getInstance().addLogEntries(validEvents); |
| |
| var numInvalidEvents = logDump.events.length - |
| (validEvents.length + numDeprecatedPassiveEvents); |
| if (numInvalidEvents > 0) { |
| errorString += 'Unable to load ' + numInvalidEvents + |
| ' events, due to invalid data.\n\n'; |
| } |
| |
| if (numDeprecatedPassiveEvents > 0) { |
| errorString += 'Discarded ' + numDeprecatedPassiveEvents + |
| ' passively collected events. Use an older version of Chrome to' + |
| ' load this dump if you want to see them.\n\n'; |
| } |
| |
| // Update all views with data from the file. Show only those views which |
| // successfully load the data. |
| for (var tabId in tabIdToView) { |
| var view = tabIdToView[tabId]; |
| 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[tabId], |
| logDump)) { |
| showView = true; |
| } |
| } catch (error) { |
| errorString += 'Caught error while calling onLoadLogFinish: ' + |
| error + '\n\n'; |
| } |
| tabSwitcher.showMenuItem(tabId, showView); |
| } |
| |
| return errorString + 'Log loaded.'; |
| } |
| |
| /** |
| * 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. |
| */ |
| function loadLogFile(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 (error2) { |
| } |
| } |
| |
| if (!parsedDump) |
| return 'Unable to parse log dump as JSON file.'; |
| return loadLogDump(parsedDump, fileName); |
| } |
| |
| // Exports. |
| return { |
| createUpdatedLogDump: createUpdatedLogDump, |
| createLogDumpAsync: createLogDumpAsync, |
| loadLogFile: loadLogFile |
| }; |
| })(); |