OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * This view displays options for importing/exporting the captured data. This | 6 * This view displays controls for capturing network events. |
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 | 7 * @constructor |
16 */ | 8 */ |
17 function DataView() { | 9 function CaptureView() { |
18 const mainBoxId = 'dataTabContent'; | 10 const mainBoxId = 'captureTabContent'; |
19 const downloadIframeId = 'dataViewDownloadIframe'; | |
20 const saveFileButtonId = 'dataViewSaveLogFile'; | |
21 const dataViewSaveStatusTextId = 'dataViewSaveStatusText'; | |
22 const securityStrippingCheckboxId = 'securityStrippingCheckbox'; | |
23 const byteLoggingCheckboxId = 'byteLoggingCheckbox'; | 11 const byteLoggingCheckboxId = 'byteLoggingCheckbox'; |
24 const passivelyCapturedCountId = 'passivelyCapturedCount'; | 12 const passivelyCapturedCountId = 'passivelyCapturedCount'; |
25 const activelyCapturedCountId = 'activelyCapturedCount'; | 13 const activelyCapturedCountId = 'activelyCapturedCount'; |
26 const deleteAllId = 'dataViewDeleteAll'; | 14 const deleteAllId = 'dataViewDeleteAll'; |
27 const dumpDataDivId = 'dataViewDumpDataDiv'; | 15 const tipAnchorId = 'captureViewTipAnchor'; |
28 const loadedDivId = 'dataViewLoadedDiv'; | 16 const tipDivId = 'captureViewTipDiv'; |
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 | 17 |
36 DivView.call(this, mainBoxId); | 18 DivView.call(this, mainBoxId); |
37 | 19 |
38 var securityStrippingCheckbox = $(securityStrippingCheckboxId); | |
39 securityStrippingCheckbox.onclick = | |
40 this.onSetSecurityStripping_.bind(this, securityStrippingCheckbox); | |
41 | |
42 var byteLoggingCheckbox = $(byteLoggingCheckboxId); | 20 var byteLoggingCheckbox = $(byteLoggingCheckboxId); |
43 byteLoggingCheckbox.onclick = | 21 byteLoggingCheckbox.onclick = |
44 this.onSetByteLogging_.bind(this, byteLoggingCheckbox); | 22 this.onSetByteLogging_.bind(this, byteLoggingCheckbox); |
45 | 23 |
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); | 24 this.activelyCapturedCountBox_ = $(activelyCapturedCountId); |
53 this.passivelyCapturedCountBox_ = $(passivelyCapturedCountId); | 25 this.passivelyCapturedCountBox_ = $(passivelyCapturedCountId); |
54 $(deleteAllId).onclick = g_browser.sourceTracker.deleteAllSourceEntries.bind( | 26 $(deleteAllId).onclick = g_browser.sourceTracker.deleteAllSourceEntries.bind( |
55 g_browser.sourceTracker); | 27 g_browser.sourceTracker); |
56 | 28 |
57 this.dumpDataDiv_ = $(dumpDataDivId); | 29 $(tipAnchorId).onclick = |
58 this.capturingTextSpan_ = $(capturingTextSpanId); | 30 this.toggleCommandLineTip_.bind(this, tipDivId); |
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 | 31 |
73 this.updateEventCounts_(); | 32 this.updateEventCounts_(); |
74 | 33 |
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); | 34 g_browser.sourceTracker.addObserver(this); |
80 } | 35 } |
81 | 36 |
82 inherits(DataView, DivView); | 37 inherits(CaptureView, DivView); |
83 | 38 |
84 /** | 39 /** |
85 * Called whenever a new event is received. | 40 * Called whenever a new event is received. |
86 */ | 41 */ |
87 DataView.prototype.onSourceEntryUpdated = function(sourceEntry) { | 42 CaptureView.prototype.onSourceEntryUpdated = function(sourceEntry) { |
88 this.updateEventCounts_(); | 43 this.updateEventCounts_(); |
89 }; | 44 }; |
90 | 45 |
91 /** | 46 /** |
| 47 * Toggles the visilibity on the command-line tip. |
| 48 */ |
| 49 CaptureView.prototype.toggleCommandLineTip_ = function(divId) { |
| 50 var n = $(divId); |
| 51 var isVisible = n.style.display != 'none'; |
| 52 setNodeDisplay(n, !isVisible); |
| 53 return false; // Prevent default handling of the click. |
| 54 }; |
| 55 |
| 56 /** |
92 * Called whenever some log events are deleted. |sourceIds| lists | 57 * Called whenever some log events are deleted. |sourceIds| lists |
93 * the source IDs of all deleted log entries. | 58 * the source IDs of all deleted log entries. |
94 */ | 59 */ |
95 DataView.prototype.onSourceEntriesDeleted = function(sourceIds) { | 60 CaptureView.prototype.onSourceEntriesDeleted = function(sourceIds) { |
96 this.updateEventCounts_(); | 61 this.updateEventCounts_(); |
97 }; | 62 }; |
98 | 63 |
99 /** | 64 /** |
100 * Called whenever all log events are deleted. | 65 * Called whenever all log events are deleted. |
101 */ | 66 */ |
102 DataView.prototype.onAllSourceEntriesDeleted = function() { | 67 CaptureView.prototype.onAllSourceEntriesDeleted = function() { |
103 this.updateEventCounts_(); | 68 this.updateEventCounts_(); |
104 }; | 69 }; |
105 | 70 |
106 /** | 71 /** |
107 * Called when a log file is loaded, after clearing the old log entries and | 72 * 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 | 73 * loading the new ones. Returns false to indicate the view should |
109 * shows those related to loading. Returns true to indicate the view should | 74 * be hidden. |
110 * still be visible. | |
111 */ | 75 */ |
112 DataView.prototype.onLoadLogFinish = function(data) { | 76 CaptureView.prototype.onLoadLogFinish = function(data) { |
113 setNodeDisplay(this.dumpDataDiv_, false); | 77 return false; |
114 setNodeDisplay(this.capturingTextSpan_, false); | |
115 setNodeDisplay(this.loggingTextSpan_, true); | |
116 setNodeDisplay(this.loadedDiv_, true); | |
117 this.updateLoadedClientInfo(); | |
118 return true; | |
119 }; | 78 }; |
120 | 79 |
121 /** | 80 /** |
122 * Updates the counters showing how many events have been captured. | 81 * Updates the counters showing how many events have been captured. |
123 */ | 82 */ |
124 DataView.prototype.updateEventCounts_ = function() { | 83 CaptureView.prototype.updateEventCounts_ = function() { |
125 this.activelyCapturedCountBox_.textContent = | 84 this.activelyCapturedCountBox_.textContent = |
126 g_browser.sourceTracker.getNumActivelyCapturedEvents(); | 85 g_browser.sourceTracker.getNumActivelyCapturedEvents(); |
127 this.passivelyCapturedCountBox_.textContent = | 86 this.passivelyCapturedCountBox_.textContent = |
128 g_browser.sourceTracker.getNumPassivelyCapturedEvents(); | 87 g_browser.sourceTracker.getNumPassivelyCapturedEvents(); |
129 }; | 88 }; |
130 | 89 |
131 /** | 90 /** |
132 * Depending on the value of the checkbox, enables or disables logging of | 91 * Depending on the value of the checkbox, enables or disables logging of |
133 * actual bytes transferred. | 92 * actual bytes transferred. |
134 */ | 93 */ |
135 DataView.prototype.onSetByteLogging_ = function(byteLoggingCheckbox) { | 94 CaptureView.prototype.onSetByteLogging_ = function(byteLoggingCheckbox) { |
136 if (byteLoggingCheckbox.checked) { | 95 if (byteLoggingCheckbox.checked) { |
137 g_browser.setLogLevel(LogLevelType.LOG_ALL); | 96 g_browser.setLogLevel(LogLevelType.LOG_ALL); |
138 } else { | 97 } else { |
139 g_browser.setLogLevel(LogLevelType.LOG_ALL_BUT_BYTES); | 98 g_browser.setLogLevel(LogLevelType.LOG_ALL_BUT_BYTES); |
140 } | 99 } |
141 }; | 100 }; |
142 | 101 |
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 |