| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 * This view displays options for importing/exporting the captured data. This | |
| 7 * view is responsible for the interface and file I/O. The BrowserBridge | |
| 8 * handles creation and loading of the data dumps. | |
| 9 * | |
| 10 * - Has a button to generate a JSON dump. | |
| 11 * | |
| 12 * - Has a button to import a JSON dump. | |
| 13 * | |
| 14 * - Shows how many events have been captured. | |
| 15 * @constructor | |
| 16 */ | |
| 17 function DataView() { | |
| 18 const mainBoxId = 'dataTabContent'; | |
| 19 const downloadIframeId = 'dataViewDownloadIframe'; | |
| 20 const saveFileButtonId = 'dataViewSaveLogFile'; | |
| 21 const dataViewSaveStatusTextId = 'dataViewSaveStatusText'; | |
| 22 const securityStrippingCheckboxId = 'securityStrippingCheckbox'; | |
| 23 const byteLoggingCheckboxId = 'byteLoggingCheckbox'; | |
| 24 const passivelyCapturedCountId = 'passivelyCapturedCount'; | |
| 25 const activelyCapturedCountId = 'activelyCapturedCount'; | |
| 26 const deleteAllId = 'dataViewDeleteAll'; | |
| 27 const dumpDataDivId = 'dataViewDumpDataDiv'; | |
| 28 const loadedDivId = 'dataViewLoadedDiv'; | |
| 29 const loadedClientInfoTextId = 'dataViewLoadedClientInfoText'; | |
| 30 const loadLogFileDropTargetId = 'dataViewDropTarget'; | |
| 31 const loadLogFileId = 'dataViewLoadLogFile'; | |
| 32 const dataViewLoadStatusTextId = 'dataViewLoadStatusText'; | |
| 33 const capturingTextSpanId = 'dataViewCapturingTextSpan'; | |
| 34 const loggingTextSpanId = 'dataViewLoggingTextSpan'; | |
| 35 | |
| 36 DivView.call(this, mainBoxId); | |
| 37 | |
| 38 var securityStrippingCheckbox = $(securityStrippingCheckboxId); | |
| 39 securityStrippingCheckbox.onclick = | |
| 40 this.onSetSecurityStripping_.bind(this, securityStrippingCheckbox); | |
| 41 | |
| 42 var byteLoggingCheckbox = $(byteLoggingCheckboxId); | |
| 43 byteLoggingCheckbox.onclick = | |
| 44 this.onSetByteLogging_.bind(this, byteLoggingCheckbox); | |
| 45 | |
| 46 this.downloadIframe_ = $(downloadIframeId); | |
| 47 | |
| 48 this.saveFileButton_ = $(saveFileButtonId); | |
| 49 this.saveFileButton_.onclick = this.onSaveFile_.bind(this); | |
| 50 this.saveStatusText_ = $(dataViewSaveStatusTextId); | |
| 51 | |
| 52 this.activelyCapturedCountBox_ = $(activelyCapturedCountId); | |
| 53 this.passivelyCapturedCountBox_ = $(passivelyCapturedCountId); | |
| 54 $(deleteAllId).onclick = g_browser.sourceTracker.deleteAllSourceEntries.bind( | |
| 55 g_browser.sourceTracker); | |
| 56 | |
| 57 this.dumpDataDiv_ = $(dumpDataDivId); | |
| 58 this.capturingTextSpan_ = $(capturingTextSpanId); | |
| 59 this.loggingTextSpan_ = $(loggingTextSpanId); | |
| 60 | |
| 61 this.loadedDiv_ = $(loadedDivId); | |
| 62 this.loadedClientInfoText_ = $(loadedClientInfoTextId); | |
| 63 | |
| 64 this.loadFileElement_ = $(loadLogFileId); | |
| 65 this.loadFileElement_.onchange = this.logFileChanged.bind(this); | |
| 66 this.loadStatusText_ = $(dataViewLoadStatusTextId); | |
| 67 | |
| 68 var dropTarget = $(loadLogFileDropTargetId); | |
| 69 dropTarget.ondragenter = this.onDrag.bind(this); | |
| 70 dropTarget.ondragover = this.onDrag.bind(this); | |
| 71 dropTarget.ondrop = this.onDrop.bind(this); | |
| 72 | |
| 73 this.updateEventCounts_(); | |
| 74 | |
| 75 // Track blob for previous log dump so it can be revoked when a new dump is | |
| 76 // saved. | |
| 77 this.lastBlobURL_ = null; | |
| 78 | |
| 79 g_browser.sourceTracker.addObserver(this); | |
| 80 } | |
| 81 | |
| 82 inherits(DataView, DivView); | |
| 83 | |
| 84 /** | |
| 85 * Called whenever a new event is received. | |
| 86 */ | |
| 87 DataView.prototype.onSourceEntryUpdated = function(sourceEntry) { | |
| 88 this.updateEventCounts_(); | |
| 89 }; | |
| 90 | |
| 91 /** | |
| 92 * Called whenever some log events are deleted. |sourceIds| lists | |
| 93 * the source IDs of all deleted log entries. | |
| 94 */ | |
| 95 DataView.prototype.onSourceEntriesDeleted = function(sourceIds) { | |
| 96 this.updateEventCounts_(); | |
| 97 }; | |
| 98 | |
| 99 /** | |
| 100 * Called whenever all log events are deleted. | |
| 101 */ | |
| 102 DataView.prototype.onAllSourceEntriesDeleted = function() { | |
| 103 this.updateEventCounts_(); | |
| 104 }; | |
| 105 | |
| 106 /** | |
| 107 * Called when a log file is loaded, after clearing the old log entries and | |
| 108 * loading the new ones. Hides parts of the page related to saving data, and | |
| 109 * shows those related to loading. Returns true to indicate the view should | |
| 110 * still be visible. | |
| 111 */ | |
| 112 DataView.prototype.onLoadLogFinish = function(data) { | |
| 113 setNodeDisplay(this.dumpDataDiv_, false); | |
| 114 setNodeDisplay(this.capturingTextSpan_, false); | |
| 115 setNodeDisplay(this.loggingTextSpan_, true); | |
| 116 setNodeDisplay(this.loadedDiv_, true); | |
| 117 this.updateLoadedClientInfo(); | |
| 118 return true; | |
| 119 }; | |
| 120 | |
| 121 /** | |
| 122 * Updates the counters showing how many events have been captured. | |
| 123 */ | |
| 124 DataView.prototype.updateEventCounts_ = function() { | |
| 125 this.activelyCapturedCountBox_.textContent = | |
| 126 g_browser.sourceTracker.getNumActivelyCapturedEvents(); | |
| 127 this.passivelyCapturedCountBox_.textContent = | |
| 128 g_browser.sourceTracker.getNumPassivelyCapturedEvents(); | |
| 129 }; | |
| 130 | |
| 131 /** | |
| 132 * Depending on the value of the checkbox, enables or disables logging of | |
| 133 * actual bytes transferred. | |
| 134 */ | |
| 135 DataView.prototype.onSetByteLogging_ = function(byteLoggingCheckbox) { | |
| 136 if (byteLoggingCheckbox.checked) { | |
| 137 g_browser.setLogLevel(LogLevelType.LOG_ALL); | |
| 138 } else { | |
| 139 g_browser.setLogLevel(LogLevelType.LOG_ALL_BUT_BYTES); | |
| 140 } | |
| 141 }; | |
| 142 | |
| 143 /** | |
| 144 * Depending on the value of the checkbox, enables or disables stripping | |
| 145 * cookies and passwords from log dumps and displayed events. | |
| 146 */ | |
| 147 DataView.prototype.onSetSecurityStripping_ = | |
| 148 function(securityStrippingCheckbox) { | |
| 149 g_browser.sourceTracker.setSecurityStripping( | |
| 150 securityStrippingCheckbox.checked); | |
| 151 }; | |
| 152 | |
| 153 DataView.prototype.onSecurityStrippingChanged = function() { | |
| 154 }; | |
| 155 | |
| 156 /** | |
| 157 * Called when something is dragged over the drop target. | |
| 158 * | |
| 159 * Returns false to cancel default browser behavior when a single file is being | |
| 160 * dragged. When this happens, we may not receive a list of files for security | |
| 161 * reasons, which is why we allow the |files| array to be empty. | |
| 162 */ | |
| 163 DataView.prototype.onDrag = function(event) { | |
| 164 return event.dataTransfer.types.indexOf('Files') == -1 || | |
| 165 event.dataTransfer.files.length > 1; | |
| 166 }; | |
| 167 | |
| 168 /** | |
| 169 * Called when something is dropped onto the drop target. If it's a single | |
| 170 * file, tries to load it as a log file. | |
| 171 */ | |
| 172 DataView.prototype.onDrop = function(event) { | |
| 173 if (event.dataTransfer.types.indexOf('Files') == -1 || | |
| 174 event.dataTransfer.files.length != 1) { | |
| 175 return; | |
| 176 } | |
| 177 event.preventDefault(); | |
| 178 | |
| 179 // Loading a log file may hide the currently active tab. Switch to the data | |
| 180 // tab to prevent this. | |
| 181 document.location.hash = 'data'; | |
| 182 | |
| 183 this.loadLogFile(event.dataTransfer.files[0]); | |
| 184 }; | |
| 185 | |
| 186 /** | |
| 187 * Called when a log file is selected. | |
| 188 * | |
| 189 * Gets the log file from the input element and tries to read from it. | |
| 190 */ | |
| 191 DataView.prototype.logFileChanged = function() { | |
| 192 this.loadLogFile(this.loadFileElement_.files[0]); | |
| 193 }; | |
| 194 | |
| 195 /** | |
| 196 * Attempts to read from the File |logFile|. | |
| 197 */ | |
| 198 DataView.prototype.loadLogFile = function(logFile) { | |
| 199 if (logFile) { | |
| 200 this.setLoadFileStatus('Loading log...', true); | |
| 201 var fileReader = new FileReader(); | |
| 202 | |
| 203 fileReader.onload = this.onLoadLogFile.bind(this, logFile); | |
| 204 fileReader.onerror = this.onLoadLogFileError.bind(this); | |
| 205 | |
| 206 fileReader.readAsText(logFile); | |
| 207 } | |
| 208 }; | |
| 209 | |
| 210 /** | |
| 211 * Displays an error message when unable to read the selected log file. | |
| 212 * Also clears the file input control, so the same file can be reloaded. | |
| 213 */ | |
| 214 DataView.prototype.onLoadLogFileError = function(event) { | |
| 215 this.loadFileElement_.value = null; | |
| 216 this.setLoadFileStatus( | |
| 217 'Error ' + getKeyWithValue(FileError, event.target.error.code) + | |
| 218 '. Unable to read file.', | |
| 219 false); | |
| 220 }; | |
| 221 | |
| 222 DataView.prototype.onLoadLogFile = function(logFile, event) { | |
| 223 var result = loadLogFile(event.target.result, logFile.fileName); | |
| 224 this.setLoadFileStatus(result, false); | |
| 225 }; | |
| 226 | |
| 227 /** | |
| 228 * Sets the load from file status text, displayed below the load file button, | |
| 229 * to |text|. Also enables or disables the save/load buttons based on the value | |
| 230 * of |isLoading|, which must be true if the load process is still ongoing, and | |
| 231 * false when the operation has stopped, regardless of success of failure. | |
| 232 * Also, when loading is done, replaces the load button so the same file can be | |
| 233 * loaded again. | |
| 234 */ | |
| 235 DataView.prototype.setLoadFileStatus = function(text, isLoading) { | |
| 236 this.enableLoadFileElement_(!isLoading); | |
| 237 this.enableSaveFileButton_(!isLoading); | |
| 238 this.loadStatusText_.textContent = text; | |
| 239 this.saveStatusText_.textContent = ''; | |
| 240 | |
| 241 if (!isLoading) { | |
| 242 // Clear the button, so the same file can be reloaded. Recreating the | |
| 243 // element seems to be the only way to do this. | |
| 244 var loadFileElementId = this.loadFileElement_.id; | |
| 245 var loadFileElementOnChange = this.loadFileElement_.onchange; | |
| 246 this.loadFileElement_.outerHTML = this.loadFileElement_.outerHTML; | |
| 247 this.loadFileElement_ = $(loadFileElementId); | |
| 248 this.loadFileElement_.onchange = loadFileElementOnChange; | |
| 249 } | |
| 250 }; | |
| 251 | |
| 252 /** | |
| 253 * Sets the save to file status text, displayed below the save to file button, | |
| 254 * to |text|. Also enables or disables the save/load buttons based on the value | |
| 255 * of |isSaving|, which must be true if the save process is still ongoing, and | |
| 256 * false when the operation has stopped, regardless of success of failure. | |
| 257 */ | |
| 258 DataView.prototype.setSaveFileStatus = function(text, isSaving) { | |
| 259 this.enableSaveFileButton_(!isSaving); | |
| 260 this.enableLoadFileElement_(!isSaving); | |
| 261 this.loadStatusText_.textContent = ''; | |
| 262 this.saveStatusText_.textContent = text; | |
| 263 }; | |
| 264 | |
| 265 DataView.prototype.enableSaveFileButton_ = function(enabled) { | |
| 266 this.saveFileButton_.disabled = !enabled; | |
| 267 }; | |
| 268 | |
| 269 DataView.prototype.enableLoadFileElement_ = function(enabled) { | |
| 270 this.loadFileElement_.disabled = !enabled; | |
| 271 }; | |
| 272 | |
| 273 /** | |
| 274 * If not already busy loading or saving a log dump, triggers asynchronous | |
| 275 * generation of log dump and starts waiting for it to complete. | |
| 276 */ | |
| 277 DataView.prototype.onSaveFile_ = function() { | |
| 278 if (this.saveFileButton_.disabled) | |
| 279 return; | |
| 280 // Clean up previous blob, if any, to reduce resource usage. | |
| 281 if (this.lastBlobURL_) { | |
| 282 window.webkitURL.revokeObjectURL(this.lastBlobURL_); | |
| 283 this.lastBlobURL_ = null; | |
| 284 } | |
| 285 this.setSaveFileStatus('Preparing data...', true); | |
| 286 | |
| 287 createLogDumpAsync(this.onLogDumpCreated_.bind(this)); | |
| 288 }; | |
| 289 | |
| 290 /** | |
| 291 * Creates a blob url and starts downloading it. | |
| 292 */ | |
| 293 DataView.prototype.onLogDumpCreated_ = function(dumpText) { | |
| 294 var blobBuilder = new WebKitBlobBuilder(); | |
| 295 blobBuilder.append(dumpText, 'native'); | |
| 296 var textBlob = blobBuilder.getBlob('octet/stream'); | |
| 297 this.lastBlobURL_ = window.webkitURL.createObjectURL(textBlob); | |
| 298 this.downloadIframe_.src = this.lastBlobURL_; | |
| 299 this.setSaveFileStatus('Dump successful', false); | |
| 300 }; | |
| 301 | |
| 302 /** | |
| 303 * Prints some basic information about the environment when the log was made. | |
| 304 */ | |
| 305 DataView.prototype.updateLoadedClientInfo = function() { | |
| 306 this.loadedClientInfoText_.textContent = ''; | |
| 307 if (typeof(ClientInfo) != 'object') | |
| 308 return; | |
| 309 | |
| 310 var text = []; | |
| 311 | |
| 312 // Dumps made with the command line option don't have a date. | |
| 313 if (ClientInfo.date) { | |
| 314 text.push('Data exported on: ' + ClientInfo.date); | |
| 315 text.push(''); | |
| 316 } | |
| 317 | |
| 318 text.push(ClientInfo.name + | |
| 319 ' ' + ClientInfo.version + | |
| 320 ' (' + ClientInfo.official + | |
| 321 ' ' + ClientInfo.cl + | |
| 322 ') ' + ClientInfo.version_mod); | |
| 323 text.push('OS Type: ' + ClientInfo.os_type); | |
| 324 text.push('Command line: ' + ClientInfo.command_line); | |
| 325 | |
| 326 this.loadedClientInfoText_.textContent = text.join('\n'); | |
| 327 }; | |
| OLD | NEW |