torLogDialog.js (3921B)
1 "use strict"; 2 3 const { setTimeout, clearTimeout } = ChromeUtils.importESModule( 4 "resource://gre/modules/Timer.sys.mjs" 5 ); 6 7 const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule( 8 "resource://gre/modules/TorProviderBuilder.sys.mjs" 9 ); 10 11 const gTorLogDialog = { 12 init() { 13 const dialog = document.getElementById("torPreferences-torLog-dialog"); 14 const copyLogButton = dialog.getButton("extra1"); 15 copyLogButton.setAttribute("data-l10n-id", "tor-log-dialog-copy-button"); 16 17 this._logTable = document.getElementById("tor-log-table"); 18 this._logBody = document.getElementById("tor-log-body"); 19 20 let restoreButtonTimeout = null; 21 copyLogButton.addEventListener("command", () => { 22 // Copy tor log messages to the system clipboard. 23 let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( 24 Ci.nsIClipboardHelper 25 ); 26 // The copied text should match the text content the user would get if 27 // they hand-selected the entire table. 28 clipboard.copyString( 29 Array.from( 30 this._logTable.querySelectorAll("td"), 31 el => el.textContent 32 ).join("\n") 33 ); 34 35 copyLogButton.setAttribute( 36 "data-l10n-id", 37 "tor-log-dialog-copy-button-copied" 38 ); 39 copyLogButton.classList.add("primary"); 40 41 const RESTORE_TIME = 1200; 42 if (restoreButtonTimeout !== null) { 43 clearTimeout(restoreButtonTimeout); 44 } 45 restoreButtonTimeout = setTimeout(() => { 46 copyLogButton.setAttribute( 47 "data-l10n-id", 48 "tor-log-dialog-copy-button" 49 ); 50 copyLogButton.classList.remove("primary"); 51 restoreButtonTimeout = null; 52 }, RESTORE_TIME); 53 }); 54 55 // Intercept the copy event. 56 // NOTE: We attach this to the window rather than the _logTable because if 57 // the whole table is selected it will not receive the "copy" event. 58 window.addEventListener("copy", event => { 59 event.preventDefault(); 60 event.clipboardData.setData( 61 "text", 62 // By default the selected text will insert "\n\t" between the <td> 63 // elements, which separates the timestamp from the message column. 64 // We drop this "\t" character, to just keep the "\n". 65 window.getSelection().toString().replace(/^\t/gm, "") 66 ); 67 }); 68 69 Services.obs.addObserver(this, TorProviderTopics.TorLog); 70 window.addEventListener( 71 "unload", 72 () => { 73 Services.obs.removeObserver(this, TorProviderTopics.TorLog); 74 }, 75 { once: true } 76 ); 77 78 for (const logEntry of TorProviderBuilder.getLog()) { 79 this.addLogEntry(logEntry, true); 80 } 81 // Set the initial scroll to the bottom. 82 this._logTable.scrollTo({ 83 top: this._logTable.scrollTopMax, 84 behaviour: "instant", 85 }); 86 }, 87 88 observe(subject, topic) { 89 if (topic === TorProviderTopics.TorLog) { 90 this.addLogEntry(subject.wrappedJSObject, false); 91 } 92 }, 93 94 addLogEntry(logEntry, initial) { 95 const timeEl = document.createElement("td"); 96 timeEl.textContent = logEntry.timestamp; 97 timeEl.classList.add("time"); 98 const messageEl = document.createElement("td"); 99 messageEl.textContent = `[${logEntry.type}] ${logEntry.msg}`; 100 messageEl.classList.add("message"); 101 102 const row = document.createElement("tr"); 103 row.append(timeEl, messageEl); 104 105 // If this is a new entry, and we are currently scrolled to the bottom (with 106 // a 6px allowance) we keep the scroll position at the bottom to "follow" 107 // the updates. 108 const scrollToBottom = 109 !initial && this._logTable.scrollTop >= this._logTable.scrollTopMax - 6; 110 111 this._logBody.append(row); 112 if (scrollToBottom) { 113 this._logTable.scrollTo({ top: this._logTable.scrollTopMax }); 114 } 115 }, 116 }; 117 118 window.addEventListener( 119 "DOMContentLoaded", 120 () => { 121 gTorLogDialog.init(); 122 }, 123 { once: true } 124 );