morgan pushed to branch tor-browser-128.7.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits: 8e663084 by Henry Wilkes at 2025-02-20T17:30:12+00:00 fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
TB 43328: Improve the Tor log dialog.
- - - - - 0cc2f849 by Henry Wilkes at 2025-02-20T17:38:02+00:00 fixup! TB 40933: Add tor-launcher functionality
TB 43328: Make getLog return the LogEntry data.
- - - - - 21c61532 by Henry Wilkes at 2025-02-20T17:38:03+00:00 fixup! Tor Browser strings
TB 43328: Improve the Tor log.
- - - - -
5 changed files:
- browser/components/torpreferences/content/torLogDialog.js - browser/components/torpreferences/content/torLogDialog.xhtml - browser/components/torpreferences/content/torPreferences.css - toolkit/components/tor-launcher/TorProvider.sys.mjs - toolkit/locales/en-US/toolkit/global/tor-browser.ftl
Changes:
===================================== browser/components/torpreferences/content/torLogDialog.js ===================================== @@ -4,20 +4,18 @@ const { setTimeout, clearTimeout } = ChromeUtils.importESModule( "resource://gre/modules/Timer.sys.mjs" );
-const { TorProviderBuilder } = ChromeUtils.importESModule( +const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule( "resource://gre/modules/TorProviderBuilder.sys.mjs" );
-window.addEventListener( - "DOMContentLoaded", - () => { +const gTorLogDialog = { + init() { const dialog = document.getElementById("torPreferences-torLog-dialog"); const copyLogButton = dialog.getButton("extra1"); copyLogButton.setAttribute("data-l10n-id", "tor-log-dialog-copy-button");
- const logText = document.getElementById( - "torPreferences-torDialog-textarea" - ); + this._logTable = document.getElementById("tor-log-table"); + this._logBody = document.getElementById("tor-log-body");
let restoreButtonTimeout = null; copyLogButton.addEventListener("command", () => { @@ -25,7 +23,14 @@ window.addEventListener( let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( Ci.nsIClipboardHelper ); - clipboard.copyString(logText.value); + // The copied text should match the text content the user would get if + // they hand-selected the entire table. + clipboard.copyString( + Array.from( + this._logTable.querySelectorAll("td"), + el => el.textContent + ).join("\n") + );
copyLogButton.setAttribute( "data-l10n-id", @@ -47,13 +52,79 @@ window.addEventListener( }, RESTORE_TIME); });
+ // Intercept the copy event. + // NOTE: We attach this to the window rather than the _logTable because if + // the whole table is selected it will not receive the "copy" event. + window.addEventListener("copy", event => { + event.preventDefault(); + event.clipboardData.setData( + "text", + // By default the selected text will insert "\n\t" between the <td> + // elements, which separates the timestamp from the message column. + // We drop this "\t" character, to just keep the "\n". + window.getSelection().toString().replace(/^\t/gm, "") + ); + }); + // A waiting state should not be needed at this point. // Also, we probably cannot even arrive here if the provider failed to // initialize, otherwise we could use a try/catch, and write the exception // text in the logs, instead. - TorProviderBuilder.build().then( - provider => (logText.value = provider.getLog()) - ); + TorProviderBuilder.build().then(provider => { + Services.obs.addObserver(this, TorProviderTopics.TorLog); + window.addEventListener( + "unload", + () => { + Services.obs.removeObserver(this, TorProviderTopics.TorLog); + }, + { once: true } + ); + + for (const logEntry of provider.getLog()) { + this.addLogEntry(logEntry, true); + } + // Set the initial scroll to the bottom. + this._logTable.scrollTo({ + top: this._logTable.scrollTopMax, + behaviour: "instant", + }); + }); + }, + + observe(subject, topic) { + if (topic === TorProviderTopics.TorLog) { + this.addLogEntry(subject.wrappedJSObject, false); + } + }, + + addLogEntry(logEntry, initial) { + const timeEl = document.createElement("td"); + timeEl.textContent = logEntry.timestamp; + timeEl.classList.add("time"); + const messageEl = document.createElement("td"); + messageEl.textContent = `[${logEntry.type}] ${logEntry.msg}`; + messageEl.classList.add("message"); + + const row = document.createElement("tr"); + row.append(timeEl, messageEl); + + // If this is a new entry, and we are currently scrolled to the bottom (with + // a 6px allowance) we keep the scroll position at the bottom to "follow" + // the updates. + const scrollToBottom = + !initial && this._logTable.scrollTop >= this._logTable.scrollTopMax - 6; + + this._logBody.append(row); + if (scrollToBottom) { + this._logTable.scrollTo({ top: this._logTable.scrollTopMax }); + } + }, +}; + +window.addEventListener( + "DOMContentLoaded", + () => { + gTorLogDialog.init(); }, { once: true } );
===================================== browser/components/torpreferences/content/torLogDialog.xhtml ===================================== @@ -23,10 +23,33 @@
<script src="chrome://browser/content/torpreferences/torLogDialog.js" />
- <html:textarea - id="torPreferences-torDialog-textarea" - multiline="true" - readonly="true" - /> + <!-- We use a <table> element rather than a <ol>. A table structure allows + - screen reader users to navigate within one column so they can avoid the + - readback of the timestamp on every row. See tor-browser#43328. + - NOTE: We add the explicit role="table". Whilst this should not be + - neccessary, nor is it recommended, some screen readers (Orca 46) do not + - read out the default table role if the CSS `display` is not `table`. + - NOTE: Even though this table is updated with live information, we do + - not want to make this an aria-live area or use a "log updated" + - notification because the log messages are potentially busy. + - Moreover, the live updates is a convience so that the log doesn't need + - to be manually refreshed, rather than important information. + - NOTE: We add a tabindex=0 to make this element focusable with or + - without the overflow. This also makes the table the the initial focus + - of the dialog. + - NOTE: We add lang="en" and dir="ltr" to the <tbody> since the content + - of the log is in English. We do not add this to the <table> element + - since its aria-label is localised and we want the scrollbar to match + - the locale direction. + - NOTE: We avoid any whitespace between the <table> and <tbody> to ensure + - it does not contribute to the text content when the top of the table is + - manually copied. --> + <html:table + id="tor-log-table" + role="table" + data-l10n-id="tor-log-dialog-table" + tabindex="0" + ><html:tbody id="tor-log-body" lang="en" dir="ltr"></html:tbody + ></html:table> </dialog> </window>
===================================== browser/components/torpreferences/content/torPreferences.css ===================================== @@ -1058,12 +1058,39 @@ groupbox#torPreferences-bridges-group textarea { }
/* Tor logs dialog */ -textarea#torPreferences-torDialog-textarea { +#tor-log-table { flex: 1 0 auto; - font-family: monospace; - font-size: 0.8em; - white-space: pre; overflow: auto; - /* 10 lines */ min-height: 20em; + height: 20em; + display: flex; + flex-direction: column; + padding: var(--space-small); + margin-block-end: 4px; + border: 1px solid var(--in-content-box-border-color); + border-radius: var(--border-radius-small); + font-size: var(--font-size-small); +} + +#tor-log-body, +#tor-log-table tr { + display: contents; +} + +#tor-log-table td { + flex: 0 0 auto; + padding: 0; +} + +#tor-log-table td.time { + color: var(--text-color-deemphasized); + margin-block-end: var(--space-xsmall); +} + +#tor-log-table td.message { + overflow-wrap: anywhere; +} + +#tor-log-table tr:not(:last-of-type) td.message { + margin-block-end: var(--space-medium); }
===================================== toolkit/components/tor-launcher/TorProvider.sys.mjs ===================================== @@ -512,14 +512,12 @@ export class TorProvider { }
/** - * Returns captured log message as a text string (one message per line). + * Returns captured log messages. * - * @returns {string} The logs we collected from the tor daemon so far + * @returns {LogEntry[]} The logs we collected from the tor daemon so far. */ getLog() { - return this.#logs - .map(logObj => `${logObj.timestamp} [${logObj.type}] ${logObj.msg}`) - .join(TorLauncherUtil.isWindows ? "\r\n" : "\n"); + return structuredClone(this.#logs); }
/**
===================================== toolkit/locales/en-US/toolkit/global/tor-browser.ftl ===================================== @@ -423,6 +423,9 @@ tor-view-log-button = View log… # "log" is a noun, referring to the recorded text output of the Tor process. tor-log-dialog-title = .title = Tor log +# The screen-reader name for the Tor log table. Should match the dialog title. +tor-log-dialog-table = + .aria-label = { tor-log-dialog-title.title } # "log" is a noun, referring to the recorded text output of the Tor process. tor-log-dialog-copy-button = .label = Copy Tor log to clipboard
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/8aa4f8c...
tor-commits@lists.torproject.org