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
-
0cc2f849
by Henry Wilkes at 2025-02-20T17:38:02+00:00
-
21c61532
by Henry Wilkes at 2025-02-20T17:38:03+00:00
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:
... | ... | @@ -4,20 +4,18 @@ const { setTimeout, clearTimeout } = ChromeUtils.importESModule( |
4 | 4 | "resource://gre/modules/Timer.sys.mjs"
|
5 | 5 | );
|
6 | 6 | |
7 | -const { TorProviderBuilder } = ChromeUtils.importESModule(
|
|
7 | +const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
|
|
8 | 8 | "resource://gre/modules/TorProviderBuilder.sys.mjs"
|
9 | 9 | );
|
10 | 10 | |
11 | -window.addEventListener(
|
|
12 | - "DOMContentLoaded",
|
|
13 | - () => {
|
|
11 | +const gTorLogDialog = {
|
|
12 | + init() {
|
|
14 | 13 | const dialog = document.getElementById("torPreferences-torLog-dialog");
|
15 | 14 | const copyLogButton = dialog.getButton("extra1");
|
16 | 15 | copyLogButton.setAttribute("data-l10n-id", "tor-log-dialog-copy-button");
|
17 | 16 | |
18 | - const logText = document.getElementById(
|
|
19 | - "torPreferences-torDialog-textarea"
|
|
20 | - );
|
|
17 | + this._logTable = document.getElementById("tor-log-table");
|
|
18 | + this._logBody = document.getElementById("tor-log-body");
|
|
21 | 19 | |
22 | 20 | let restoreButtonTimeout = null;
|
23 | 21 | copyLogButton.addEventListener("command", () => {
|
... | ... | @@ -25,7 +23,14 @@ window.addEventListener( |
25 | 23 | let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
|
26 | 24 | Ci.nsIClipboardHelper
|
27 | 25 | );
|
28 | - clipboard.copyString(logText.value);
|
|
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 | + );
|
|
29 | 34 | |
30 | 35 | copyLogButton.setAttribute(
|
31 | 36 | "data-l10n-id",
|
... | ... | @@ -47,13 +52,79 @@ window.addEventListener( |
47 | 52 | }, RESTORE_TIME);
|
48 | 53 | });
|
49 | 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 | + |
|
50 | 69 | // A waiting state should not be needed at this point.
|
51 | 70 | // Also, we probably cannot even arrive here if the provider failed to
|
52 | 71 | // initialize, otherwise we could use a try/catch, and write the exception
|
53 | 72 | // text in the logs, instead.
|
54 | - TorProviderBuilder.build().then(
|
|
55 | - provider => (logText.value = provider.getLog())
|
|
56 | - );
|
|
73 | + TorProviderBuilder.build().then(provider => {
|
|
74 | + Services.obs.addObserver(this, TorProviderTopics.TorLog);
|
|
75 | + window.addEventListener(
|
|
76 | + "unload",
|
|
77 | + () => {
|
|
78 | + Services.obs.removeObserver(this, TorProviderTopics.TorLog);
|
|
79 | + },
|
|
80 | + { once: true }
|
|
81 | + );
|
|
82 | + |
|
83 | + for (const logEntry of provider.getLog()) {
|
|
84 | + this.addLogEntry(logEntry, true);
|
|
85 | + }
|
|
86 | + // Set the initial scroll to the bottom.
|
|
87 | + this._logTable.scrollTo({
|
|
88 | + top: this._logTable.scrollTopMax,
|
|
89 | + behaviour: "instant",
|
|
90 | + });
|
|
91 | + });
|
|
92 | + },
|
|
93 | + |
|
94 | + observe(subject, topic) {
|
|
95 | + if (topic === TorProviderTopics.TorLog) {
|
|
96 | + this.addLogEntry(subject.wrappedJSObject, false);
|
|
97 | + }
|
|
98 | + },
|
|
99 | + |
|
100 | + addLogEntry(logEntry, initial) {
|
|
101 | + const timeEl = document.createElement("td");
|
|
102 | + timeEl.textContent = logEntry.timestamp;
|
|
103 | + timeEl.classList.add("time");
|
|
104 | + const messageEl = document.createElement("td");
|
|
105 | + messageEl.textContent = `[${logEntry.type}] ${logEntry.msg}`;
|
|
106 | + messageEl.classList.add("message");
|
|
107 | + |
|
108 | + const row = document.createElement("tr");
|
|
109 | + row.append(timeEl, messageEl);
|
|
110 | + |
|
111 | + // If this is a new entry, and we are currently scrolled to the bottom (with
|
|
112 | + // a 6px allowance) we keep the scroll position at the bottom to "follow"
|
|
113 | + // the updates.
|
|
114 | + const scrollToBottom =
|
|
115 | + !initial && this._logTable.scrollTop >= this._logTable.scrollTopMax - 6;
|
|
116 | + |
|
117 | + this._logBody.append(row);
|
|
118 | + if (scrollToBottom) {
|
|
119 | + this._logTable.scrollTo({ top: this._logTable.scrollTopMax });
|
|
120 | + }
|
|
121 | + },
|
|
122 | +};
|
|
123 | + |
|
124 | +window.addEventListener(
|
|
125 | + "DOMContentLoaded",
|
|
126 | + () => {
|
|
127 | + gTorLogDialog.init();
|
|
57 | 128 | },
|
58 | 129 | { once: true }
|
59 | 130 | ); |
... | ... | @@ -23,10 +23,33 @@ |
23 | 23 | |
24 | 24 | <script src="chrome://browser/content/torpreferences/torLogDialog.js" />
|
25 | 25 | |
26 | - <html:textarea
|
|
27 | - id="torPreferences-torDialog-textarea"
|
|
28 | - multiline="true"
|
|
29 | - readonly="true"
|
|
30 | - />
|
|
26 | + <!-- We use a <table> element rather than a <ol>. A table structure allows
|
|
27 | + - screen reader users to navigate within one column so they can avoid the
|
|
28 | + - readback of the timestamp on every row. See tor-browser#43328.
|
|
29 | + - NOTE: We add the explicit role="table". Whilst this should not be
|
|
30 | + - neccessary, nor is it recommended, some screen readers (Orca 46) do not
|
|
31 | + - read out the default table role if the CSS `display` is not `table`.
|
|
32 | + - NOTE: Even though this table is updated with live information, we do
|
|
33 | + - not want to make this an aria-live area or use a "log updated"
|
|
34 | + - notification because the log messages are potentially busy.
|
|
35 | + - Moreover, the live updates is a convience so that the log doesn't need
|
|
36 | + - to be manually refreshed, rather than important information.
|
|
37 | + - NOTE: We add a tabindex=0 to make this element focusable with or
|
|
38 | + - without the overflow. This also makes the table the the initial focus
|
|
39 | + - of the dialog.
|
|
40 | + - NOTE: We add lang="en" and dir="ltr" to the <tbody> since the content
|
|
41 | + - of the log is in English. We do not add this to the <table> element
|
|
42 | + - since its aria-label is localised and we want the scrollbar to match
|
|
43 | + - the locale direction.
|
|
44 | + - NOTE: We avoid any whitespace between the <table> and <tbody> to ensure
|
|
45 | + - it does not contribute to the text content when the top of the table is
|
|
46 | + - manually copied. -->
|
|
47 | + <html:table
|
|
48 | + id="tor-log-table"
|
|
49 | + role="table"
|
|
50 | + data-l10n-id="tor-log-dialog-table"
|
|
51 | + tabindex="0"
|
|
52 | + ><html:tbody id="tor-log-body" lang="en" dir="ltr"></html:tbody
|
|
53 | + ></html:table>
|
|
31 | 54 | </dialog>
|
32 | 55 | </window> |
... | ... | @@ -1058,12 +1058,39 @@ groupbox#torPreferences-bridges-group textarea { |
1058 | 1058 | }
|
1059 | 1059 | |
1060 | 1060 | /* Tor logs dialog */
|
1061 | -textarea#torPreferences-torDialog-textarea {
|
|
1061 | +#tor-log-table {
|
|
1062 | 1062 | flex: 1 0 auto;
|
1063 | - font-family: monospace;
|
|
1064 | - font-size: 0.8em;
|
|
1065 | - white-space: pre;
|
|
1066 | 1063 | overflow: auto;
|
1067 | - /* 10 lines */
|
|
1068 | 1064 | min-height: 20em;
|
1065 | + height: 20em;
|
|
1066 | + display: flex;
|
|
1067 | + flex-direction: column;
|
|
1068 | + padding: var(--space-small);
|
|
1069 | + margin-block-end: 4px;
|
|
1070 | + border: 1px solid var(--in-content-box-border-color);
|
|
1071 | + border-radius: var(--border-radius-small);
|
|
1072 | + font-size: var(--font-size-small);
|
|
1073 | +}
|
|
1074 | + |
|
1075 | +#tor-log-body,
|
|
1076 | +#tor-log-table tr {
|
|
1077 | + display: contents;
|
|
1078 | +}
|
|
1079 | + |
|
1080 | +#tor-log-table td {
|
|
1081 | + flex: 0 0 auto;
|
|
1082 | + padding: 0;
|
|
1083 | +}
|
|
1084 | + |
|
1085 | +#tor-log-table td.time {
|
|
1086 | + color: var(--text-color-deemphasized);
|
|
1087 | + margin-block-end: var(--space-xsmall);
|
|
1088 | +}
|
|
1089 | + |
|
1090 | +#tor-log-table td.message {
|
|
1091 | + overflow-wrap: anywhere;
|
|
1092 | +}
|
|
1093 | + |
|
1094 | +#tor-log-table tr:not(:last-of-type) td.message {
|
|
1095 | + margin-block-end: var(--space-medium);
|
|
1069 | 1096 | } |
... | ... | @@ -512,14 +512,12 @@ export class TorProvider { |
512 | 512 | }
|
513 | 513 | |
514 | 514 | /**
|
515 | - * Returns captured log message as a text string (one message per line).
|
|
515 | + * Returns captured log messages.
|
|
516 | 516 | *
|
517 | - * @returns {string} The logs we collected from the tor daemon so far
|
|
517 | + * @returns {LogEntry[]} The logs we collected from the tor daemon so far.
|
|
518 | 518 | */
|
519 | 519 | getLog() {
|
520 | - return this.#logs
|
|
521 | - .map(logObj => `${logObj.timestamp} [${logObj.type}] ${logObj.msg}`)
|
|
522 | - .join(TorLauncherUtil.isWindows ? "\r\n" : "\n");
|
|
520 | + return structuredClone(this.#logs);
|
|
523 | 521 | }
|
524 | 522 | |
525 | 523 | /**
|
... | ... | @@ -423,6 +423,9 @@ tor-view-log-button = View log… |
423 | 423 | # "log" is a noun, referring to the recorded text output of the Tor process.
|
424 | 424 | tor-log-dialog-title =
|
425 | 425 | .title = Tor log
|
426 | +# The screen-reader name for the Tor log table. Should match the dialog title.
|
|
427 | +tor-log-dialog-table =
|
|
428 | + .aria-label = { tor-log-dialog-title.title }
|
|
426 | 429 | # "log" is a noun, referring to the recorded text output of the Tor process.
|
427 | 430 | tor-log-dialog-copy-button =
|
428 | 431 | .label = Copy Tor log to clipboard
|