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 options for importing data from a log file. |
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 ImportView() { |
18 const mainBoxId = 'dataTabContent'; | 10 const mainBoxId = 'importTabContent'; |
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'; | 11 const loadedDivId = 'dataViewLoadedDiv'; |
29 const loadedClientInfoTextId = 'dataViewLoadedClientInfoText'; | 12 const loadedClientInfoTextId = 'dataViewLoadedClientInfoText'; |
30 const loadLogFileDropTargetId = 'dataViewDropTarget'; | 13 const loadLogFileDropTargetId = 'dataViewDropTarget'; |
31 const loadLogFileId = 'dataViewLoadLogFile'; | 14 const loadLogFileId = 'dataViewLoadLogFile'; |
32 const dataViewLoadStatusTextId = 'dataViewLoadStatusText'; | 15 const dataViewLoadStatusTextId = 'dataViewLoadStatusText'; |
33 const capturingTextSpanId = 'dataViewCapturingTextSpan'; | |
34 const loggingTextSpanId = 'dataViewLoggingTextSpan'; | |
35 | 16 |
36 DivView.call(this, mainBoxId); | 17 DivView.call(this, mainBoxId); |
37 | 18 |
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 | 19 |
61 this.loadedDiv_ = $(loadedDivId); | 20 this.loadedDiv_ = $(loadedDivId); |
62 this.loadedClientInfoText_ = $(loadedClientInfoTextId); | 21 this.loadedClientInfoText_ = $(loadedClientInfoTextId); |
63 | 22 |
64 this.loadFileElement_ = $(loadLogFileId); | 23 this.loadFileElement_ = $(loadLogFileId); |
65 this.loadFileElement_.onchange = this.logFileChanged.bind(this); | 24 this.loadFileElement_.onchange = this.logFileChanged.bind(this); |
66 this.loadStatusText_ = $(dataViewLoadStatusTextId); | 25 this.loadStatusText_ = $(dataViewLoadStatusTextId); |
67 | 26 |
68 var dropTarget = $(loadLogFileDropTargetId); | 27 var dropTarget = $(loadLogFileDropTargetId); |
69 dropTarget.ondragenter = this.onDrag.bind(this); | 28 dropTarget.ondragenter = this.onDrag.bind(this); |
70 dropTarget.ondragover = this.onDrag.bind(this); | 29 dropTarget.ondragover = this.onDrag.bind(this); |
71 dropTarget.ondrop = this.onDrop.bind(this); | 30 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 } | 31 } |
81 | 32 |
82 inherits(DataView, DivView); | 33 inherits(ImportView, 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 | 34 |
106 /** | 35 /** |
107 * Called when a log file is loaded, after clearing the old log entries and | 36 * 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 | 37 * loading the new ones. Returns true to indicate the view should |
109 * shows those related to loading. Returns true to indicate the view should | |
110 * still be visible. | 38 * still be visible. |
111 */ | 39 */ |
112 DataView.prototype.onLoadLogFinish = function(data) { | 40 ImportView.prototype.onLoadLogFinish = function(data) { |
113 setNodeDisplay(this.dumpDataDiv_, false); | |
114 setNodeDisplay(this.capturingTextSpan_, false); | |
115 setNodeDisplay(this.loggingTextSpan_, true); | |
116 setNodeDisplay(this.loadedDiv_, true); | 41 setNodeDisplay(this.loadedDiv_, true); |
117 this.updateLoadedClientInfo(); | 42 this.updateLoadedClientInfo(); |
118 return true; | 43 return true; |
119 }; | 44 }; |
120 | 45 |
121 /** | 46 /** |
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. | 47 * Called when something is dragged over the drop target. |
158 * | 48 * |
159 * Returns false to cancel default browser behavior when a single file is being | 49 * 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 | 50 * 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. | 51 * reasons, which is why we allow the |files| array to be empty. |
162 */ | 52 */ |
163 DataView.prototype.onDrag = function(event) { | 53 ImportView.prototype.onDrag = function(event) { |
164 return event.dataTransfer.types.indexOf('Files') == -1 || | 54 return event.dataTransfer.types.indexOf('Files') == -1 || |
165 event.dataTransfer.files.length > 1; | 55 event.dataTransfer.files.length > 1; |
166 }; | 56 }; |
167 | 57 |
168 /** | 58 /** |
169 * Called when something is dropped onto the drop target. If it's a single | 59 * Called when something is dropped onto the drop target. If it's a single |
170 * file, tries to load it as a log file. | 60 * file, tries to load it as a log file. |
171 */ | 61 */ |
172 DataView.prototype.onDrop = function(event) { | 62 ImportView.prototype.onDrop = function(event) { |
173 if (event.dataTransfer.types.indexOf('Files') == -1 || | 63 if (event.dataTransfer.types.indexOf('Files') == -1 || |
174 event.dataTransfer.files.length != 1) { | 64 event.dataTransfer.files.length != 1) { |
175 return; | 65 return; |
176 } | 66 } |
177 event.preventDefault(); | 67 event.preventDefault(); |
178 | 68 |
179 // Loading a log file may hide the currently active tab. Switch to the data | 69 // Loading a log file may hide the currently active tab. Switch to the import |
180 // tab to prevent this. | 70 // tab to prevent this. |
181 document.location.hash = 'data'; | 71 document.location.hash = 'import'; |
182 | 72 |
183 this.loadLogFile(event.dataTransfer.files[0]); | 73 this.loadLogFile(event.dataTransfer.files[0]); |
184 }; | 74 }; |
185 | 75 |
186 /** | 76 /** |
187 * Called when a log file is selected. | 77 * Called when a log file is selected. |
188 * | 78 * |
189 * Gets the log file from the input element and tries to read from it. | 79 * Gets the log file from the input element and tries to read from it. |
190 */ | 80 */ |
191 DataView.prototype.logFileChanged = function() { | 81 ImportView.prototype.logFileChanged = function() { |
192 this.loadLogFile(this.loadFileElement_.files[0]); | 82 this.loadLogFile(this.loadFileElement_.files[0]); |
193 }; | 83 }; |
194 | 84 |
195 /** | 85 /** |
196 * Attempts to read from the File |logFile|. | 86 * Attempts to read from the File |logFile|. |
197 */ | 87 */ |
198 DataView.prototype.loadLogFile = function(logFile) { | 88 ImportView.prototype.loadLogFile = function(logFile) { |
199 if (logFile) { | 89 if (logFile) { |
200 this.setLoadFileStatus('Loading log...', true); | 90 this.setLoadFileStatus('Loading log...', true); |
201 var fileReader = new FileReader(); | 91 var fileReader = new FileReader(); |
202 | 92 |
203 fileReader.onload = this.onLoadLogFile.bind(this, logFile); | 93 fileReader.onload = this.onLoadLogFile.bind(this, logFile); |
204 fileReader.onerror = this.onLoadLogFileError.bind(this); | 94 fileReader.onerror = this.onLoadLogFileError.bind(this); |
205 | 95 |
206 fileReader.readAsText(logFile); | 96 fileReader.readAsText(logFile); |
207 } | 97 } |
208 }; | 98 }; |
209 | 99 |
210 /** | 100 /** |
211 * Displays an error message when unable to read the selected log file. | 101 * 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. | 102 * Also clears the file input control, so the same file can be reloaded. |
213 */ | 103 */ |
214 DataView.prototype.onLoadLogFileError = function(event) { | 104 ImportView.prototype.onLoadLogFileError = function(event) { |
215 this.loadFileElement_.value = null; | 105 this.loadFileElement_.value = null; |
216 this.setLoadFileStatus( | 106 this.setLoadFileStatus( |
217 'Error ' + getKeyWithValue(FileError, event.target.error.code) + | 107 'Error ' + getKeyWithValue(FileError, event.target.error.code) + |
218 '. Unable to read file.', | 108 '. Unable to read file.', |
219 false); | 109 false); |
220 }; | 110 }; |
221 | 111 |
222 DataView.prototype.onLoadLogFile = function(logFile, event) { | 112 ImportView.prototype.onLoadLogFile = function(logFile, event) { |
223 var result = loadLogFile(event.target.result, logFile.fileName); | 113 var result = loadLogFile(event.target.result, logFile.fileName); |
224 this.setLoadFileStatus(result, false); | 114 this.setLoadFileStatus(result, false); |
225 }; | 115 }; |
226 | 116 |
227 /** | 117 /** |
228 * Sets the load from file status text, displayed below the load file button, | 118 * 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 | 119 * to |text|. Also enables or disables the load buttons based on the value |
230 * of |isLoading|, which must be true if the load process is still ongoing, and | 120 * 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. | 121 * 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 | 122 * Also, when loading is done, replaces the load button so the same file can be |
233 * loaded again. | 123 * loaded again. |
234 */ | 124 */ |
235 DataView.prototype.setLoadFileStatus = function(text, isLoading) { | 125 ImportView.prototype.setLoadFileStatus = function(text, isLoading) { |
236 this.enableLoadFileElement_(!isLoading); | 126 this.enableLoadFileElement_(!isLoading); |
237 this.enableSaveFileButton_(!isLoading); | |
238 this.loadStatusText_.textContent = text; | 127 this.loadStatusText_.textContent = text; |
239 this.saveStatusText_.textContent = ''; | |
240 | 128 |
241 if (!isLoading) { | 129 if (!isLoading) { |
242 // Clear the button, so the same file can be reloaded. Recreating the | 130 // Clear the button, so the same file can be reloaded. Recreating the |
243 // element seems to be the only way to do this. | 131 // element seems to be the only way to do this. |
244 var loadFileElementId = this.loadFileElement_.id; | 132 var loadFileElementId = this.loadFileElement_.id; |
245 var loadFileElementOnChange = this.loadFileElement_.onchange; | 133 var loadFileElementOnChange = this.loadFileElement_.onchange; |
246 this.loadFileElement_.outerHTML = this.loadFileElement_.outerHTML; | 134 this.loadFileElement_.outerHTML = this.loadFileElement_.outerHTML; |
247 this.loadFileElement_ = $(loadFileElementId); | 135 this.loadFileElement_ = $(loadFileElementId); |
248 this.loadFileElement_.onchange = loadFileElementOnChange; | 136 this.loadFileElement_.onchange = loadFileElementOnChange; |
249 } | 137 } |
250 }; | 138 }; |
251 | 139 |
252 /** | 140 ImportView.prototype.enableLoadFileElement_ = function(enabled) { |
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; | 141 this.loadFileElement_.disabled = !enabled; |
271 }; | 142 }; |
272 | 143 |
273 /** | 144 /** |
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. | 145 * Prints some basic information about the environment when the log was made. |
304 */ | 146 */ |
305 DataView.prototype.updateLoadedClientInfo = function() { | 147 ImportView.prototype.updateLoadedClientInfo = function() { |
306 this.loadedClientInfoText_.textContent = ''; | 148 this.loadedClientInfoText_.textContent = ''; |
307 if (typeof(ClientInfo) != 'object') | 149 if (typeof(ClientInfo) != 'object') |
308 return; | 150 return; |
309 | 151 |
310 var text = []; | 152 var text = []; |
311 | 153 |
312 // Dumps made with the command line option don't have a date. | 154 // Dumps made with the command line option don't have a date. |
313 if (ClientInfo.date) { | 155 if (ClientInfo.date) { |
314 text.push('Data exported on: ' + ClientInfo.date); | 156 text.push('Data exported on: ' + ClientInfo.date); |
315 text.push(''); | 157 text.push(''); |
316 } | 158 } |
317 | 159 |
318 text.push(ClientInfo.name + | 160 text.push(ClientInfo.name + |
319 ' ' + ClientInfo.version + | 161 ' ' + ClientInfo.version + |
320 ' (' + ClientInfo.official + | 162 ' (' + ClientInfo.official + |
321 ' ' + ClientInfo.cl + | 163 ' ' + ClientInfo.cl + |
322 ') ' + ClientInfo.version_mod); | 164 ') ' + ClientInfo.version_mod); |
323 text.push('OS Type: ' + ClientInfo.os_type); | 165 text.push('OS Type: ' + ClientInfo.os_type); |
324 text.push('Command line: ' + ClientInfo.command_line); | 166 text.push('Command line: ' + ClientInfo.command_line); |
325 | 167 |
326 this.loadedClientInfoText_.textContent = text.join('\n'); | 168 this.loadedClientInfoText_.textContent = text.join('\n'); |
327 }; | 169 }; |
OLD | NEW |