Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: chrome/browser/resources/net_internals/dataview.js

Issue 7155031: Save net-internals log dumps directly to disk. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Hidden iframe & exportText->exportFile Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome/browser/resources/net_internals/index.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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. Its 6 * This view displays options for importing/exporting the captured data. Its
7 * primarily usefulness is to allow users to copy-paste their data in an easy 7 * primarily usefulness is to allow users to copy-paste their data in an easy
8 * to read format for bug reports. 8 * to read format for bug reports.
9 * 9 *
10 * - Has a button to generate a text report. 10 * - Has a button to generate a text report.
11 * 11 *
12 * - Shows how many events have been captured. 12 * - Shows how many events have been captured.
13 * @constructor 13 * @constructor
14 */ 14 */
15 function DataView(mainBoxId, 15 function DataView(mainBoxId,
16 outputTextBoxId, 16 downloadIframeId,
17 exportTextButtonId, 17 exportFileButtonId,
18 securityStrippingCheckboxId, 18 securityStrippingCheckboxId,
19 byteLoggingCheckboxId, 19 byteLoggingCheckboxId,
20 passivelyCapturedCountId, 20 passivelyCapturedCountId,
21 activelyCapturedCountId, 21 activelyCapturedCountId,
22 deleteAllId, 22 deleteAllId,
23 dumpDataDivId, 23 dumpDataDivId,
24 loadDataDivId, 24 loadDataDivId,
25 loadLogFileId, 25 loadLogFileId,
26 capturingTextSpanId, 26 capturingTextSpanId,
27 loggingTextSpanId) { 27 loggingTextSpanId) {
28 DivView.call(this, mainBoxId); 28 DivView.call(this, mainBoxId);
29 29
30 this.textPre_ = document.getElementById(outputTextBoxId);
31
32 var securityStrippingCheckbox = 30 var securityStrippingCheckbox =
33 document.getElementById(securityStrippingCheckboxId); 31 document.getElementById(securityStrippingCheckboxId);
34 securityStrippingCheckbox.onclick = 32 securityStrippingCheckbox.onclick =
35 this.onSetSecurityStripping_.bind(this, securityStrippingCheckbox); 33 this.onSetSecurityStripping_.bind(this, securityStrippingCheckbox);
36 34
37 var byteLoggingCheckbox = document.getElementById(byteLoggingCheckboxId); 35 var byteLoggingCheckbox = document.getElementById(byteLoggingCheckboxId);
38 byteLoggingCheckbox.onclick = 36 byteLoggingCheckbox.onclick =
39 this.onSetByteLogging_.bind(this, byteLoggingCheckbox); 37 this.onSetByteLogging_.bind(this, byteLoggingCheckbox);
40 38
41 var exportTextButton = document.getElementById(exportTextButtonId); 39 this.downloadIframe_ = document.getElementById(downloadIframeId);
42 exportTextButton.onclick = this.onExportToText_.bind(this); 40
41 this.exportFileButton_ = document.getElementById(exportFileButtonId);
42 this.exportFileButton_.onclick = this.onExportToText_.bind(this);
43 43
44 this.activelyCapturedCountBox_ = 44 this.activelyCapturedCountBox_ =
45 document.getElementById(activelyCapturedCountId); 45 document.getElementById(activelyCapturedCountId);
46 this.passivelyCapturedCountBox_ = 46 this.passivelyCapturedCountBox_ =
47 document.getElementById(passivelyCapturedCountId); 47 document.getElementById(passivelyCapturedCountId);
48 document.getElementById(deleteAllId).onclick = 48 document.getElementById(deleteAllId).onclick =
49 g_browser.deleteAllEvents.bind(g_browser); 49 g_browser.deleteAllEvents.bind(g_browser);
50 50
51 this.dumpDataDiv_ = document.getElementById(dumpDataDivId); 51 this.dumpDataDiv_ = document.getElementById(dumpDataDivId);
52 this.loadDataDiv_ = document.getElementById(loadDataDivId); 52 this.loadDataDiv_ = document.getElementById(loadDataDivId);
53 this.capturingTextSpan_ = document.getElementById(capturingTextSpanId); 53 this.capturingTextSpan_ = document.getElementById(capturingTextSpanId);
54 this.loggingTextSpan_ = document.getElementById(loggingTextSpanId); 54 this.loggingTextSpan_ = document.getElementById(loggingTextSpanId);
55 55
56 var loadLogFileElement = document.getElementById(loadLogFileId); 56 var loadLogFileElement = document.getElementById(loadLogFileId);
57 loadLogFileElement.onchange = 57 loadLogFileElement.onchange =
58 this.logFileChanged.bind(this, loadLogFileElement); 58 this.logFileChanged.bind(this, loadLogFileElement);
59 59
60 this.updateEventCounts_(); 60 this.updateEventCounts_();
61 this.waitingForUpdate_ = false;
62 61
63 g_browser.addLogObserver(this); 62 g_browser.addLogObserver(this);
64 } 63 }
65 64
66 inherits(DataView, DivView); 65 inherits(DataView, DivView);
67 66
68 /** 67 /**
69 * Called whenever a new event is received. 68 * Called whenever a new event is received.
70 */ 69 */
71 DataView.prototype.onLogEntryAdded = function(logEntry) { 70 DataView.prototype.onLogEntryAdded = function(logEntry) {
(...skipping 17 matching lines...) Expand all
89 88
90 /** 89 /**
91 * Called when either a log file is loaded or when going back to actively 90 * Called when either a log file is loaded or when going back to actively
92 * logging events. In either case, called after clearing the old entries, 91 * logging events. In either case, called after clearing the old entries,
93 * but before getting any new ones. 92 * but before getting any new ones.
94 */ 93 */
95 DataView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) { 94 DataView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) {
96 setNodeDisplay(this.dumpDataDiv_, !isViewingLogFile); 95 setNodeDisplay(this.dumpDataDiv_, !isViewingLogFile);
97 setNodeDisplay(this.capturingTextSpan_, !isViewingLogFile); 96 setNodeDisplay(this.capturingTextSpan_, !isViewingLogFile);
98 setNodeDisplay(this.loggingTextSpan_, isViewingLogFile); 97 setNodeDisplay(this.loggingTextSpan_, isViewingLogFile);
99 this.setText_('');
100 }; 98 };
101 99
102 /** 100 /**
103 * Updates the counters showing how many events have been captured. 101 * Updates the counters showing how many events have been captured.
104 */ 102 */
105 DataView.prototype.updateEventCounts_ = function() { 103 DataView.prototype.updateEventCounts_ = function() {
106 this.activelyCapturedCountBox_.innerText = 104 this.activelyCapturedCountBox_.innerText =
107 g_browser.getNumActivelyCapturedEvents() 105 g_browser.getNumActivelyCapturedEvents()
108 this.passivelyCapturedCountBox_.innerText = 106 this.passivelyCapturedCountBox_.innerText =
109 g_browser.getNumPassivelyCapturedEvents(); 107 g_browser.getNumPassivelyCapturedEvents();
(...skipping 13 matching lines...) Expand all
123 121
124 /** 122 /**
125 * Depending on the value of the checkbox, enables or disables stripping 123 * Depending on the value of the checkbox, enables or disables stripping
126 * cookies and passwords from log dumps and displayed events. 124 * cookies and passwords from log dumps and displayed events.
127 */ 125 */
128 DataView.prototype.onSetSecurityStripping_ = 126 DataView.prototype.onSetSecurityStripping_ =
129 function(securityStrippingCheckbox) { 127 function(securityStrippingCheckbox) {
130 g_browser.setSecurityStripping(securityStrippingCheckbox.checked); 128 g_browser.setSecurityStripping(securityStrippingCheckbox.checked);
131 }; 129 };
132 130
133 /**
134 * Clears displayed text when security stripping is toggled.
135 */
136 DataView.prototype.onSecurityStrippingChanged = function() { 131 DataView.prototype.onSecurityStrippingChanged = function() {
137 this.setText_(''); 132 };
138 }
139 133
140 /** 134 /**
141 * Called when a log file is selected. 135 * Called when a log file is selected.
142 * 136 *
143 * Gets the log file from the input element and tries to read from it. 137 * Gets the log file from the input element and tries to read from it.
144 */ 138 */
145 DataView.prototype.logFileChanged = function(loadLogFileElement) { 139 DataView.prototype.logFileChanged = function(loadLogFileElement) {
146 var logFile = loadLogFileElement.files[0]; 140 var logFile = loadLogFileElement.files[0];
147 if (logFile) { 141 if (logFile) {
148 var fileReader = new FileReader(); 142 var fileReader = new FileReader();
149 143
150 fileReader.onload = this.onLoadLogFile.bind(this); 144 fileReader.onload = this.onLoadLogFile.bind(this);
151 fileReader.onerror = this.onLoadLogFileError.bind(this); 145 fileReader.onerror = this.onLoadLogFileError.bind(this);
152 146
153 fileReader.readAsText(logFile); 147 fileReader.readAsText(logFile);
154 } 148 }
155 } 149 };
156 150
157 /** 151 /**
158 * Displays an error message when unable to read the selected log file. 152 * Displays an error message when unable to read the selected log file.
159 */ 153 */
160 DataView.prototype.onLoadLogFileError = function(event) { 154 DataView.prototype.onLoadLogFileError = function(event) {
161 alert('Error ' + event.target.error.code + '. Unable to load file.'); 155 alert('Error ' + event.target.error.code + '. Unable to load file.');
162 } 156 };
163 157
164 /** 158 /**
165 * Tries to load the contents of the log file. 159 * Tries to load the contents of the log file.
166 */ 160 */
167 DataView.prototype.onLoadLogFile = function(event) { 161 DataView.prototype.onLoadLogFile = function(event) {
168 g_browser.loadedLogFile(event.target.result); 162 g_browser.loadedLogFile(event.target.result);
169 } 163 };
164
165 DataView.prototype.enableExportFileButton_ = function(enabled) {
166 this.exportFileButton_.disabled = !enabled;
167 };
170 168
171 /** 169 /**
172 * If not already waiting for results from all updates, triggers all 170 * If not already waiting for results from all updates, triggers all
173 * updates and starts waiting for them to complete. 171 * updates and starts waiting for them to complete.
174 */ 172 */
175 DataView.prototype.onExportToText_ = function() { 173 DataView.prototype.onExportToText_ = function() {
176 if (this.waitingForUpdate_) 174 if (this.exportFileButton_.disabled)
177 return; 175 return;
178 this.waitingForUpdate = true; 176 this.enableExportFileButton_(false);
179 this.setText_('Generating...'); 177
180 g_browser.updateAllInfo(this.onUpdateAllCompleted.bind(this)); 178 g_browser.updateAllInfo(this.onUpdateAllCompleted.bind(this));
181 }; 179 };
182 180
183 /** 181 /**
184 * Presents the captured data as formatted text. 182 * Presents the captured data as formatted text.
185 */ 183 */
186 DataView.prototype.onUpdateAllCompleted = function(data) { 184 DataView.prototype.onUpdateAllCompleted = function(data) {
187 // It's possible for a log file to be loaded while a dump is being generated. 185 // It's possible for a log file to be loaded while a dump is being generated.
188 // When that happens, don't display the log dump, to avoid any confusion. 186 // When that happens, don't display the log dump, to avoid any confusion.
189 if (g_browser.isViewingLogFile()) 187 if (g_browser.isViewingLogFile())
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
397 var provider = namespaceProviders[i]; 395 var provider = namespaceProviders[i];
398 text.push('name: ' + provider.name); 396 text.push('name: ' + provider.name);
399 text.push('version: ' + provider.version); 397 text.push('version: ' + provider.version);
400 text.push('type: ' + 398 text.push('type: ' +
401 ServiceProvidersView.getNamespaceProviderType(provider)); 399 ServiceProvidersView.getNamespaceProviderType(provider));
402 text.push('active: ' + provider.active); 400 text.push('active: ' + provider.active);
403 text.push(''); 401 text.push('');
404 } 402 }
405 } 403 }
406 404
407 // Open a new window to display this text. 405 var flatText = text.join('\n');
408 this.setText_(text.join('\n')); 406 // Use Windows-style line breaks under Windows, so text file can be viewed
407 // with notepad.
408 if (g_browser.isPlatformWindows())
409 flatText.replace(/\n/g, '\r\n');
eroman 2011/06/16 19:16:45 I suspect the file API might have something simila
mmenke 2011/06/16 19:23:55 Specs don't seem to mention it. I could see it us
kinuko 2011/06/17 08:00:16 BlobBuilder has some support for line-ending conve
mmenke 2011/06/17 13:45:59 Tried it, and it works.
409 410
410 this.selectText_(); 411 var blobBuilder = new WebKitBlobBuilder();
412 blobBuilder.append(flatText);
413 var textBlob = blobBuilder.getBlob('octet/stream');
414
415 window.webkitRequestFileSystem(
416 window.TEMPORARY, 10*1024 + textBlob.size,
eroman 2011/06/16 19:16:45 what is the 10kb padding for?
mmenke 2011/06/16 19:23:55 The file API doesn't specify what the size actuall
kinuko 2011/06/17 08:00:16 For now this 'size' has no significant meaning but
mmenke 2011/06/17 13:45:59 Ok. I'll just go ahead and remove the 10 kB, then
417 this.onFileSystemCreate_.bind(this, textBlob),
418 this.onFileError_.bind(this, 'Unable to create file system.'));
419 };
420
421 // Once we have access to the file system, create a log file.
422 DataView.prototype.onFileSystemCreate_ = function(textBlob, fileSystem) {
423 fileSystem.root.getFile(
424 'net_internals_log.txt', {create: true},
425 this.onFileCreate_.bind(this, textBlob),
426 this.onFileError_.bind(this, 'Unable to create file.'));
427 };
428
429 // Once the file is created, or an existing one has been opened, create a
430 // writer for it.
431 DataView.prototype.onFileCreate_ = function(textBlob, fileEntry) {
432 fileEntry.createWriter(
433 this.onFileCreateWriter_.bind(this, textBlob, fileEntry),
434 this.onFileError_.bind(this, 'Unable to create writer.'));
435 };
436
437 // Once the |fileWriter| has been created, truncate the file, in case it already
438 // existed.
439 DataView.prototype.onFileCreateWriter_ = function(textBlob,
440 fileEntry, fileWriter) {
441 fileWriter.onerror = this.onFileError_.bind(this, 'Truncate failed.');
442 fileWriter.onwriteend = this.onFileTruncate_.bind(this, textBlob,
443 fileWriter, fileEntry);
444 fileWriter.truncate(0);
445 };
446
447 // Once the file has been truncated, write |textBlob| to the file.
448 DataView.prototype.onFileTruncate_ = function(textBlob, fileWriter, fileEntry) {
449 fileWriter.onerror = this.onFileError_.bind(this, 'Write failed.');
450 fileWriter.onwriteend = this.onFileWriteComplete_.bind(this, fileEntry);
451 fileWriter.write(textBlob);
452 };
453
454 // Once the file has been written to, start the download.
455 DataView.prototype.onFileWriteComplete_ = function(fileEntry) {
456 this.downloadIframe_.src = fileEntry.toURL();
457 this.enableExportFileButton_(true);
458 };
459
460 // On any Javascript File API error, enable the export button and display
461 // |errorText|, followed by the specific error.
462 DataView.prototype.onFileError_ = function(errorText, error) {
463 this.enableExportFileButton_(true);
464 alert(errorText + ' ' + getKeyWithValue(FileError, error.code));
411 }; 465 };
412 466
413 DataView.prototype.appendEventsPrintedAsText_ = function(out) { 467 DataView.prototype.appendEventsPrintedAsText_ = function(out) {
414 var allEvents = g_browser.getAllCapturedEvents(); 468 var allEvents = g_browser.getAllCapturedEvents();
415 469
416 // Group the events into buckets by source ID, and buckets by source type. 470 // Group the events into buckets by source ID, and buckets by source type.
417 var sourceIds = []; 471 var sourceIds = [];
418 var sourceIdToEventList = {}; 472 var sourceIdToEventList = {};
419 var sourceTypeToSourceIdList = {}; 473 var sourceTypeToSourceIdList = {};
420 474
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 527
474 for (var i = 0; i < socketPools.length; ++i) { 528 for (var i = 0; i < socketPools.length; ++i) {
475 if (socketPools[i].origPool.groups == undefined) 529 if (socketPools[i].origPool.groups == undefined)
476 continue; 530 continue;
477 var groupTablePrinter = socketPools[i].createGroupTablePrinter(); 531 var groupTablePrinter = socketPools[i].createGroupTablePrinter();
478 text.push(groupTablePrinter.toText(2)); 532 text.push(groupTablePrinter.toText(2));
479 } 533 }
480 }; 534 };
481 535
482 /** 536 /**
483 * Helper function to set this view's content to |text|.
484 */
485 DataView.prototype.setText_ = function(text) {
486 this.textPre_.innerHTML = '';
487 addTextNode(this.textPre_, text);
488 };
489
490 /**
491 * Format a time ticks count as a timestamp. 537 * Format a time ticks count as a timestamp.
492 */ 538 */
493 DataView.prototype.formatExpirationTime_ = function(timeTicks) { 539 DataView.prototype.formatExpirationTime_ = function(timeTicks) {
494 var d = g_browser.convertTimeTicksToDate(timeTicks); 540 var d = g_browser.convertTimeTicksToDate(timeTicks);
495 var isExpired = d.getTime() < (new Date()).getTime(); 541 var isExpired = d.getTime() < (new Date()).getTime();
496 return 't=' + d.getTime() + (isExpired ? ' [EXPIRED]' : ''); 542 return 't=' + d.getTime() + (isExpired ? ' [EXPIRED]' : '');
497 }; 543 };
498
499 /**
500 * Select all text from log dump.
501 */
502 DataView.prototype.selectText_ = function() {
503 var selection = window.getSelection();
504 selection.removeAllRanges();
505
506 var range = document.createRange();
507 range.selectNodeContents(this.textPre_);
508 selection.addRange(range);
509 };
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/net_internals/index.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698