OLD | NEW |
(Empty) | |
| 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 log_util = (function() { |
| 6 'use strict'; |
| 7 |
| 8 /** |
| 9 * Creates a new log dump. |events| is a list of all events, |polledData| is |
| 10 * an object containing the results of each poll, |tabData| is an object |
| 11 * containing data for individual tabs, |date| is the time the dump was |
| 12 * created, as a formatted string, and |privacyStripping| is whether or not |
| 13 * private information should be removed from the generated dump. |
| 14 * |
| 15 * Returns the new log dump as an object. Resulting object may have a null |
| 16 * |numericDate|. |
| 17 * |
| 18 * TODO(eroman): Use javadoc notation for these parameters. |
| 19 * |
| 20 * Log dumps are just JSON objects containing five values: |
| 21 * |
| 22 * |userComments| User-provided notes describing what this dump file is |
| 23 * about. |
| 24 * |constants| needed to interpret the data. This also includes some |
| 25 * browser state information. |
| 26 * |events| from the NetLog. |
| 27 * |polledData| from each PollableDataHelper available on the source OS. |
| 28 * |tabData| containing any tab-specific state that's not present in |
| 29 * |polledData|. |
| 30 * |
| 31 * |polledData| and |tabData| may be empty objects, or may be missing data for |
| 32 * tabs not present on the OS the log is from. |
| 33 */ |
| 34 function createLogDump(userComments, constants, events, polledData, tabData, |
| 35 numericDate, privacyStripping) { |
| 36 if (privacyStripping) |
| 37 events = events.map(stripPrivacyInfo); |
| 38 |
| 39 var logDump = { |
| 40 'userComments': userComments, |
| 41 'constants': constants, |
| 42 'events': events, |
| 43 'polledData': polledData, |
| 44 'tabData': tabData |
| 45 }; |
| 46 |
| 47 // Not technically client info, but it's used at the same point in the code. |
| 48 if (numericDate && constants.clientInfo) { |
| 49 constants.clientInfo.numericDate = numericDate; |
| 50 } |
| 51 |
| 52 return logDump; |
| 53 } |
| 54 |
| 55 /** |
| 56 * Returns a new log dump created using the polled data and date from the |
| 57 * |oldLogDump|. The other parts of the log dump come from current |
| 58 * net-internals state. |
| 59 */ |
| 60 function createUpdatedLogDump(userComments, oldLogDump, privacyStripping) { |
| 61 var numericDate = null; |
| 62 if (oldLogDump.constants.clientInfo && |
| 63 oldLogDump.constants.clientInfo.numericDate) { |
| 64 numericDate = oldLogDump.constants.clientInfo.numericDate; |
| 65 } |
| 66 var logDump = createLogDump( |
| 67 userComments, |
| 68 Constants, |
| 69 EventsTracker.getInstance().getAllCapturedEvents(), |
| 70 oldLogDump.polledData, |
| 71 getTabData_(), |
| 72 numericDate, |
| 73 privacyStripping); |
| 74 return JSON.stringify(logDump); |
| 75 } |
| 76 |
| 77 /** |
| 78 * Creates a full log dump using |polledData| and the return value of each |
| 79 * tab's saveState function and passes it to |callback|. |
| 80 */ |
| 81 function onUpdateAllCompleted(userComments, callback, privacyStripping, |
| 82 polledData) { |
| 83 var logDump = createLogDump( |
| 84 userComments, |
| 85 Constants, |
| 86 EventsTracker.getInstance().getAllCapturedEvents(), |
| 87 polledData, |
| 88 getTabData_(), |
| 89 timeutil.getCurrentTime(), |
| 90 privacyStripping); |
| 91 callback(JSON.stringify(logDump)); |
| 92 } |
| 93 |
| 94 /** |
| 95 * Called to create a new log dump. Must not be called once a dump has been |
| 96 * loaded. Once a log dump has been created, |callback| is passed the dumped |
| 97 * text as a string. |
| 98 */ |
| 99 function createLogDumpAsync(userComments, callback, privacyStripping) { |
| 100 g_browser.updateAllInfo( |
| 101 onUpdateAllCompleted.bind(null, userComments, callback, |
| 102 privacyStripping)); |
| 103 } |
| 104 |
| 105 /** |
| 106 * Gather any tab-specific state information prior to creating a log dump. |
| 107 */ |
| 108 function getTabData_() { |
| 109 var tabData = {}; |
| 110 var tabSwitcher = MainView.getInstance().tabSwitcher(); |
| 111 var tabIdToView = tabSwitcher.getAllTabViews(); |
| 112 for (var tabId in tabIdToView) { |
| 113 var view = tabIdToView[tabId]; |
| 114 if (view.saveState) |
| 115 tabData[tabId] = view.saveState(); |
| 116 } |
| 117 } |
| 118 |
| 119 /** |
| 120 * Loads a full log dump. Returns a string containing a log of the load. |
| 121 * |opt_fileName| should always be given when loading from a file, instead of |
| 122 * from a log dump generated in-memory. |
| 123 * The process goes like this: |
| 124 * 1) Load constants. If this fails, or the version number can't be handled, |
| 125 * abort the load. If this step succeeds, the load cannot be aborted. |
| 126 * 2) Clear all events. Any event observers are informed of the clear as |
| 127 * normal. |
| 128 * 3) Call onLoadLogStart(polledData, tabData) for each view with an |
| 129 * onLoadLogStart function. This allows tabs to clear any extra state |
| 130 * that would affect the next step. |polledData| contains the data polled |
| 131 * for all helpers, but |tabData| contains only the data from that |
| 132 * specific tab. |
| 133 * 4) Add all events from the log file. |
| 134 * 5) Call onLoadLogFinish(polledData, tabData) for each view with an |
| 135 * onLoadLogFinish function. The arguments are the same as in step 3. If |
| 136 * there is no onLoadLogFinish function, it throws an exception, or it |
| 137 * returns false instead of true, the data dump is assumed to contain no |
| 138 * valid data for the tab, so the tab is hidden. Otherwise, the tab is |
| 139 * shown. |
| 140 */ |
| 141 function loadLogDump(logDump, opt_fileName) { |
| 142 // Perform minimal validity check, and abort if it fails. |
| 143 if (typeof(logDump) != 'object') |
| 144 return 'Load failed. Top level JSON data is not an object.'; |
| 145 |
| 146 // String listing text summary of load errors, if any. |
| 147 var errorString = ''; |
| 148 |
| 149 if (!areValidConstants(logDump.constants)) |
| 150 errorString += 'Invalid constants object.\n'; |
| 151 if (typeof(logDump.events) != 'object') |
| 152 errorString += 'NetLog events missing.\n'; |
| 153 if (typeof(logDump.constants.logFormatVersion) != 'number') |
| 154 errorString += 'Invalid version number.\n'; |
| 155 |
| 156 if (errorString.length > 0) |
| 157 return 'Load failed:\n\n' + errorString; |
| 158 |
| 159 if (typeof(logDump.polledData) != 'object') |
| 160 logDump.polledData = {}; |
| 161 if (typeof(logDump.tabData) != 'object') |
| 162 logDump.tabData = {}; |
| 163 |
| 164 var kSupportedLogFormatVersion = 1; |
| 165 |
| 166 if (logDump.constants.logFormatVersion != kSupportedLogFormatVersion) { |
| 167 return 'Unable to load different log version.' + |
| 168 ' Found ' + logDump.constants.logFormatVersion + |
| 169 ', Expected ' + kSupportedLogFormatVersion; |
| 170 } |
| 171 |
| 172 g_browser.receivedConstants(logDump.constants); |
| 173 |
| 174 // Check for validity of each log entry, and then add the ones that pass. |
| 175 // Since the events are kept around, and we can't just hide a single view |
| 176 // on a bad event, we have more error checking for them than other data. |
| 177 var validEvents = []; |
| 178 var numDeprecatedPassiveEvents = 0; |
| 179 for (var eventIndex = 0; eventIndex < logDump.events.length; ++eventIndex) { |
| 180 var event = logDump.events[eventIndex]; |
| 181 if (typeof event == 'object' && |
| 182 typeof event.source == 'object' && |
| 183 typeof event.time == 'string' && |
| 184 typeof EventTypeNames[event.type] == 'string' && |
| 185 typeof EventSourceTypeNames[event.source.type] == 'string' && |
| 186 getKeyWithValue(EventPhase, event.phase) != '?') { |
| 187 if (event.wasPassivelyCaptured) { |
| 188 // NOTE: Up until Chrome 18, log dumps included "passively captured" |
| 189 // events. These are no longer supported, so skip past them |
| 190 // to avoid confusing the rest of the code. |
| 191 numDeprecatedPassiveEvents++; |
| 192 continue; |
| 193 } |
| 194 validEvents.push(event); |
| 195 } |
| 196 } |
| 197 |
| 198 // Make sure the loaded log contained an export date. If not we will |
| 199 // synthesize one. This can legitimately happen for dump files created |
| 200 // via command line flag, or for older dump formats (before Chrome 17). |
| 201 if (typeof logDump.constants.clientInfo.numericDate != 'number') { |
| 202 errorString += 'The log file is missing clientInfo.numericDate.\n'; |
| 203 |
| 204 if (validEvents.length > 0) { |
| 205 errorString += |
| 206 'Synthesizing export date as time of last event captured.\n'; |
| 207 var lastEvent = validEvents[validEvents.length - 1]; |
| 208 ClientInfo.numericDate = |
| 209 timeutil.convertTimeTicksToDate(lastEvent.time).getTime(); |
| 210 } else { |
| 211 errorString += 'Can\'t guess export date!\n'; |
| 212 ClientInfo.numericDate = 0; |
| 213 } |
| 214 } |
| 215 |
| 216 // Prevent communication with the browser. Once the constants have been |
| 217 // loaded, it's safer to continue trying to load the log, even in the case |
| 218 // of bad data. |
| 219 MainView.getInstance().onLoadLog(opt_fileName); |
| 220 |
| 221 // Delete all events. This will also update all logObservers. |
| 222 EventsTracker.getInstance().deleteAllLogEntries(); |
| 223 |
| 224 // Inform all the views that a log file is being loaded, and pass in |
| 225 // view-specific saved state, if any. |
| 226 var tabSwitcher = MainView.getInstance().tabSwitcher(); |
| 227 var tabIdToView = tabSwitcher.getAllTabViews(); |
| 228 for (var tabId in tabIdToView) { |
| 229 var view = tabIdToView[tabId]; |
| 230 view.onLoadLogStart(logDump.polledData, logDump.tabData[tabId]); |
| 231 } |
| 232 EventsTracker.getInstance().addLogEntries(validEvents); |
| 233 |
| 234 var numInvalidEvents = logDump.events.length - |
| 235 (validEvents.length + numDeprecatedPassiveEvents); |
| 236 if (numInvalidEvents > 0) { |
| 237 errorString += 'Unable to load ' + numInvalidEvents + |
| 238 ' events, due to invalid data.\n\n'; |
| 239 } |
| 240 |
| 241 if (numDeprecatedPassiveEvents > 0) { |
| 242 errorString += 'Discarded ' + numDeprecatedPassiveEvents + |
| 243 ' passively collected events. Use an older version of Chrome to' + |
| 244 ' load this dump if you want to see them.\n\n'; |
| 245 } |
| 246 |
| 247 // Update all views with data from the file. Show only those views which |
| 248 // successfully load the data. |
| 249 for (var tabId in tabIdToView) { |
| 250 var view = tabIdToView[tabId]; |
| 251 var showView = false; |
| 252 // The try block eliminates the need for checking every single value |
| 253 // before trying to access it. |
| 254 try { |
| 255 if (view.onLoadLogFinish(logDump.polledData, |
| 256 logDump.tabData[tabId], |
| 257 logDump)) { |
| 258 showView = true; |
| 259 } |
| 260 } catch (error) { |
| 261 errorString += 'Caught error while calling onLoadLogFinish: ' + |
| 262 error + '\n\n'; |
| 263 } |
| 264 tabSwitcher.showTabLink(tabId, showView); |
| 265 } |
| 266 |
| 267 return errorString + 'Log loaded.'; |
| 268 } |
| 269 |
| 270 /** |
| 271 * Loads a log dump from the string |logFileContents|, which can be either a |
| 272 * full net-internals dump, or a NetLog dump only. Returns a string |
| 273 * containing a log of the load. |
| 274 */ |
| 275 function loadLogFile(logFileContents, fileName) { |
| 276 // Try and parse the log dump as a single JSON string. If this succeeds, |
| 277 // it's most likely a full log dump. Otherwise, it may be a dump created by |
| 278 // --log-net-log. |
| 279 var parsedDump = null; |
| 280 var errorString = ''; |
| 281 try { |
| 282 parsedDump = JSON.parse(logFileContents); |
| 283 } catch (error) { |
| 284 try { |
| 285 // We may have a --log-net-log=blah log dump. If so, remove the comma |
| 286 // after the final good entry, and add the necessary close brackets. |
| 287 var end = Math.max(logFileContents.lastIndexOf(',\n'), |
| 288 logFileContents.lastIndexOf(',\r')); |
| 289 if (end != -1) { |
| 290 parsedDump = JSON.parse(logFileContents.substring(0, end) + ']}'); |
| 291 errorString += 'Log file truncated. Events may be missing.\n'; |
| 292 } |
| 293 } |
| 294 catch (error2) { |
| 295 } |
| 296 } |
| 297 |
| 298 if (!parsedDump) |
| 299 return 'Unable to parse log dump as JSON file.'; |
| 300 return errorString + loadLogDump(parsedDump, fileName); |
| 301 } |
| 302 |
| 303 // Exports. |
| 304 return { |
| 305 createUpdatedLogDump: createUpdatedLogDump, |
| 306 createLogDumpAsync: createLogDumpAsync, |
| 307 loadLogFile: loadLogFile |
| 308 }; |
| 309 })(); |
| 310 |
OLD | NEW |