Loading browser/components/torpreferences/content/connectionPane.js +11 −1 Changes for browser/components/torpreferences/content/connectionPane.js: 11 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -1321,7 +1321,17 @@ const gLoxStatus = { ); this._invitesButton.addEventListener("click", () => { // TODO: Show invites. gSubDialog.open( "chrome://browser/content/torpreferences/loxInviteDialog.xhtml", { features: "resizable=yes", closedCallback: () => { // TODO: Listen for events from Lox, rather than call _updateInvites // directly. this._updateInvites(); }, } ); }); this._unlockAlertButton.addEventListener("click", () => { // TODO: Have a way to ensure that the cleared event data matches the Loading browser/components/torpreferences/content/loxInviteDialog.js 0 → 100644 +347 −0 Changes for browser/components/torpreferences/content/loxInviteDialog.js: 347 added lines, 0 removed lines. Original line number Diff line number Diff line "use strict"; const { TorSettings, TorSettingsTopics, TorBridgeSource } = ChromeUtils.importESModule("resource://gre/modules/TorSettings.sys.mjs"); const { Lox, LoxErrors } = ChromeUtils.importESModule( "resource://gre/modules/Lox.sys.mjs" ); /** * Fake Lox module const LoxErrors = { LoxServerUnreachable: "LoxServerUnreachable", Other: "Other", }; const Lox = { remainingInvites: 5, getRemainingInviteCount() { return this.remainingInvites; }, invites: [ '{"invite": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]}', '{"invite": [9,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]}', ], getInvites() { return this.invites; }, failError: null, generateInvite() { return new Promise((res, rej) => { setTimeout(() => { if (this.failError) { rej({ type: this.failError }); return; } if (!this.remainingInvites) { rej({ type: LoxErrors.Other }); return; } const invite = JSON.stringify({ invite: Array.from({ length: 100 }, () => Math.floor(Math.random() * 265) ), }); this.invites.push(invite); this.remainingInvites--; res(invite); }, 4000); }); }, }; */ const gLoxInvites = { /** * Initialize the dialog. */ init() { this._dialog = document.getElementById("lox-invite-dialog"); this._remainingInvitesEl = document.getElementById( "lox-invite-dialog-remaining" ); this._generateButton = document.getElementById( "lox-invite-dialog-generate-button" ); this._connectingEl = document.getElementById( "lox-invite-dialog-connecting" ); this._errorEl = document.getElementById("lox-invite-dialog-error-message"); this._inviteListEl = document.getElementById("lox-invite-dialog-list"); this._generateButton.addEventListener("click", () => { this._generateNewInvite(); }); const menu = document.getElementById("lox-invite-dialog-item-menu"); this._inviteListEl.addEventListener("contextmenu", event => { if (!this._inviteListEl.selectedItem) { return; } menu.openPopupAtScreen(event.screenX, event.screenY, true); }); menu.addEventListener("popuphidden", () => { menu.setAttribute("aria-hidden", "true"); }); menu.addEventListener("popupshowing", () => { menu.removeAttribute("aria-hidden"); }); document .getElementById("lox-invite-dialog-copy-menu-item") .addEventListener("command", () => { const selected = this._inviteListEl.selectedItem; if (!selected) { return; } const clipboard = Cc[ "@mozilla.org/widget/clipboardhelper;1" ].getService(Ci.nsIClipboardHelper); clipboard.copyString(selected.textContent); }); // NOTE: TorSettings should already be initialized when this dialog is // opened. Services.obs.addObserver(this, TorSettingsTopics.SettingsChanged); // TODO: Listen for new invites from Lox, when supported. // Set initial _loxId value. Can close this dialog. this._updateLoxId(); this._updateRemainingInvites(); this._updateExistingInvites(); }, /** * Un-initialize the dialog. */ uninit() { Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged); }, observe(subject, topic, data) { switch (topic) { case TorSettingsTopics.SettingsChanged: const { changes } = subject.wrappedJSObject; if ( changes.includes("bridges.source") || changes.includes("bridges.lox_id") ) { this._updateLoxId(); } break; } }, /** * The loxId this dialog is shown for. null if uninitailized. * * @type {string?} */ _loxId: null, /** * Update the _loxId value. Will close the dialog if it changes after * initialization. */ _updateLoxId() { const loxId = TorSettings.bridges.source === TorBridgeSource.Lox ? TorSettings.bridges.lox_id : ""; if (!loxId || (this._loxId !== null && loxId !== this._loxId)) { // No lox id, or it changed. Close this dialog. this._dialog.cancelDialog(); } this._loxId = loxId; }, /** * The invites that are already shown. * * @type {Set<string>} */ _shownInvites: new Set(), /** * Add a new invite at the start of the list. * * @param {string} invite - The invite to add. */ _addInvite(invite) { if (this._shownInvites.has(invite)) { return; } const newInvite = document.createXULElement("richlistitem"); newInvite.classList.add("lox-invite-dialog-list-item"); newInvite.textContent = invite; this._inviteListEl.prepend(newInvite); this._shownInvites.add(invite); }, /** * Update the display of the existing invites. */ _updateExistingInvites() { // Add new invites. // NOTE: we only expect invites to be appended, so we won't re-order any. // NOTE: invites are ordered with the oldest first. for (const invite of Lox.getInvites()) { this._addInvite(invite); } }, /** * The shown number or remaining invites we have. * * @type {integer} */ _remainingInvites: 0, /** * Update the display of the remaining invites. */ _updateRemainingInvites() { this._remainingInvites = Lox.getRemainingInviteCount(); document.l10n.setAttributes( this._remainingInvitesEl, "tor-bridges-lox-remaining-invites", { numInvites: this._remainingInvites } ); this._updateGenerateButtonState(); }, /** * Whether we are currently generating an invite. * * @type {boolean} */ _generating: false, /** * Set whether we are generating an invite. * * @param {boolean} isGenerating - Whether we are generating. */ _setGenerating(isGenerating) { this._generating = isGenerating; this._updateGenerateButtonState(); this._connectingEl.classList.toggle("show-connecting", isGenerating); }, /** * Update the state of the generate button. */ _updateGenerateButtonState() { this._generateButton.disabled = this._generating || !this._remainingInvites; }, /** * Start generating a new invite. */ _generateNewInvite() { if (this._generating) { console.error("Already generating an invite"); return; } this._setGenerating(true); // Clear the previous error. this._updateGenerateError(null); // Move focus from the button to the connecting element, since button is // now disabled. this._connectingEl.focus(); let lostFocus = false; Lox.generateInvite() .finally(() => { // Fetch whether the connecting label still has focus before we hide it. lostFocus = this._connectingEl.contains(document.activeElement); this._setGenerating(false); }) .then( invite => { this._addInvite(invite); if (!this._inviteListEl.contains(document.activeElement)) { // Does not have focus, change the selected item to be the new // invite (at index 0). this._inviteListEl.selectedIndex = 0; } if (lostFocus) { // Move focus to the new invite before we hide the "Connecting" // message. this._inviteListEl.focus(); } // TODO: When Lox sends out notifications, let the observer handle the // change rather than calling _updateRemainingInvites directly. this._updateRemainingInvites(); }, loxError => { console.error("Failed to generate an invite", loxError); switch (loxError.type) { case LoxErrors.LoxServerUnreachable: this._updateGenerateError("no-server"); break; default: this._updateGenerateError("generic"); break; } if (lostFocus) { // Move focus back to the button before we hide the "Connecting" // message. this._generateButton.focus(); } } ); }, /** * Update the shown generation error. * * @param {string?} type - The error type, or null if no error should be * shown. */ _updateGenerateError(type) { // First clear the existing error. this._errorEl.removeAttribute("data-l10n-id"); this._errorEl.textContent = ""; this._errorEl.classList.toggle("show-error", !!type); if (!type) { return; } let errorId; switch (type) { case "no-server": errorId = "lox-invite-dialog-no-server-error"; break; case "generic": // Generic error. errorId = "lox-invite-dialog-generic-invite-error"; break; } document.l10n.setAttributes(this._errorEl, errorId); }, }; window.addEventListener( "DOMContentLoaded", () => { gLoxInvites.init(); window.addEventListener( "unload", () => { gLoxInvites.uninit(); }, { once: true } ); }, { once: true } ); browser/components/torpreferences/content/loxInviteDialog.xhtml 0 → 100644 +56 −0 Changes for browser/components/torpreferences/content/loxInviteDialog.xhtml: 56 added lines, 0 removed lines. Original line number Diff line number Diff line <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> <?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> <window type="child" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" data-l10n-id="lox-invite-dialog-title" > <!-- Context menu, aria-hidden whilst not shown so it does not appear in the - document content. --> <menupopup id="lox-invite-dialog-item-menu" aria-hidden="true"> <menuitem id="lox-invite-dialog-copy-menu-item" data-l10n-id="lox-invite-dialog-menu-item-copy-invite" /> </menupopup> <dialog id="lox-invite-dialog" buttons="accept"> <linkset> <html:link rel="localization" href="browser/tor-browser.ftl" /> </linkset> <script src="chrome://browser/content/torpreferences/loxInviteDialog.js" /> <description data-l10n-id="lox-invite-dialog-description"></description> <html:div id="lox-invite-dialog-generate-area"> <html:span id="lox-invite-dialog-remaining"></html:span> <html:button id="lox-invite-dialog-generate-button" data-l10n-id="lox-invite-dialog-request-button" ></html:button> <html:div id="lox-invite-dialog-message-area"> <html:span id="lox-invite-dialog-error-message" role="alert" ></html:span> <html:span id="lox-invite-dialog-connecting" role="alert" tabindex="0" data-l10n-id="lox-invite-dialog-connecting" ></html:span> </html:div> </html:div> <html:div id="lox-invite-dialog-list-label" data-l10n-id="lox-invite-dialog-invites-label" ></html:div> <richlistbox id="lox-invite-dialog-list" aria-labelledby="lox-invite-dialog-list-label" ></richlistbox> </dialog> </window> browser/components/torpreferences/content/torPreferences.css +69 −0 Changes for browser/components/torpreferences/content/torPreferences.css: 69 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -820,6 +820,75 @@ dialog#torPreferences-requestBridge-dialog > hbox { background: var(--qr-one); } /* Lox invite dialog */ #lox-invite-dialog-generate-area { flex: 0 0 auto; display: grid; grid-template: ". remaining button" min-content "message message message" auto / 1fr max-content max-content; gap: 8px; margin-block: 16px 8px; align-items: center; } #lox-invite-dialog-remaining { grid-area: remaining; } #lox-invite-dialog-generate-button { grid-area: button; } #lox-invite-dialog-message-area { grid-area: message; justify-self: end; } #lox-invite-dialog-message-area::after { /* Zero width space, to ensure we are always one line high. */ content: "\200B"; } #lox-invite-dialog-error-message { color: var(--in-content-error-text-color); } #lox-invite-dialog-error-message:not(.show-error) { display: none; } #lox-invite-dialog-connecting { color: var(--text-color-deemphasized); /* TODO: Add spinner ::before */ } #lox-invite-dialog-connecting:not(.show-connecting) { display: none; } #lox-invite-dialog-list-label { font-weight: 700; } #lox-invite-dialog-list { flex: 1 1 auto; /* basis height */ height: 10em; margin-block: 8px; } .lox-invite-dialog-list-item { white-space: nowrap; overflow-x: hidden; /* FIXME: ellipsis does not show. */ text-overflow: ellipsis; padding-block: 6px; padding-inline: 8px; } /* Builtin bridge dialog */ #torPreferences-builtinBridge-header { margin: 8px 0 10px 0; Loading browser/components/torpreferences/jar.mn +2 −0 Changes for browser/components/torpreferences/jar.mn: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -9,6 +9,8 @@ browser.jar: content/browser/torpreferences/lox-success.svg (content/lox-success.svg) content/browser/torpreferences/lox-complete-ring.svg (content/lox-complete-ring.svg) content/browser/torpreferences/lox-progress-ring.svg (content/lox-progress-ring.svg) content/browser/torpreferences/loxInviteDialog.xhtml (content/loxInviteDialog.xhtml) content/browser/torpreferences/loxInviteDialog.js (content/loxInviteDialog.js) content/browser/torpreferences/bridgeQrDialog.xhtml (content/bridgeQrDialog.xhtml) content/browser/torpreferences/bridgeQrDialog.js (content/bridgeQrDialog.js) content/browser/torpreferences/builtinBridgeDialog.xhtml (content/builtinBridgeDialog.xhtml) Loading Loading
browser/components/torpreferences/content/connectionPane.js +11 −1 Changes for browser/components/torpreferences/content/connectionPane.js: 11 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -1321,7 +1321,17 @@ const gLoxStatus = { ); this._invitesButton.addEventListener("click", () => { // TODO: Show invites. gSubDialog.open( "chrome://browser/content/torpreferences/loxInviteDialog.xhtml", { features: "resizable=yes", closedCallback: () => { // TODO: Listen for events from Lox, rather than call _updateInvites // directly. this._updateInvites(); }, } ); }); this._unlockAlertButton.addEventListener("click", () => { // TODO: Have a way to ensure that the cleared event data matches the Loading
browser/components/torpreferences/content/loxInviteDialog.js 0 → 100644 +347 −0 Changes for browser/components/torpreferences/content/loxInviteDialog.js: 347 added lines, 0 removed lines. Original line number Diff line number Diff line "use strict"; const { TorSettings, TorSettingsTopics, TorBridgeSource } = ChromeUtils.importESModule("resource://gre/modules/TorSettings.sys.mjs"); const { Lox, LoxErrors } = ChromeUtils.importESModule( "resource://gre/modules/Lox.sys.mjs" ); /** * Fake Lox module const LoxErrors = { LoxServerUnreachable: "LoxServerUnreachable", Other: "Other", }; const Lox = { remainingInvites: 5, getRemainingInviteCount() { return this.remainingInvites; }, invites: [ '{"invite": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]}', '{"invite": [9,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]}', ], getInvites() { return this.invites; }, failError: null, generateInvite() { return new Promise((res, rej) => { setTimeout(() => { if (this.failError) { rej({ type: this.failError }); return; } if (!this.remainingInvites) { rej({ type: LoxErrors.Other }); return; } const invite = JSON.stringify({ invite: Array.from({ length: 100 }, () => Math.floor(Math.random() * 265) ), }); this.invites.push(invite); this.remainingInvites--; res(invite); }, 4000); }); }, }; */ const gLoxInvites = { /** * Initialize the dialog. */ init() { this._dialog = document.getElementById("lox-invite-dialog"); this._remainingInvitesEl = document.getElementById( "lox-invite-dialog-remaining" ); this._generateButton = document.getElementById( "lox-invite-dialog-generate-button" ); this._connectingEl = document.getElementById( "lox-invite-dialog-connecting" ); this._errorEl = document.getElementById("lox-invite-dialog-error-message"); this._inviteListEl = document.getElementById("lox-invite-dialog-list"); this._generateButton.addEventListener("click", () => { this._generateNewInvite(); }); const menu = document.getElementById("lox-invite-dialog-item-menu"); this._inviteListEl.addEventListener("contextmenu", event => { if (!this._inviteListEl.selectedItem) { return; } menu.openPopupAtScreen(event.screenX, event.screenY, true); }); menu.addEventListener("popuphidden", () => { menu.setAttribute("aria-hidden", "true"); }); menu.addEventListener("popupshowing", () => { menu.removeAttribute("aria-hidden"); }); document .getElementById("lox-invite-dialog-copy-menu-item") .addEventListener("command", () => { const selected = this._inviteListEl.selectedItem; if (!selected) { return; } const clipboard = Cc[ "@mozilla.org/widget/clipboardhelper;1" ].getService(Ci.nsIClipboardHelper); clipboard.copyString(selected.textContent); }); // NOTE: TorSettings should already be initialized when this dialog is // opened. Services.obs.addObserver(this, TorSettingsTopics.SettingsChanged); // TODO: Listen for new invites from Lox, when supported. // Set initial _loxId value. Can close this dialog. this._updateLoxId(); this._updateRemainingInvites(); this._updateExistingInvites(); }, /** * Un-initialize the dialog. */ uninit() { Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged); }, observe(subject, topic, data) { switch (topic) { case TorSettingsTopics.SettingsChanged: const { changes } = subject.wrappedJSObject; if ( changes.includes("bridges.source") || changes.includes("bridges.lox_id") ) { this._updateLoxId(); } break; } }, /** * The loxId this dialog is shown for. null if uninitailized. * * @type {string?} */ _loxId: null, /** * Update the _loxId value. Will close the dialog if it changes after * initialization. */ _updateLoxId() { const loxId = TorSettings.bridges.source === TorBridgeSource.Lox ? TorSettings.bridges.lox_id : ""; if (!loxId || (this._loxId !== null && loxId !== this._loxId)) { // No lox id, or it changed. Close this dialog. this._dialog.cancelDialog(); } this._loxId = loxId; }, /** * The invites that are already shown. * * @type {Set<string>} */ _shownInvites: new Set(), /** * Add a new invite at the start of the list. * * @param {string} invite - The invite to add. */ _addInvite(invite) { if (this._shownInvites.has(invite)) { return; } const newInvite = document.createXULElement("richlistitem"); newInvite.classList.add("lox-invite-dialog-list-item"); newInvite.textContent = invite; this._inviteListEl.prepend(newInvite); this._shownInvites.add(invite); }, /** * Update the display of the existing invites. */ _updateExistingInvites() { // Add new invites. // NOTE: we only expect invites to be appended, so we won't re-order any. // NOTE: invites are ordered with the oldest first. for (const invite of Lox.getInvites()) { this._addInvite(invite); } }, /** * The shown number or remaining invites we have. * * @type {integer} */ _remainingInvites: 0, /** * Update the display of the remaining invites. */ _updateRemainingInvites() { this._remainingInvites = Lox.getRemainingInviteCount(); document.l10n.setAttributes( this._remainingInvitesEl, "tor-bridges-lox-remaining-invites", { numInvites: this._remainingInvites } ); this._updateGenerateButtonState(); }, /** * Whether we are currently generating an invite. * * @type {boolean} */ _generating: false, /** * Set whether we are generating an invite. * * @param {boolean} isGenerating - Whether we are generating. */ _setGenerating(isGenerating) { this._generating = isGenerating; this._updateGenerateButtonState(); this._connectingEl.classList.toggle("show-connecting", isGenerating); }, /** * Update the state of the generate button. */ _updateGenerateButtonState() { this._generateButton.disabled = this._generating || !this._remainingInvites; }, /** * Start generating a new invite. */ _generateNewInvite() { if (this._generating) { console.error("Already generating an invite"); return; } this._setGenerating(true); // Clear the previous error. this._updateGenerateError(null); // Move focus from the button to the connecting element, since button is // now disabled. this._connectingEl.focus(); let lostFocus = false; Lox.generateInvite() .finally(() => { // Fetch whether the connecting label still has focus before we hide it. lostFocus = this._connectingEl.contains(document.activeElement); this._setGenerating(false); }) .then( invite => { this._addInvite(invite); if (!this._inviteListEl.contains(document.activeElement)) { // Does not have focus, change the selected item to be the new // invite (at index 0). this._inviteListEl.selectedIndex = 0; } if (lostFocus) { // Move focus to the new invite before we hide the "Connecting" // message. this._inviteListEl.focus(); } // TODO: When Lox sends out notifications, let the observer handle the // change rather than calling _updateRemainingInvites directly. this._updateRemainingInvites(); }, loxError => { console.error("Failed to generate an invite", loxError); switch (loxError.type) { case LoxErrors.LoxServerUnreachable: this._updateGenerateError("no-server"); break; default: this._updateGenerateError("generic"); break; } if (lostFocus) { // Move focus back to the button before we hide the "Connecting" // message. this._generateButton.focus(); } } ); }, /** * Update the shown generation error. * * @param {string?} type - The error type, or null if no error should be * shown. */ _updateGenerateError(type) { // First clear the existing error. this._errorEl.removeAttribute("data-l10n-id"); this._errorEl.textContent = ""; this._errorEl.classList.toggle("show-error", !!type); if (!type) { return; } let errorId; switch (type) { case "no-server": errorId = "lox-invite-dialog-no-server-error"; break; case "generic": // Generic error. errorId = "lox-invite-dialog-generic-invite-error"; break; } document.l10n.setAttributes(this._errorEl, errorId); }, }; window.addEventListener( "DOMContentLoaded", () => { gLoxInvites.init(); window.addEventListener( "unload", () => { gLoxInvites.uninit(); }, { once: true } ); }, { once: true } );
browser/components/torpreferences/content/loxInviteDialog.xhtml 0 → 100644 +56 −0 Changes for browser/components/torpreferences/content/loxInviteDialog.xhtml: 56 added lines, 0 removed lines. Original line number Diff line number Diff line <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?> <?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?> <window type="child" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" data-l10n-id="lox-invite-dialog-title" > <!-- Context menu, aria-hidden whilst not shown so it does not appear in the - document content. --> <menupopup id="lox-invite-dialog-item-menu" aria-hidden="true"> <menuitem id="lox-invite-dialog-copy-menu-item" data-l10n-id="lox-invite-dialog-menu-item-copy-invite" /> </menupopup> <dialog id="lox-invite-dialog" buttons="accept"> <linkset> <html:link rel="localization" href="browser/tor-browser.ftl" /> </linkset> <script src="chrome://browser/content/torpreferences/loxInviteDialog.js" /> <description data-l10n-id="lox-invite-dialog-description"></description> <html:div id="lox-invite-dialog-generate-area"> <html:span id="lox-invite-dialog-remaining"></html:span> <html:button id="lox-invite-dialog-generate-button" data-l10n-id="lox-invite-dialog-request-button" ></html:button> <html:div id="lox-invite-dialog-message-area"> <html:span id="lox-invite-dialog-error-message" role="alert" ></html:span> <html:span id="lox-invite-dialog-connecting" role="alert" tabindex="0" data-l10n-id="lox-invite-dialog-connecting" ></html:span> </html:div> </html:div> <html:div id="lox-invite-dialog-list-label" data-l10n-id="lox-invite-dialog-invites-label" ></html:div> <richlistbox id="lox-invite-dialog-list" aria-labelledby="lox-invite-dialog-list-label" ></richlistbox> </dialog> </window>
browser/components/torpreferences/content/torPreferences.css +69 −0 Changes for browser/components/torpreferences/content/torPreferences.css: 69 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -820,6 +820,75 @@ dialog#torPreferences-requestBridge-dialog > hbox { background: var(--qr-one); } /* Lox invite dialog */ #lox-invite-dialog-generate-area { flex: 0 0 auto; display: grid; grid-template: ". remaining button" min-content "message message message" auto / 1fr max-content max-content; gap: 8px; margin-block: 16px 8px; align-items: center; } #lox-invite-dialog-remaining { grid-area: remaining; } #lox-invite-dialog-generate-button { grid-area: button; } #lox-invite-dialog-message-area { grid-area: message; justify-self: end; } #lox-invite-dialog-message-area::after { /* Zero width space, to ensure we are always one line high. */ content: "\200B"; } #lox-invite-dialog-error-message { color: var(--in-content-error-text-color); } #lox-invite-dialog-error-message:not(.show-error) { display: none; } #lox-invite-dialog-connecting { color: var(--text-color-deemphasized); /* TODO: Add spinner ::before */ } #lox-invite-dialog-connecting:not(.show-connecting) { display: none; } #lox-invite-dialog-list-label { font-weight: 700; } #lox-invite-dialog-list { flex: 1 1 auto; /* basis height */ height: 10em; margin-block: 8px; } .lox-invite-dialog-list-item { white-space: nowrap; overflow-x: hidden; /* FIXME: ellipsis does not show. */ text-overflow: ellipsis; padding-block: 6px; padding-inline: 8px; } /* Builtin bridge dialog */ #torPreferences-builtinBridge-header { margin: 8px 0 10px 0; Loading
browser/components/torpreferences/jar.mn +2 −0 Changes for browser/components/torpreferences/jar.mn: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -9,6 +9,8 @@ browser.jar: content/browser/torpreferences/lox-success.svg (content/lox-success.svg) content/browser/torpreferences/lox-complete-ring.svg (content/lox-complete-ring.svg) content/browser/torpreferences/lox-progress-ring.svg (content/lox-progress-ring.svg) content/browser/torpreferences/loxInviteDialog.xhtml (content/loxInviteDialog.xhtml) content/browser/torpreferences/loxInviteDialog.js (content/loxInviteDialog.js) content/browser/torpreferences/bridgeQrDialog.xhtml (content/bridgeQrDialog.xhtml) content/browser/torpreferences/bridgeQrDialog.js (content/bridgeQrDialog.js) content/browser/torpreferences/builtinBridgeDialog.xhtml (content/builtinBridgeDialog.xhtml) Loading