tor-commits
Threads by month
- ----- 2026 -----
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 215699 discussions
[Git][tpo/applications/tor-browser][base-browser-115.7.0esr-13.5-1] fixup! Bug 32308: use direct browser sizing for letterboxing.
by ma1 (@ma1) 29 Jan '24
by ma1 (@ma1) 29 Jan '24
29 Jan '24
ma1 pushed to branch base-browser-115.7.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
0cb13087 by hackademix at 2024-01-29T16:32:32+01:00
fixup! Bug 32308: use direct browser sizing for letterboxing.
Bug 41917: Make the appearance of letterboxing look more intentional
- - - - -
3 changed files:
- browser/app/profile/001-base-profile.js
- browser/base/content/browser.css
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -376,6 +376,10 @@ pref("security.remote_settings.intermediates.enabled", false);
pref("dom.use_components_shim", false);
// Enable letterboxing
pref("privacy.resistFingerprinting.letterboxing", true);
+// tor-browser#41917 center letterboxed area vertically
+pref("privacy.resistFingerprinting.letterboxing.vcenter", true);
+// tor-browser#41917 letterboxing gradient background
+pref("privacy.resistFingerprinting.letterboxing.gradient", true);
// tor-browser#41695: how many warnings we show if user closes them without restoring the window size
pref("privacy.resistFingerprinting.resizeWarnings", 3);
// tor-browser#33282: new windows start at 1400x900 when there's enough screen space, otherwise down by 200x100 blocks
=====================================
browser/base/content/browser.css
=====================================
@@ -134,6 +134,56 @@ body {
doesn't get notified on horizontal shrinking.
*/
overflow-x: hidden;
+ background: var(--letterboxing-bgcolor);
+}
+
+.letterboxing {
+ --letterboxing-border-radius: 8px;
+ --letterboxing-border-top-radius: 0;
+ --letterboxing-vertical-alignment: start;
+ --letterboxing-shadow-color: rgba(12, 12, 13, 0.10);
+ --letterboxing-bgcolor: var(--tabpanel-background-color);
+ --letterboxing-gradient-color1: var(--letterboxing-bgcolor);
+ --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor));
+}
+
+.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
+ --letterboxing-border-top-radius: var(--letterboxing-border-radius);
+ --letterboxing-vertical-alignment: center;
+}
+
+.letterboxing.letterboxing-gradient .browserContainer {
+ background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor);
+ box-shadow: rgba(0, 0, 0, 0.5) 0px -1px 2px;
+}
+
+/*
+ Align status bar with content.
+ TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117)
+*/
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+ > #statuspanel:not([hidden]) {
+ position: relative;
+ place-self: end left;
+ left: 0;
+ right: 0;
+ --letterboxing-status-left-radius: var(--letterboxing-border-radius);
+ --letterboxing-status-right-radius: 0;
+}
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+ > #statuspanel:not([mirror]):-moz-locale-dir(rtl),
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+ > #statuspanel[mirror]:-moz-locale-dir(ltr) {
+ left: 0;
+ right: 0;
+ --letterboxing-status-right-radius: var(--letterboxing-border-radius);
+ --letterboxing-status-left-radius: 0;
+ justify-self: right;
+}
+
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+#statuspanel-label {
+ border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius);
}
/**
@@ -142,21 +192,15 @@ body {
**/
.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
/* width & height to be dynamically set by RFPHelper.jsm */
- outline: 1px solid var(--chrome-content-separator-color);
+ box-shadow: 0px 4px 8px 0px var(--letterboxing-shadow-color);
+ border-radius: var(--letterboxing-border-radius);
+ border-top-left-radius: var(--letterboxing-border-top-radius);
+ border-top-right-radius: var(--letterboxing-border-top-radius);
}
-.exclude-letterboxing > browser {
- outline: initial;
-}
-
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
- place-content: start center;
-}
-
-/* extend down the toolbar's colors when letterboxing is enabled */
-.letterboxing {
- background-color: var(--toolbar-bgcolor);
- background-image: var(--toolbar-bgimage);
+:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
+ > .browserStack:not(.exclude-letterboxing) {
+ place-content: var(--letterboxing-vertical-alignment) center;
}
#toolbar-menubar[autohide="true"] {
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -14,6 +14,11 @@ const kPrefLetterboxingDimensions =
"privacy.resistFingerprinting.letterboxing.dimensions";
const kPrefLetterboxingTesting =
"privacy.resistFingerprinting.letterboxing.testing";
+const kPrefLetterboxingVcenter =
+ "privacy.resistFingerprinting.letterboxing.vcenter";
+const kPrefLetterboxingGradient =
+ "privacy.resistFingerprinting.letterboxing.gradient";
+
const kTopicDOMWindowOpened = "domwindowopened";
const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings";
@@ -140,6 +145,9 @@ class _RFPHelper {
// Add unconditional observers
Services.prefs.addObserver(kPrefResistFingerprinting, this);
Services.prefs.addObserver(kPrefLetterboxing, this);
+ Services.prefs.addObserver(kPrefLetterboxingVcenter, this);
+ Services.prefs.addObserver(kPrefLetterboxingGradient, this);
+
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_letterboxingDimensions",
@@ -171,6 +179,8 @@ class _RFPHelper {
// Remove unconditional observers
Services.prefs.removeObserver(kPrefResistFingerprinting, this);
+ Services.prefs.removeObserver(kPrefLetterboxingGradient, this);
+ Services.prefs.removeObserver(kPrefLetterboxingVcenter, this);
Services.prefs.removeObserver(kPrefLetterboxing, this);
// Remove the RFP observers, swallowing exceptions if they weren't present
this._removeRFPObservers();
@@ -218,6 +228,8 @@ class _RFPHelper {
this._handleSpoofEnglishChanged();
break;
case kPrefLetterboxing:
+ case kPrefLetterboxingVcenter:
+ case kPrefLetterboxingGradient:
this._handleLetterboxingPrefChanged();
break;
default:
@@ -429,8 +441,7 @@ class _RFPHelper {
});
}
- getLetterboxingDefaultRule(aBrowser) {
- let document = aBrowser.ownerDocument;
+ getLetterboxingDefaultRule(document) {
return (document._letterBoxingSizingRule ||= (() => {
// If not already cached on the document object, traverse the CSSOM and
// find the rule applying the default letterboxing styles to browsers
@@ -555,11 +566,16 @@ class _RFPHelper {
return;
}
+ let lastRoundedSize;
+
const roundDimensions = (aWidth, aHeight) => {
- const r = (aWidth, aHeight) => ({
- width: `var(--rdm-width, ${aWidth}px)`,
- height: `var(--rdm-height, ${aHeight}px)`,
- });
+ const r = (width, height) => {
+ lastRoundedSize = {width, height};
+ return {
+ width: `var(--rdm-width, ${width}px)`,
+ height: `var(--rdm-height, ${height}px)`,
+ }
+ };
let result;
@@ -642,7 +658,7 @@ class _RFPHelper {
const roundedDefault = roundDimensions(containerWidth, containerHeight);
styleChanges.queueIfNeeded(
- this.getLetterboxingDefaultRule(aBrowser),
+ this.getLetterboxingDefaultRule(aBrowser.ownerDocument),
roundedDefault
);
@@ -655,6 +671,21 @@ class _RFPHelper {
: { width: "", height: "" }; // otherwise we can keep the default (rounded) size
styleChanges.queueIfNeeded(aBrowser, roundedInline);
+ if (lastRoundedSize) {
+ // check wether the letterboxing margin is less than the border radius, and if so flatten the borders
+ let borderRadius = parseInt(win.getComputedStyle(browserContainer).getPropertyValue("--letterboxing-border-radius"));
+ if (borderRadius &&
+ (parentWidth - lastRoundedSize.width < borderRadius &&
+ parentHeight - lastRoundedSize.height < borderRadius)) {
+ borderRadius = 0;
+ } else {
+ borderRadius = "";
+ }
+ styleChanges.queueIfNeeded(browserParent, {
+ '--letterboxing-border-radius': borderRadius
+ });
+ }
+
// If the size of the content is already quantized, we do nothing.
if (!styleChanges.length) {
log(`${logPrefix} is_rounded == true`);
@@ -690,6 +721,10 @@ class _RFPHelper {
_updateSizeForTabsInWindow(aWindow) {
let tabBrowser = aWindow.gBrowser;
tabBrowser.tabpanels?.classList.add("letterboxing");
+ tabBrowser.tabpanels?.classList.toggle("letterboxing-vcenter",
+ Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false));
+ tabBrowser.tabpanels?.classList.toggle("letterboxing-gradient",
+ Services.prefs.getBoolPref(kPrefLetterboxingGradient, false));
for (let tab of tabBrowser.tabs) {
let browser = tab.linkedBrowser;
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/0cb1308…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/0cb1308…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.7.0esr-13.5-1] 2 commits: fixup! Bug 32308: use direct browser sizing for letterboxing.
by ma1 (@ma1) 29 Jan '24
by ma1 (@ma1) 29 Jan '24
29 Jan '24
ma1 pushed to branch tor-browser-115.7.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
8d25c1cc by hackademix at 2024-01-29T16:28:12+01:00
fixup! Bug 32308: use direct browser sizing for letterboxing.
Bug 41917: Make the appearance of letterboxing look more intentional
- - - - -
366c81df by hackademix at 2024-01-29T16:28:18+01:00
Bug 41917: Tor brand-specific styles.
- - - - -
6 changed files:
- browser/app/profile/001-base-profile.js
- browser/base/content/browser.css
- browser/themes/shared/browser-shared.css
- browser/themes/shared/jar.inc.mn
- + browser/themes/shared/tor-branding.css
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -376,6 +376,10 @@ pref("security.remote_settings.intermediates.enabled", false);
pref("dom.use_components_shim", false);
// Enable letterboxing
pref("privacy.resistFingerprinting.letterboxing", true);
+// tor-browser#41917 center letterboxed area vertically
+pref("privacy.resistFingerprinting.letterboxing.vcenter", true);
+// tor-browser#41917 letterboxing gradient background
+pref("privacy.resistFingerprinting.letterboxing.gradient", true);
// tor-browser#41695: how many warnings we show if user closes them without restoring the window size
pref("privacy.resistFingerprinting.resizeWarnings", 3);
// tor-browser#33282: new windows start at 1400x900 when there's enough screen space, otherwise down by 200x100 blocks
=====================================
browser/base/content/browser.css
=====================================
@@ -134,6 +134,56 @@ body {
doesn't get notified on horizontal shrinking.
*/
overflow-x: hidden;
+ background: var(--letterboxing-bgcolor);
+}
+
+.letterboxing {
+ --letterboxing-border-radius: 8px;
+ --letterboxing-border-top-radius: 0;
+ --letterboxing-vertical-alignment: start;
+ --letterboxing-shadow-color: rgba(12, 12, 13, 0.10);
+ --letterboxing-bgcolor: var(--tabpanel-background-color);
+ --letterboxing-gradient-color1: var(--letterboxing-bgcolor);
+ --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor));
+}
+
+.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
+ --letterboxing-border-top-radius: var(--letterboxing-border-radius);
+ --letterboxing-vertical-alignment: center;
+}
+
+.letterboxing.letterboxing-gradient .browserContainer {
+ background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor);
+ box-shadow: rgba(0, 0, 0, 0.5) 0px -1px 2px;
+}
+
+/*
+ Align status bar with content.
+ TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117)
+*/
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+ > #statuspanel:not([hidden]) {
+ position: relative;
+ place-self: end left;
+ left: 0;
+ right: 0;
+ --letterboxing-status-left-radius: var(--letterboxing-border-radius);
+ --letterboxing-status-right-radius: 0;
+}
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+ > #statuspanel:not([mirror]):-moz-locale-dir(rtl),
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+ > #statuspanel[mirror]:-moz-locale-dir(ltr) {
+ left: 0;
+ right: 0;
+ --letterboxing-status-right-radius: var(--letterboxing-border-radius);
+ --letterboxing-status-left-radius: 0;
+ justify-self: right;
+}
+
+.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
+#statuspanel-label {
+ border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius);
}
/**
@@ -142,21 +192,15 @@ body {
**/
.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
/* width & height to be dynamically set by RFPHelper.jsm */
- outline: 1px solid var(--chrome-content-separator-color);
+ box-shadow: 0px 4px 8px 0px var(--letterboxing-shadow-color);
+ border-radius: var(--letterboxing-border-radius);
+ border-top-left-radius: var(--letterboxing-border-top-radius);
+ border-top-right-radius: var(--letterboxing-border-top-radius);
}
-.exclude-letterboxing > browser {
- outline: initial;
-}
-
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
- place-content: start center;
-}
-
-/* extend down the toolbar's colors when letterboxing is enabled */
-.letterboxing {
- background-color: var(--toolbar-bgcolor);
- background-image: var(--toolbar-bgimage);
+:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
+ > .browserStack:not(.exclude-letterboxing) {
+ place-content: var(--letterboxing-vertical-alignment) center;
}
#toolbar-menubar[autohide="true"] {
=====================================
browser/themes/shared/browser-shared.css
=====================================
@@ -24,6 +24,7 @@
@import url("chrome://browser/skin/UITour.css");
@import url("chrome://global/skin/browser-colors.css");
@import url("chrome://browser/skin/tor-urlbar-button.css");
+@import url("chrome://browser/skin/tor-branding.css");
@import url("chrome://browser/skin/onionlocation.css");
@namespace html url("http://www.w3.org/1999/xhtml");
=====================================
browser/themes/shared/jar.inc.mn
=====================================
@@ -29,6 +29,7 @@
skin/classic/browser/tabs.css (../shared/tabs.css)
skin/classic/browser/toolbarbuttons.css (../shared/toolbarbuttons.css)
skin/classic/browser/toolbarbutton-icons.css (../shared/toolbarbutton-icons.css)
+ skin/classic/browser/tor-branding.css (../shared/tor-branding.css)
skin/classic/browser/tor-urlbar-button.css (../shared/tor-urlbar-button.css)
skin/classic/browser/urlbar-dynamic-results.css (../shared/urlbar-dynamic-results.css)
skin/classic/browser/urlbar-searchbar.css (../shared/urlbar-searchbar.css)
=====================================
browser/themes/shared/tor-branding.css
=====================================
@@ -0,0 +1,13 @@
+.letterboxing {
+ --letterboxing-bgcolor: var(--color-grey-light-20, #F0F0F4);
+ --letterboxing-gradient-color1: rgba(0, 219, 222, 0.02);
+ --letterboxing-gradient-color2: rgba(252, 0, 255, 0.02);
+}
+
+@media (prefers-color-scheme: dark) {
+ .letterboxing {
+ --letterboxing-bgcolor: var(--color-grey-dark-10, #52525E);
+ --letterboxing-gradient-color1: rgba(0, 219, 222, 0.06);
+ --letterboxing-gradient-color2: rgba(252, 0, 255, 0.06);
+ }
+}
\ No newline at end of file
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -14,6 +14,11 @@ const kPrefLetterboxingDimensions =
"privacy.resistFingerprinting.letterboxing.dimensions";
const kPrefLetterboxingTesting =
"privacy.resistFingerprinting.letterboxing.testing";
+const kPrefLetterboxingVcenter =
+ "privacy.resistFingerprinting.letterboxing.vcenter";
+const kPrefLetterboxingGradient =
+ "privacy.resistFingerprinting.letterboxing.gradient";
+
const kTopicDOMWindowOpened = "domwindowopened";
const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings";
@@ -140,6 +145,9 @@ class _RFPHelper {
// Add unconditional observers
Services.prefs.addObserver(kPrefResistFingerprinting, this);
Services.prefs.addObserver(kPrefLetterboxing, this);
+ Services.prefs.addObserver(kPrefLetterboxingVcenter, this);
+ Services.prefs.addObserver(kPrefLetterboxingGradient, this);
+
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_letterboxingDimensions",
@@ -171,6 +179,8 @@ class _RFPHelper {
// Remove unconditional observers
Services.prefs.removeObserver(kPrefResistFingerprinting, this);
+ Services.prefs.removeObserver(kPrefLetterboxingGradient, this);
+ Services.prefs.removeObserver(kPrefLetterboxingVcenter, this);
Services.prefs.removeObserver(kPrefLetterboxing, this);
// Remove the RFP observers, swallowing exceptions if they weren't present
this._removeRFPObservers();
@@ -218,6 +228,8 @@ class _RFPHelper {
this._handleSpoofEnglishChanged();
break;
case kPrefLetterboxing:
+ case kPrefLetterboxingVcenter:
+ case kPrefLetterboxingGradient:
this._handleLetterboxingPrefChanged();
break;
default:
@@ -429,8 +441,7 @@ class _RFPHelper {
});
}
- getLetterboxingDefaultRule(aBrowser) {
- let document = aBrowser.ownerDocument;
+ getLetterboxingDefaultRule(document) {
return (document._letterBoxingSizingRule ||= (() => {
// If not already cached on the document object, traverse the CSSOM and
// find the rule applying the default letterboxing styles to browsers
@@ -555,11 +566,16 @@ class _RFPHelper {
return;
}
+ let lastRoundedSize;
+
const roundDimensions = (aWidth, aHeight) => {
- const r = (aWidth, aHeight) => ({
- width: `var(--rdm-width, ${aWidth}px)`,
- height: `var(--rdm-height, ${aHeight}px)`,
- });
+ const r = (width, height) => {
+ lastRoundedSize = {width, height};
+ return {
+ width: `var(--rdm-width, ${width}px)`,
+ height: `var(--rdm-height, ${height}px)`,
+ }
+ };
let result;
@@ -642,7 +658,7 @@ class _RFPHelper {
const roundedDefault = roundDimensions(containerWidth, containerHeight);
styleChanges.queueIfNeeded(
- this.getLetterboxingDefaultRule(aBrowser),
+ this.getLetterboxingDefaultRule(aBrowser.ownerDocument),
roundedDefault
);
@@ -655,6 +671,21 @@ class _RFPHelper {
: { width: "", height: "" }; // otherwise we can keep the default (rounded) size
styleChanges.queueIfNeeded(aBrowser, roundedInline);
+ if (lastRoundedSize) {
+ // check wether the letterboxing margin is less than the border radius, and if so flatten the borders
+ let borderRadius = parseInt(win.getComputedStyle(browserContainer).getPropertyValue("--letterboxing-border-radius"));
+ if (borderRadius &&
+ (parentWidth - lastRoundedSize.width < borderRadius &&
+ parentHeight - lastRoundedSize.height < borderRadius)) {
+ borderRadius = 0;
+ } else {
+ borderRadius = "";
+ }
+ styleChanges.queueIfNeeded(browserParent, {
+ '--letterboxing-border-radius': borderRadius
+ });
+ }
+
// If the size of the content is already quantized, we do nothing.
if (!styleChanges.length) {
log(`${logPrefix} is_rounded == true`);
@@ -690,6 +721,10 @@ class _RFPHelper {
_updateSizeForTabsInWindow(aWindow) {
let tabBrowser = aWindow.gBrowser;
tabBrowser.tabpanels?.classList.add("letterboxing");
+ tabBrowser.tabpanels?.classList.toggle("letterboxing-vcenter",
+ Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false));
+ tabBrowser.tabpanels?.classList.toggle("letterboxing-gradient",
+ Services.prefs.getBoolPref(kPrefLetterboxingGradient, false));
for (let tab of tabBrowser.tabs) {
let browser = tab.linkedBrowser;
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/478dde…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/478dde…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.7.0esr-13.5-1] 3 commits: Temporary commit: manually place generated wasm files
by Pier Angelo Vendrame (@pierov) 26 Jan '24
by Pier Angelo Vendrame (@pierov) 26 Jan '24
26 Jan '24
Pier Angelo Vendrame pushed to branch tor-browser-115.7.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
ebfac0ab by Cecylia Bocovich at 2024-01-26T16:27:01+00:00
Temporary commit: manually place generated wasm files
These files are built reproducibly using tor-browser-build: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/merge_re…
We're manually adding them here while working on the interface, but
eventually these should be placed in the right location using
tor-browser-build.
- - - - -
10d5eb0b by Cecylia Bocovich at 2024-01-26T16:27:01+00:00
Lox integration
- - - - -
478ddecb by Cecylia Bocovich at 2024-01-26T16:27:01+00:00
fixup! Bug 40597: Implement TorSettings module
Implement Lox backend.
- - - - -
9 changed files:
- dom/security/nsContentSecurityUtils.cpp
- + toolkit/components/lox/Lox.sys.mjs
- + toolkit/components/lox/content/lox_wasm_bg.wasm
- + toolkit/components/lox/jar.mn
- + toolkit/components/lox/lox_wasm.jsm
- + toolkit/components/lox/moz.build
- toolkit/components/moz.build
- toolkit/components/tor-launcher/TorStartupService.sys.mjs
- toolkit/modules/TorSettings.sys.mjs
Changes:
=====================================
dom/security/nsContentSecurityUtils.cpp
=====================================
@@ -618,6 +618,9 @@ bool nsContentSecurityUtils::IsEvalAllowed(JSContext* cx,
// The Browser Toolbox/Console
"debugger"_ns,
+
+ // Tor Browser's Lox wasm integration
+ "resource://gre/modules/lox_wasm.jsm"_ns,
};
// We also permit two specific idioms in eval()-like contexts. We'd like to
=====================================
toolkit/components/lox/Lox.sys.mjs
=====================================
@@ -0,0 +1,801 @@
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+import {
+ clearInterval,
+ setInterval,
+} from "resource://gre/modules/Timer.sys.mjs";
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ DomainFrontRequestBuilder:
+ "resource://gre/modules/DomainFrontedRequests.sys.mjs",
+ TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
+ TorConnectState: "resource://gre/modules/TorConnect.sys.mjs",
+ TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
+ TorSettingsTopics: "resource://gre/modules/TorSettings.sys.mjs",
+ TorBridgeSource: "resource://gre/modules/TorSettings.sys.mjs",
+});
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ init: "resource://gre/modules/lox_wasm.jsm",
+ open_invite: "resource://gre/modules/lox_wasm.jsm",
+ handle_new_lox_credential: "resource://gre/modules/lox_wasm.jsm",
+ set_panic_hook: "resource://gre/modules/lox_wasm.jsm",
+ invitation_is_trusted: "resource://gre/modules/lox_wasm.jsm",
+ issue_invite: "resource://gre/modules/lox_wasm.jsm",
+ prepare_invite: "resource://gre/modules/lox_wasm.jsm",
+ get_invites_remaining: "resource://gre/modules/lox_wasm.jsm",
+ get_trust_level: "resource://gre/modules/lox_wasm.jsm",
+ level_up: "resource://gre/modules/lox_wasm.jsm",
+ handle_level_up: "resource://gre/modules/lox_wasm.jsm",
+ trust_promotion: "resource://gre/modules/lox_wasm.jsm",
+ handle_trust_promotion: "resource://gre/modules/lox_wasm.jsm",
+ trust_migration: "resource://gre/modules/lox_wasm.jsm",
+ handle_trust_migration: "resource://gre/modules/lox_wasm.jsm",
+ get_next_unlock: "resource://gre/modules/lox_wasm.jsm",
+ check_blockage: "resource://gre/modules/lox_wasm.jsm",
+ handle_check_blockage: "resource://gre/modules/lox_wasm.jsm",
+ blockage_migration: "resource://gre/modules/lox_wasm.jsm",
+ handle_blockage_migration: "resource://gre/modules/lox_wasm.jsm",
+});
+
+export const LoxErrors = Object.freeze({
+ BadInvite: "BadInvite",
+ MissingCredential: "MissingCredential",
+ LoxServerUnreachable: "LoxServerUnreachable",
+ NoInvitations: "NoInvitations",
+ InitError: "InitializationError",
+ NotInitialized: "NotInitialized",
+});
+
+const LoxSettingsPrefs = Object.freeze({
+ /* string: the lox credential */
+ credentials: "lox.settings.credentials",
+ invites: "lox.settings.invites",
+ events: "lox.settings.events",
+ pubkeys: "lox.settings.pubkeys",
+ enctable: "lox.settings.enctable",
+ constants: "lox.settings.constants",
+});
+
+class LoxError extends Error {
+ constructor(type) {
+ super("");
+ this.type = type;
+ }
+}
+
+class LoxImpl {
+ #initialized = false;
+ #window = null;
+ #pubKeyPromise = null;
+ #encTablePromise = null;
+ #constantsPromise = null;
+ #domainFrontedRequests = null;
+ #invites = null;
+ #pubKeys = null;
+ #encTable = null;
+ #constants = null;
+ #credentials = null;
+ #events = [];
+ #backgroundInterval = null;
+
+ observe(subject, topic, data) {
+ switch (topic) {
+ case lazy.TorSettingsTopics.SettingsChanged:
+ const { changes } = subject.wrappedJSObject;
+ if (
+ changes.includes("bridges.enabled") ||
+ changes.includes("bridges.source") ||
+ changes.includes("bridges.lox_id")
+ ) {
+ // if lox_id has changed, clear event and invite queues
+ if (changes.includes("bridges.lox_id")) {
+ this.clearEventData();
+ this.clearInvites();
+ }
+
+ // Only run background tasks if Lox is enabled
+ if (this.#inuse) {
+ if (!this.#backgroundInterval) {
+ this.#backgroundInterval = setInterval(
+ this.#backgroundTasks.bind(this),
+ 1000 * 60 * 60 * 12
+ );
+ }
+ } else if (this.#backgroundInterval) {
+ clearInterval(this.#backgroundInterval);
+ this.#backgroundInterval = null;
+ }
+ }
+ break;
+ case lazy.TorSettingsTopics.Ready:
+ // Run background tasks every 12 hours if Lox is enabled
+ if (this.#inuse) {
+ this.#backgroundInterval = setInterval(
+ this.#backgroundTasks.bind(this),
+ 1000 * 60 * 60 * 12
+ );
+ }
+ break;
+ }
+ }
+
+ get #inuse() {
+ return (
+ lazy.TorSettings.bridges.enabled === true &&
+ lazy.TorSettings.bridges.source === lazy.TorBridgeSource.Lox &&
+ lazy.TorSettings.bridges.lox_id
+ );
+ }
+
+ /**
+ * Formats and returns bridges from the stored Lox credential
+ *
+ * @param {string} loxid The id string associated with a lox credential
+ *
+ * @returns {string[]} An array of formatted bridge lines. The array is empty
+ * if there are no bridges.
+ */
+ getBridges(loxid) {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ if (loxid === null) {
+ return [];
+ }
+ if (!this.#credentials[loxid]) {
+ // This lox id doesn't correspond to a stored credential
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ // Note: this is messy now but can be mostly removed after we have
+ // https://gitlab.torproject.org/tpo/anti-censorship/lox/-/issues/46
+ let bridgelines = JSON.parse(this.#credentials[loxid]).bridgelines;
+ let bridges = [];
+ for (const bridge of bridgelines) {
+ let addr = bridge.addr;
+ while (addr[addr.length - 1] === 0) {
+ addr.pop();
+ }
+ addr = new Uint8Array(addr);
+ let decoder = new TextDecoder("utf-8");
+ addr = decoder.decode(addr);
+
+ let info = bridge.info;
+ while (info[info.length - 1] === 0) {
+ info.pop();
+ }
+ info = new Uint8Array(info);
+ info = decoder.decode(info);
+
+ let regexpTransport = /type=([a-zA-Z0-9]*)/;
+ let transport = info.match(regexpTransport);
+ if (transport !== null) {
+ transport = transport[1];
+ } else {
+ transport = "";
+ }
+
+ let regexpFingerprint = /fingerprint=\"([a-zA-Z0-9]*)\"/;
+ let fingerprint = info.match(regexpFingerprint);
+ if (fingerprint !== null) {
+ fingerprint = fingerprint[1];
+ } else {
+ fingerprint = "";
+ }
+
+ let regexpParams = /params=Some\(\{(.*)\}\)/;
+ let params = info.match(regexpParams);
+ if (params !== null) {
+ params = params[1]
+ .replaceAll('"', "")
+ .replaceAll(": ", "=")
+ .replaceAll(",", " ");
+ } else {
+ params = "";
+ }
+
+ bridges.push(
+ `${transport} ${addr}:${bridge.port} ${fingerprint} ${params}`
+ );
+ }
+ return bridges;
+ }
+
+ #store() {
+ Services.prefs.setStringPref(LoxSettingsPrefs.pubkeys, this.#pubKeys);
+ Services.prefs.setStringPref(LoxSettingsPrefs.enctable, this.#encTable);
+ Services.prefs.setStringPref(LoxSettingsPrefs.constants, this.#constants);
+ Services.prefs.setStringPref(
+ LoxSettingsPrefs.credentials,
+ JSON.stringify(this.#credentials)
+ );
+ Services.prefs.setStringPref(
+ LoxSettingsPrefs.invites,
+ JSON.stringify(this.#invites)
+ );
+ Services.prefs.setStringPref(
+ LoxSettingsPrefs.events,
+ JSON.stringify(this.#events)
+ );
+ }
+
+ #load() {
+ if (this.#credentials === null) {
+ let cred = Services.prefs.getStringPref(LoxSettingsPrefs.credentials, "");
+ this.#credentials = cred !== "" ? JSON.parse(cred) : {};
+ let invites = Services.prefs.getStringPref(LoxSettingsPrefs.invites, "");
+ if (invites !== "") {
+ this.#invites = JSON.parse(invites);
+ }
+ let events = Services.prefs.getStringPref(LoxSettingsPrefs.events, "");
+ if (events !== "") {
+ this.#events = JSON.parse(events);
+ }
+ }
+ this.#pubKeys = Services.prefs.getStringPref(
+ LoxSettingsPrefs.pubkeys,
+ null
+ );
+ this.#encTable = Services.prefs.getStringPref(
+ LoxSettingsPrefs.enctable,
+ null
+ );
+ this.#constants = Services.prefs.getStringPref(
+ LoxSettingsPrefs.constants,
+ null
+ );
+ }
+
+ async #getPubKeys() {
+ if (this.#pubKeyPromise === null) {
+ this.#pubKeyPromise = this.#makeRequest("pubkeys", [])
+ .then(pubKeys => {
+ this.#pubKeys = JSON.stringify(pubKeys);
+ })
+ .catch(() => {
+ // We always try to update, but if that doesn't work fall back to stored data
+ if (!this.#pubKeys) {
+ throw new LoxError(LoxErrors.LoxServerUnreachable);
+ }
+ });
+ }
+ await this.#pubKeyPromise;
+ }
+
+ async #getEncTable() {
+ if (this.#encTablePromise === null) {
+ this.#encTablePromise = this.#makeRequest("reachability", [])
+ .then(encTable => {
+ this.#encTable = JSON.stringify(encTable);
+ })
+ .catch(() => {
+ // Try to update first, but if that doesn't work fall back to stored data
+ if (!this.#encTable) {
+ throw new LoxError(LoxErrors.LoxServerUnreachable);
+ }
+ });
+ }
+ await this.#encTablePromise;
+ }
+
+ async #getConstants() {
+ if (this.#constantsPromise === null) {
+ // Try to update first, but if that doesn't work fall back to stored data
+ this.#constantsPromise = this.#makeRequest("constants", [])
+ .then(constants => {
+ this.#constants = JSON.stringify(constants);
+ })
+ .catch(() => {
+ if (!this.#constants) {
+ throw new LoxError(LoxErrors.LoxServerUnreachable);
+ }
+ });
+ }
+ await this.#constantsPromise;
+ }
+
+ /**
+ * Check for blockages and attempt to perform a levelup
+ *
+ * If either blockages or a levelup happened, add an event to the event queue
+ */
+ async #backgroundTasks() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ const loxid = lazy.TorSettings.bridges.lox_id;
+ try {
+ const levelup = await this.#attemptUpgrade(loxid);
+ if (levelup) {
+ const level = lazy.get_trust_level(this.#credentials[loxid]);
+ const newEvent = {
+ type: "levelup",
+ newlevel: level,
+ };
+ this.#events.push(newEvent);
+ this.#store();
+ }
+ } catch (err) {
+ console.log(err);
+ }
+ try {
+ const leveldown = await this.#blockageMigration(loxid);
+ if (leveldown) {
+ let level = lazy.get_trust_level(this.#credentials[loxid]);
+ const newEvent = {
+ type: "blockage",
+ newlevel: level,
+ };
+ this.#events.push(newEvent);
+ this.#store();
+ }
+ } catch (err) {
+ console.log(err);
+ }
+ }
+
+ /**
+ * Generates a new random lox id to be associated with an invitation/credential
+ */
+ #genLoxId() {
+ return crypto.randomUUID();
+ }
+
+ async init() {
+ // If lox_id is set, load it
+ Services.obs.addObserver(this, lazy.TorSettingsTopics.SettingsChanged);
+ Services.obs.addObserver(this, lazy.TorSettingsTopics.Ready);
+
+ // Hack to make the generated wasm happy
+ this.#window = {
+ crypto,
+ };
+ this.#window.window = this.#window;
+ await lazy.init(this.#window);
+ lazy.set_panic_hook();
+ if (typeof lazy.open_invite !== "function") {
+ throw new LoxError(LoxErrors.InitError);
+ }
+ this.#invites = [];
+ this.#events = [];
+ this.#load();
+ this.#initialized = true;
+ }
+
+ async uninit() {
+ Services.obs.removeObserver(this, lazy.TorSettingsTopics.SettingsChanged);
+ Services.obs.removeObserver(this, lazy.TorSettingsTopics.Ready);
+ if (this.#domainFrontedRequests !== null) {
+ try {
+ const domainFronting = await this.#domainFrontedRequests;
+ domainFronting.uninit();
+ } catch {}
+ this.#domainFrontedRequests = null;
+ }
+ this.#initialized = false;
+ this.#window = null;
+ this.#invites = null;
+ this.#pubKeys = null;
+ this.#encTable = null;
+ this.#constants = null;
+ this.#pubKeyPromise = null;
+ this.#encTablePromise = null;
+ this.#constantsPromise = null;
+ this.#credentials = null;
+ this.#events = [];
+ if (this.#backgroundInterval) {
+ clearInterval(this.#backgroundInterval);
+ }
+ this.#backgroundInterval = null;
+ }
+
+ /**
+ * Parses an input string to check if it is a valid Lox invitation
+ *
+ * @param {string} invite A Lox invitation
+ * @returns {Promise<bool>} A promise that resolves to true if the value passed
+ * in was a Lox invitation and false if it is not
+ */
+ async validateInvitation(invite) {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ try {
+ await lazy.invitation_is_trusted(invite);
+ } catch (err) {
+ console.log(err);
+ return false;
+ }
+ return true;
+ }
+
+ // Note: This is only here for testing purposes. We're going to be using telegram
+ // to issue open invitations for Lox bridges.
+ async requestOpenInvite() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ let invite = await this.#makeRequest("invite", []);
+ console.log(invite);
+ return invite;
+ }
+
+ /**
+ * Redeems a Lox invitation to obtain a credential and bridges
+ *
+ * @param {string} invite A Lox invitation
+ * @returns {Promise<string>} A promise with the loxid of the associated credential on success
+ */
+ async redeemInvite(invite) {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ await this.#getPubKeys();
+ let request = await lazy.open_invite(invite.invite);
+ let id = this.#genLoxId();
+ let response = await this.#makeRequest(
+ "openreq",
+ JSON.parse(request).request
+ );
+ console.log("openreq response: ", response);
+ if (response.hasOwnProperty("error")) {
+ throw new LoxError(LoxErrors.BadInvite);
+ }
+ let cred = lazy.handle_new_lox_credential(
+ request,
+ JSON.stringify(response),
+ this.#pubKeys
+ );
+ this.#credentials[id] = cred;
+ this.#store();
+ return id;
+ }
+
+ /**
+ * Get metadata on all invites historically generated by this credential
+ *
+ * @returns {object[]} A list of all historical invites
+ */
+ getInvites() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ return this.#invites;
+ }
+
+ /**
+ * Generates a new trusted Lox invitation that a user can pass to their contacts
+ *
+ * @returns {Promise<string>} A promise that resolves to a valid Lox invitation. The promise
+ * will reject if:
+ * - there is no saved Lox credential
+ * - the saved credential does not have any invitations available
+ */
+ async generateInvite() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ const loxid = lazy.TorSettings.bridges.lox_id;
+ if (!loxid || !this.#credentials[loxid]) {
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ await this.#getPubKeys();
+ await this.#getEncTable();
+ let level = lazy.get_trust_level(this.#credentials[loxid]);
+ if (level < 1) {
+ throw new LoxError(LoxErrors.NoInvitations);
+ }
+ let request = lazy.issue_invite(
+ JSON.stringify(this.#credentials[loxid]),
+ this.#encTable,
+ this.#pubKeys
+ );
+ let response;
+ try {
+ response = await this.#makeRequest(
+ "issueinvite",
+ JSON.parse(request).request
+ );
+ } catch {
+ throw new LoxError(LoxErrors.LoxServerUnreachable);
+ }
+ if (response.hasOwnProperty("error")) {
+ console.log(response.error);
+ throw new LoxError(LoxErrors.NoInvitations);
+ } else {
+ this.#credentials[loxid] = response;
+ const invite = lazy.prepare_invite(response);
+ this.#invites.push(invite);
+ // cap length of stored invites
+ if (this.#invites.len > 50) {
+ this.#invites.shift();
+ }
+ return invite;
+ }
+ }
+
+ /**
+ * Get the number of invites that a user has remaining
+ *
+ * @returns {Promise<int>} A promise with the number of invites that can still be generated
+ * by a user's credential. This promise will reject if:
+ * - There is no credential
+ */
+ getRemainingInviteCount() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ const loxid = lazy.TorSettings.bridges.lox_id;
+ if (!loxid || !this.#credentials[loxid]) {
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ return parseInt(lazy.get_invites_remaining(this.#credentials[loxid]));
+ }
+
+ async #blockageMigration(loxid) {
+ if (!loxid || !this.#credentials[loxid]) {
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ await this.#getPubKeys();
+ let request;
+ try {
+ request = lazy.check_blockage(this.#credentials[loxid], this.#pubKeys);
+ } catch {
+ console.log("Not ready for blockage migration");
+ return false;
+ }
+ let response = await this.#makeRequest("checkblockage", request);
+ if (response.hasOwnProperty("error")) {
+ console.log(response.error);
+ throw new LoxError(LoxErrors.LoxServerUnreachable);
+ }
+ const migrationCred = lazy.handle_check_blockage(
+ this.#credentials[loxid],
+ JSON.stringify(response)
+ );
+ request = lazy.blockage_migration(
+ this.#credentials[loxid],
+ migrationCred,
+ this.#pubKeys
+ );
+ response = await this.#makeRequest("blockagemigration", request);
+ if (response.hasOwnProperty("error")) {
+ console.log(response.error);
+ throw new LoxError(LoxErrors.LoxServerUnreachable);
+ }
+ const cred = lazy.handle_blockage_migration(
+ this.#credentials[loxid],
+ JSON.stringify(response),
+ this.#pubKeys
+ );
+ this.#credentials[loxid] = cred;
+ this.#store();
+ return true;
+ }
+
+ /** Attempts to upgrade the currently saved Lox credential.
+ * If an upgrade is available, save an event in the event list.
+ *
+ * @returns {boolean} whether a levelup event occured
+ */
+ async #attemptUpgrade(loxid) {
+ if (!loxid || !this.#credentials[loxid]) {
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ await this.#getPubKeys();
+ await this.#getEncTable();
+ await this.#getConstants();
+ let success = false;
+ let level = lazy.get_trust_level(this.#credentials[loxid]);
+ if (level < 1) {
+ // attempt trust promotion instead
+ try {
+ success = await this.#trustMigration();
+ } catch (err) {
+ console.log(err);
+ return false;
+ }
+ } else {
+ let request = lazy.level_up(
+ this.#credentials[loxid],
+ this.#encTable,
+ this.#pubKeys
+ );
+ const response = await this.#makeRequest("levelup", request);
+ if (response.hasOwnProperty("error")) {
+ console.log(response.error);
+ throw new LoxError(LoxErrors.LoxServerUnreachable);
+ }
+ const cred = lazy.handle_level_up(
+ request,
+ JSON.stringify(response),
+ this.#pubKeys
+ );
+ this.#credentials[loxid] = cred;
+ return true;
+ }
+ return success;
+ }
+
+ /**
+ * Attempt to migrate from an untrusted to a trusted Lox credential
+ *
+ * @returns {Promise<bool>} A bool value indicated whether the credential
+ * was successfully migrated.
+ */
+ async #trustMigration() {
+ const loxid = lazy.TorSettings.bridges.lox_id;
+ if (!loxid || !this.#credentials[loxid]) {
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ await this.#getPubKeys();
+ return new Promise((resolve, reject) => {
+ let request = "";
+ try {
+ request = lazy.trust_promotion(this.#credentials[loxid], this.#pubKeys);
+ } catch (err) {
+ console.log("Not ready to upgrade");
+ resolve(false);
+ }
+ this.#makeRequest("trustpromo", JSON.parse(request).request)
+ .then(response => {
+ if (response.hasOwnProperty("error")) {
+ resolve(false);
+ }
+ console.log("Got promotion cred");
+ console.log(response);
+ console.log(request);
+ let promoCred = lazy.handle_trust_promotion(
+ request,
+ JSON.stringify(response)
+ );
+ console.log("Formatted promotion cred");
+ request = lazy.trust_migration(
+ this.#credentials[loxid],
+ promoCred,
+ this.#pubKeys
+ );
+ console.log("Formatted migration request");
+ this.#makeRequest("trustmig", JSON.parse(request).request)
+ .then(response => {
+ if (response.hasOwnProperty("error")) {
+ resolve(false);
+ }
+ console.log("Got new credential");
+ let cred = lazy.handle_trust_migration(request, response);
+ this.#credentials[loxid] = cred;
+ this.#store();
+ resolve(true);
+ })
+ .catch(err => {
+ console.log(err);
+ console.log("Failed trust migration");
+ resolve(false);
+ });
+ })
+ .catch(err => {
+ console.log(err);
+ console.log("Failed trust promotion");
+ resolve(false);
+ });
+ });
+ }
+
+ /**
+ * @typedef {object} EventData
+ *
+ * @property {string} [type] - the type of event. This should be one of:
+ * ("levelup", "blockage")
+ * @property {integer} [newlevel] - the new level, after the event. Levels count
+ * from 0, but "blockage" events can never take the user to 0, so this will always
+ * be 1 or greater.
+ */
+
+ /**
+ * Get a list of accumulated events
+ *
+ * @returns {Promise<EventData[]>} A promise with a list of the accumulated,
+ * unacknowledged events associated with a user's credential. This promise will reject if
+ * - There is no credential
+ */
+ getEventData() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ const loxid = lazy.TorSettings.bridges.lox_id;
+ if (!loxid || !this.#credentials[loxid]) {
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ return this.#events;
+ }
+
+ /**
+ * Clears accumulated event data
+ */
+ clearEventData() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ this.#events = [];
+ this.#store();
+ }
+
+ /**
+ * Clears accumulated invitations
+ */
+ clearInvites() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ this.#invites = [];
+ this.#store();
+ }
+
+ /**
+ * @typedef {object} UnlockData
+ *
+ * @property {string} date - The date-time for the next level up, formatted as YYYY-MM-DDTHH:mm:ssZ.
+ * @property {integer} nextLevel - The next level. Levels count from 0, so this will be 1 or greater.
+ *
+ */
+
+ /**
+ * Get dates at which access to new features will unlock
+ */
+ async getNextUnlock() {
+ if (!this.#initialized) {
+ throw new LoxError(LoxErrors.NotInitialized);
+ }
+ const loxid = lazy.TorSettings.bridges.lox_id;
+ if (!loxid || !this.#credentials[loxid]) {
+ throw new LoxError(LoxErrors.MissingCredential);
+ }
+ await this.#getConstants();
+ let nextUnlocks = JSON.parse(
+ lazy.get_next_unlock(this.#constants, this.#credentials[loxid])
+ );
+ const level = lazy.get_trust_level(this.#credentials[loxid]);
+ const unlocks = {
+ date: nextUnlocks.trust_level_unlock_date,
+ level: level + 1,
+ };
+ return unlocks;
+ }
+
+ async #makeRequest(procedure, args) {
+ // TODO: Customize to for Lox
+ const serviceUrl = "https://rdsys-frontend-01.torproject.org/lox";
+ const url = `${serviceUrl}/${procedure}`;
+
+ if (lazy.TorConnect.state === lazy.TorConnectState.Bootstrapped) {
+ const request = await fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/vnd.api+json",
+ },
+ body: JSON.stringify(args),
+ });
+ return request.json();
+ }
+
+ if (this.#domainFrontedRequests === null) {
+ this.#domainFrontedRequests = new Promise((resolve, reject) => {
+ // TODO: Customize to the values for Lox
+ const reflector = Services.prefs.getStringPref(
+ "extensions.torlauncher.bridgedb_reflector"
+ );
+ const front = Services.prefs.getStringPref(
+ "extensions.torlauncher.bridgedb_front"
+ );
+ const builder = new lazy.DomainFrontRequestBuilder();
+ builder
+ .init(reflector, front)
+ .then(() => resolve(builder))
+ .catch(reject);
+ });
+ }
+ const builder = await this.#domainFrontedRequests;
+ return builder.buildPostRequest(url, args);
+ }
+}
+
+export const Lox = new LoxImpl();
=====================================
toolkit/components/lox/content/lox_wasm_bg.wasm
=====================================
Binary files /dev/null and b/toolkit/components/lox/content/lox_wasm_bg.wasm differ
=====================================
toolkit/components/lox/jar.mn
=====================================
@@ -0,0 +1,2 @@
+toolkit.jar:
+ content/global/lox/lox_wasm_bg.wasm (content/lox_wasm_bg.wasm)
=====================================
toolkit/components/lox/lox_wasm.jsm
=====================================
@@ -0,0 +1,1217 @@
+var EXPORTED_SYMBOLS = ["set_panic_hook", "open_invite", "handle_new_lox_credential", "trust_promotion", "handle_trust_promotion", "trust_migration", "handle_trust_migration", "level_up", "handle_level_up", "issue_invite", "handle_issue_invite", "prepare_invite", "redeem_invite", "handle_redeem_invite", "check_blockage", "handle_check_blockage", "blockage_migration", "handle_blockage_migration", "get_last_upgrade_time", "get_trust_level", "get_invites_remaining", "get_issued_invite_expiry", "get_received_invite_expiry", "get_bridgelines_from_bucket", "invitation_is_trusted", "get_next_unlock", "init", "initSync"];
+
+let wasm;
+let module;
+
+const heap = new Array(128).fill(undefined);
+
+heap.push(undefined, null, true, false);
+
+function getObject(idx) { return heap[idx]; }
+
+let heap_next = heap.length;
+
+function dropObject(idx) {
+ if (idx < 132) return;
+ heap[idx] = heap_next;
+ heap_next = idx;
+}
+
+function takeObject(idx) {
+ const ret = getObject(idx);
+ dropObject(idx);
+ return ret;
+}
+
+const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
+
+if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
+
+let cachedUint8Memory0 = null;
+
+function getUint8Memory0() {
+ if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {
+ cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
+ }
+ return cachedUint8Memory0;
+}
+
+function getStringFromWasm0(ptr, len) {
+ ptr = ptr >>> 0;
+ return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
+}
+
+function addHeapObject(obj) {
+ if (heap_next === heap.length) heap.push(heap.length + 1);
+ const idx = heap_next;
+ heap_next = heap[idx];
+
+ heap[idx] = obj;
+ return idx;
+}
+/**
+*/
+function set_panic_hook() {
+ wasm.set_panic_hook();
+}
+
+let WASM_VECTOR_LEN = 0;
+
+function passArray8ToWasm0(arg, malloc) {
+ const ptr = malloc(arg.length * 1, 1) >>> 0;
+ getUint8Memory0().set(arg, ptr / 1);
+ WASM_VECTOR_LEN = arg.length;
+ return ptr;
+}
+
+let cachedInt32Memory0 = null;
+
+function getInt32Memory0() {
+ if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) {
+ cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
+ }
+ return cachedInt32Memory0;
+}
+/**
+* @param {Uint8Array} invite
+* @returns {string}
+*/
+function open_invite(invite) {
+ let deferred3_0;
+ let deferred3_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passArray8ToWasm0(invite, wasm.__wbindgen_malloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.open_invite(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr2 = r0;
+ var len2 = r1;
+ if (r3) {
+ ptr2 = 0; len2 = 0;
+ throw takeObject(r2);
+ }
+ deferred3_0 = ptr2;
+ deferred3_1 = len2;
+ return getStringFromWasm0(ptr2, len2);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
+ }
+}
+
+const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
+
+const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
+ ? function (arg, view) {
+ return cachedTextEncoder.encodeInto(arg, view);
+}
+ : function (arg, view) {
+ const buf = cachedTextEncoder.encode(arg);
+ view.set(buf);
+ return {
+ read: arg.length,
+ written: buf.length
+ };
+});
+
+function passStringToWasm0(arg, malloc, realloc) {
+
+ if (realloc === undefined) {
+ const buf = cachedTextEncoder.encode(arg);
+ const ptr = malloc(buf.length, 1) >>> 0;
+ getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
+ WASM_VECTOR_LEN = buf.length;
+ return ptr;
+ }
+
+ let len = arg.length;
+ let ptr = malloc(len, 1) >>> 0;
+
+ const mem = getUint8Memory0();
+
+ let offset = 0;
+
+ for (; offset < len; offset++) {
+ const code = arg.charCodeAt(offset);
+ if (code > 0x7F) break;
+ mem[ptr + offset] = code;
+ }
+
+ if (offset !== len) {
+ if (offset !== 0) {
+ arg = arg.slice(offset);
+ }
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
+ const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
+ const ret = encodeString(arg, view);
+
+ offset += ret.written;
+ }
+
+ WASM_VECTOR_LEN = offset;
+ return ptr;
+}
+/**
+* @param {string} open_lox_result
+* @param {string} open_lox_response
+* @param {string} lox_pub
+* @returns {string}
+*/
+function handle_new_lox_credential(open_lox_result, open_lox_response, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(open_lox_result, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(open_lox_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.handle_new_lox_credential(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} open_lox_cred
+* @param {string} lox_pub
+* @returns {string}
+*/
+function trust_promotion(open_lox_cred, lox_pub) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(open_lox_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.trust_promotion(retptr, ptr0, len0, ptr1, len1);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
+ }
+}
+
+/**
+* @param {string} trust_promo_request
+* @param {string} trust_promo_response
+* @returns {string}
+*/
+function handle_trust_promotion(trust_promo_request, trust_promo_response) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(trust_promo_request, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(trust_promo_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.handle_trust_promotion(retptr, ptr0, len0, ptr1, len1);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
+ }
+}
+
+/**
+* @param {string} open_lox_cred
+* @param {string} trust_promo_cred
+* @param {string} lox_pub
+* @returns {string}
+*/
+function trust_migration(open_lox_cred, trust_promo_cred, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(open_lox_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(trust_promo_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.trust_migration(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} trust_migration_request
+* @param {string} trust_migration_response
+* @param {string} lox_pub
+* @returns {string}
+*/
+function handle_trust_migration(trust_migration_request, trust_migration_response, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(trust_migration_request, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(trust_migration_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.handle_trust_migration(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} level_one_cred
+* @param {string} encrypted_table
+* @param {string} lox_pub
+* @returns {string}
+*/
+function level_up(level_one_cred, encrypted_table, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(level_one_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(encrypted_table, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.level_up(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} levelup_request
+* @param {string} levelup_response
+* @param {string} lox_pub
+* @returns {string}
+*/
+function handle_level_up(levelup_request, levelup_response, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(levelup_request, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(levelup_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.handle_level_up(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} trusted_cred
+* @param {string} encrypted_table
+* @param {string} lox_pub
+* @returns {string}
+*/
+function issue_invite(trusted_cred, encrypted_table, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(trusted_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(encrypted_table, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.issue_invite(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} issue_invite_request
+* @param {string} issue_invite_response
+* @param {string} lox_pub
+* @returns {string}
+*/
+function handle_issue_invite(issue_invite_request, issue_invite_response, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(issue_invite_request, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(issue_invite_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.handle_issue_invite(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} invitation_cred
+* @returns {string}
+*/
+function prepare_invite(invitation_cred) {
+ let deferred3_0;
+ let deferred3_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(invitation_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.prepare_invite(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr2 = r0;
+ var len2 = r1;
+ if (r3) {
+ ptr2 = 0; len2 = 0;
+ throw takeObject(r2);
+ }
+ deferred3_0 = ptr2;
+ deferred3_1 = len2;
+ return getStringFromWasm0(ptr2, len2);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
+ }
+}
+
+/**
+* @param {string} invitation
+* @param {string} lox_pub
+* @returns {string}
+*/
+function redeem_invite(invitation, lox_pub) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(invitation, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.redeem_invite(retptr, ptr0, len0, ptr1, len1);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
+ }
+}
+
+/**
+* @param {string} redeem_invite_request
+* @param {string} redeem_invite_response
+* @param {string} lox_pub
+* @returns {string}
+*/
+function handle_redeem_invite(redeem_invite_request, redeem_invite_response, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(redeem_invite_request, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(redeem_invite_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.handle_redeem_invite(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} lox_cred
+* @param {string} lox_pub
+* @returns {string}
+*/
+function check_blockage(lox_cred, lox_pub) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(lox_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.check_blockage(retptr, ptr0, len0, ptr1, len1);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
+ }
+}
+
+/**
+* @param {string} check_blockage_request
+* @param {string} check_blockage_response
+* @returns {string}
+*/
+function handle_check_blockage(check_blockage_request, check_blockage_response) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(check_blockage_request, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(check_blockage_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.handle_check_blockage(retptr, ptr0, len0, ptr1, len1);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
+ }
+}
+
+/**
+* @param {string} lox_cred
+* @param {string} check_migration_cred
+* @param {string} lox_pub
+* @returns {string}
+*/
+function blockage_migration(lox_cred, check_migration_cred, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(lox_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(check_migration_cred, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.blockage_migration(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} blockage_migration_request
+* @param {string} blockage_migration_response
+* @param {string} lox_pub
+* @returns {string}
+*/
+function handle_blockage_migration(blockage_migration_request, blockage_migration_response, lox_pub) {
+ let deferred5_0;
+ let deferred5_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(blockage_migration_request, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(blockage_migration_response, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ const ptr2 = passStringToWasm0(lox_pub, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len2 = WASM_VECTOR_LEN;
+ wasm.handle_blockage_migration(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr4 = r0;
+ var len4 = r1;
+ if (r3) {
+ ptr4 = 0; len4 = 0;
+ throw takeObject(r2);
+ }
+ deferred5_0 = ptr4;
+ deferred5_1 = len4;
+ return getStringFromWasm0(ptr4, len4);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
+ }
+}
+
+/**
+* @param {string} lox_cred_str
+* @returns {string}
+*/
+function get_last_upgrade_time(lox_cred_str) {
+ let deferred3_0;
+ let deferred3_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(lox_cred_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.get_last_upgrade_time(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr2 = r0;
+ var len2 = r1;
+ if (r3) {
+ ptr2 = 0; len2 = 0;
+ throw takeObject(r2);
+ }
+ deferred3_0 = ptr2;
+ deferred3_1 = len2;
+ return getStringFromWasm0(ptr2, len2);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
+ }
+}
+
+/**
+* @param {string} lox_cred_str
+* @returns {string}
+*/
+function get_trust_level(lox_cred_str) {
+ let deferred3_0;
+ let deferred3_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(lox_cred_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.get_trust_level(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr2 = r0;
+ var len2 = r1;
+ if (r3) {
+ ptr2 = 0; len2 = 0;
+ throw takeObject(r2);
+ }
+ deferred3_0 = ptr2;
+ deferred3_1 = len2;
+ return getStringFromWasm0(ptr2, len2);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
+ }
+}
+
+/**
+* @param {string} lox_cred_str
+* @returns {string}
+*/
+function get_invites_remaining(lox_cred_str) {
+ let deferred3_0;
+ let deferred3_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(lox_cred_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.get_invites_remaining(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr2 = r0;
+ var len2 = r1;
+ if (r3) {
+ ptr2 = 0; len2 = 0;
+ throw takeObject(r2);
+ }
+ deferred3_0 = ptr2;
+ deferred3_1 = len2;
+ return getStringFromWasm0(ptr2, len2);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
+ }
+}
+
+/**
+* @param {string} lox_cred_str
+* @returns {string}
+*/
+function get_issued_invite_expiry(lox_cred_str) {
+ let deferred3_0;
+ let deferred3_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(lox_cred_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.get_issued_invite_expiry(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr2 = r0;
+ var len2 = r1;
+ if (r3) {
+ ptr2 = 0; len2 = 0;
+ throw takeObject(r2);
+ }
+ deferred3_0 = ptr2;
+ deferred3_1 = len2;
+ return getStringFromWasm0(ptr2, len2);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
+ }
+}
+
+/**
+* @param {string} invite_cred_str
+* @returns {string}
+*/
+function get_received_invite_expiry(invite_cred_str) {
+ let deferred3_0;
+ let deferred3_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(invite_cred_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.get_received_invite_expiry(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr2 = r0;
+ var len2 = r1;
+ if (r3) {
+ ptr2 = 0; len2 = 0;
+ throw takeObject(r2);
+ }
+ deferred3_0 = ptr2;
+ deferred3_1 = len2;
+ return getStringFromWasm0(ptr2, len2);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
+ }
+}
+
+/**
+* @param {string} lox_cred_str
+* @param {string} encrypted_table
+* @returns {string}
+*/
+function get_bridgelines_from_bucket(lox_cred_str, encrypted_table) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(lox_cred_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(encrypted_table, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.get_bridgelines_from_bucket(retptr, ptr0, len0, ptr1, len1);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
+ }
+}
+
+/**
+* @param {string} unspecified_invitation_str
+* @returns {boolean}
+*/
+function invitation_is_trusted(unspecified_invitation_str) {
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(unspecified_invitation_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ wasm.invitation_is_trusted(retptr, ptr0, len0);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ if (r2) {
+ throw takeObject(r1);
+ }
+ return r0 !== 0;
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ }
+}
+
+/**
+* @param {string} constants_str
+* @param {string} lox_cred_str
+* @returns {string}
+*/
+function get_next_unlock(constants_str, lox_cred_str) {
+ let deferred4_0;
+ let deferred4_1;
+ try {
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+ const ptr0 = passStringToWasm0(constants_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len0 = WASM_VECTOR_LEN;
+ const ptr1 = passStringToWasm0(lox_cred_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ wasm.get_next_unlock(retptr, ptr0, len0, ptr1, len1);
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
+ var ptr3 = r0;
+ var len3 = r1;
+ if (r3) {
+ ptr3 = 0; len3 = 0;
+ throw takeObject(r2);
+ }
+ deferred4_0 = ptr3;
+ deferred4_1 = len3;
+ return getStringFromWasm0(ptr3, len3);
+ } finally {
+ wasm.__wbindgen_add_to_stack_pointer(16);
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
+ }
+}
+
+function handleError(f, args) {
+ try {
+ return f.apply(this, args);
+ } catch (e) {
+ wasm.__wbindgen_exn_store(addHeapObject(e));
+ }
+}
+
+async function __wbg_load(module, imports) {
+ if (typeof Response === 'function' && module instanceof Response) {
+ if (typeof WebAssembly.instantiateStreaming === 'function') {
+ try {
+ return await WebAssembly.instantiateStreaming(module, imports);
+
+ } catch (e) {
+ if (module.headers.get('Content-Type') != 'application/wasm') {
+ console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
+
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ const bytes = await module.arrayBuffer();
+ return await WebAssembly.instantiate(bytes, imports);
+
+ } else {
+ const instance = await WebAssembly.instantiate(module, imports);
+
+ if (instance instanceof WebAssembly.Instance) {
+ return { instance, module };
+
+ } else {
+ return instance;
+ }
+ }
+}
+
+function __wbg_get_imports(window) {
+ const imports = {};
+ imports.wbg = {};
+ imports.wbg.__wbg_new0_622c21a64f3d83ea = function() {
+ const ret = new Date();
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_getTime_9272be78826033e1 = function(arg0) {
+ const ret = getObject(arg0).getTime();
+ return ret;
+ };
+ imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
+ takeObject(arg0);
+ };
+ imports.wbg.__wbg_log_9b9925d843c39805 = function(arg0, arg1) {
+ console.log(getStringFromWasm0(arg0, arg1));
+ };
+ imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
+ const ret = getStringFromWasm0(arg0, arg1);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_new_abda76e883ba8a5f = function() {
+ const ret = new Error();
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) {
+ const ret = getObject(arg1).stack;
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+ const len1 = WASM_VECTOR_LEN;
+ getInt32Memory0()[arg0 / 4 + 1] = len1;
+ getInt32Memory0()[arg0 / 4 + 0] = ptr1;
+ };
+ imports.wbg.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) {
+ let deferred0_0;
+ let deferred0_1;
+ try {
+ deferred0_0 = arg0;
+ deferred0_1 = arg1;
+ console.error(getStringFromWasm0(arg0, arg1));
+ } finally {
+ wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
+ }
+ };
+ imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
+ const ret = getObject(arg0);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_crypto_c48a774b022d20ac = function(arg0) {
+ const ret = getObject(arg0).crypto;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_is_object = function(arg0) {
+ const val = getObject(arg0);
+ const ret = typeof(val) === 'object' && val !== null;
+ return ret;
+ };
+ imports.wbg.__wbg_process_298734cf255a885d = function(arg0) {
+ const ret = getObject(arg0).process;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_versions_e2e78e134e3e5d01 = function(arg0) {
+ const ret = getObject(arg0).versions;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_node_1cd7a5d853dbea79 = function(arg0) {
+ const ret = getObject(arg0).node;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbindgen_is_string = function(arg0) {
+ const ret = typeof(getObject(arg0)) === 'string';
+ return ret;
+ };
+ imports.wbg.__wbg_require_8f08ceecec0f4fee = function() { return handleError(function () {
+ const ret = module.require;
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbindgen_is_function = function(arg0) {
+ const ret = typeof(getObject(arg0)) === 'function';
+ return ret;
+ };
+ imports.wbg.__wbg_call_5da1969d7cd31ccd = function() { return handleError(function (arg0, arg1, arg2) {
+ const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbg_msCrypto_bcb970640f50a1e8 = function(arg0) {
+ const ret = getObject(arg0).msCrypto;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_newwithlength_6c2df9e2f3028c43 = function(arg0) {
+ const ret = new Uint8Array(arg0 >>> 0);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_self_f0e34d89f33b99fd = function() { return handleError(function () {
+ const ret = window;
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbg_window_d3b084224f4774d7 = function() { return handleError(function () {
+ const ret = window.window;
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbg_globalThis_9caa27ff917c6860 = function() { return handleError(function () {
+ const ret = globalThis.globalThis;
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbg_global_35dfdd59a4da3e74 = function() { return handleError(function () {
+ const ret = global.global;
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbindgen_is_undefined = function(arg0) {
+ const ret = getObject(arg0) === undefined;
+ return ret;
+ };
+ imports.wbg.__wbg_newnoargs_c62ea9419c21fbac = function(arg0, arg1) {
+ const ret = new Function(getStringFromWasm0(arg0, arg1));
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_call_90c26b09837aba1c = function() { return handleError(function (arg0, arg1) {
+ const ret = getObject(arg0).call(getObject(arg1));
+ return addHeapObject(ret);
+ }, arguments) };
+ imports.wbg.__wbindgen_memory = function() {
+ const ret = wasm.memory;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_buffer_a448f833075b71ba = function(arg0) {
+ const ret = getObject(arg0).buffer;
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_newwithbyteoffsetandlength_d0482f893617af71 = function(arg0, arg1, arg2) {
+ const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_randomFillSync_dc1e9a60c158336d = function() { return handleError(function (arg0, arg1) {
+ getObject(arg0).randomFillSync(takeObject(arg1));
+ }, arguments) };
+ imports.wbg.__wbg_subarray_2e940e41c0f5a1d9 = function(arg0, arg1, arg2) {
+ const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0);
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_getRandomValues_37fa2ca9e4e07fab = function() { return handleError(function (arg0, arg1) {
+ getObject(arg0).getRandomValues(getObject(arg1));
+ }, arguments) };
+ imports.wbg.__wbg_new_8f67e318f15d7254 = function(arg0) {
+ const ret = new Uint8Array(getObject(arg0));
+ return addHeapObject(ret);
+ };
+ imports.wbg.__wbg_set_2357bf09366ee480 = function(arg0, arg1, arg2) {
+ getObject(arg0).set(getObject(arg1), arg2 >>> 0);
+ };
+ imports.wbg.__wbindgen_throw = function(arg0, arg1) {
+ throw new Error(getStringFromWasm0(arg0, arg1));
+ };
+
+ return imports;
+}
+
+function __wbg_init_memory(imports, maybe_memory) {
+
+}
+
+function __wbg_finalize_init(instance, module) {
+ wasm = instance.exports;
+ init.__wbindgen_wasm_module = module;
+ cachedInt32Memory0 = null;
+ cachedUint8Memory0 = null;
+
+
+ return wasm;
+}
+
+function initSync(module, window) {
+ if (wasm !== undefined) return wasm;
+
+ const imports = __wbg_get_imports(window);
+
+ __wbg_init_memory(imports);
+
+ if (!(module instanceof WebAssembly.Module)) {
+ module = new WebAssembly.Module(module);
+ }
+
+ const instance = new WebAssembly.Instance(module, imports);
+
+ return __wbg_finalize_init(instance, module);
+}
+
+async function init(window, input) {
+ if (wasm !== undefined) return wasm;
+
+ if (typeof input === 'undefined') {{
+ input = "chrome://global/content/lox/lox_wasm_bg.wasm";
+ }}
+ const imports = __wbg_get_imports(window);
+
+ if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
+ input = fetch(input);
+ }
+
+ __wbg_init_memory(imports);
+
+ const { instance, module } = await __wbg_load(await input, imports);
+
+ return __wbg_finalize_init(instance, module);
+}
+
=====================================
toolkit/components/lox/moz.build
=====================================
@@ -0,0 +1,7 @@
+EXTRA_JS_MODULES += [
+ "Lox.sys.mjs",
+ # Let's keep the old jsm format until wasm-bindgen is updated
+ "lox_wasm.jsm",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
=====================================
toolkit/components/moz.build
=====================================
@@ -46,6 +46,7 @@ DIRS += [
"httpsonlyerror",
"jsoncpp/src/lib_json",
"kvstore",
+ "lox",
"mediasniffer",
"mozintl",
"mozprotocol",
=====================================
toolkit/components/tor-launcher/TorStartupService.sys.mjs
=====================================
@@ -54,5 +54,6 @@ export class TorStartupService {
lazy.TorProviderBuilder.uninit();
lazy.TorLauncherUtil.cleanupTempDirectories();
+ lazy.TorSettings.uninit();
}
}
=====================================
toolkit/modules/TorSettings.sys.mjs
=====================================
@@ -8,6 +8,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ Lox: "resource://gre/modules/Lox.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "logger", () => {
@@ -40,6 +41,8 @@ const TorSettingsPrefs = Object.freeze({
enabled: "torbrowser.settings.bridges.enabled",
/* int: See TorBridgeSource */
source: "torbrowser.settings.bridges.source",
+ /* string: output of crypto.randomUUID() */
+ lox_id: "torbrowser.settings.bridges.lox_id",
/* string: obfs4|meek-azure|snowflake|etc */
builtin_type: "torbrowser.settings.bridges.builtin_type",
/* preference branch: each child branch should be a bridge string */
@@ -86,6 +89,7 @@ export const TorBridgeSource = Object.freeze({
BuiltIn: 0,
BridgeDB: 1,
UserProvided: 2,
+ Lox: 3,
});
export const TorProxyType = Object.freeze({
@@ -159,6 +163,7 @@ class TorSettingsImpl {
bridges: {
enabled: false,
source: TorBridgeSource.Invalid,
+ lox_id: "",
builtin_type: "",
bridge_strings: [],
},
@@ -292,6 +297,20 @@ class TorSettingsImpl {
this.bridges.source = TorBridgeSource.Invalid;
},
},
+ /**
+ * The lox id is used with the Lox "source", and remains set with the stored value when
+ * other sources are used.
+ *
+ * @type {string}
+ */
+ lox_id: {
+ callback: (val, addError) => {
+ if (!val) {
+ return;
+ }
+ this.bridges.bridge_strings = lazy.Lox.getBridges(val);
+ },
+ },
});
this.#addProperties("proxy", {
enabled: {},
@@ -379,6 +398,9 @@ class TorSettingsImpl {
if (this.bridges.source !== TorBridgeSource.BuiltIn) {
this.bridges.builtin_type = "";
}
+ if (this.bridges.source !== TorBridgeSource.Lox) {
+ this.bridges.lox_id = "";
+ }
if (!this.proxy.enabled) {
this.proxy.type = TorProxyType.Invalid;
this.proxy.address = "";
@@ -639,6 +661,14 @@ class TorSettingsImpl {
lazy.logger.error("Could not load the built-in PT config.", e);
}
+ // Initialize this before loading from prefs because we need Lox initialized before
+ // any calls to Lox.getBridges()
+ try {
+ await lazy.Lox.init();
+ } catch (e) {
+ lazy.logger.error("Could not initialize Lox.", e.type);
+ }
+
// TODO: We could use a shared promise, and wait for it to be fullfilled
// instead of Service.obs.
if (lazy.TorLauncherUtil.shouldStartAndOwnTor) {
@@ -668,6 +698,13 @@ class TorSettingsImpl {
}
}
+ /**
+ * Unload or uninit our settings, and unregister observers.
+ */
+ async uninit() {
+ await lazy.Lox.uninit();
+ }
+
/**
* Check whether the object has been successfully initialized, and throw if
* it has not.
@@ -738,6 +775,10 @@ class TorSettingsImpl {
TorSettingsPrefs.bridges.source,
TorBridgeSource.Invalid
);
+ this.bridges.lox_id = Services.prefs.getStringPref(
+ TorSettingsPrefs.bridges.lox_id,
+ ""
+ );
if (this.bridges.source == TorBridgeSource.BuiltIn) {
this.bridges.builtin_type = Services.prefs.getStringPref(
TorSettingsPrefs.bridges.builtin_type,
@@ -823,6 +864,10 @@ class TorSettingsImpl {
TorSettingsPrefs.bridges.builtin_type,
this.bridges.builtin_type
);
+ Services.prefs.setStringPref(
+ TorSettingsPrefs.bridges.lox_id,
+ this.bridges.lox_id
+ );
// erase existing bridge strings
const bridgeBranchPrefs = Services.prefs
.getBranch(TorSettingsPrefs.bridges.bridge_strings)
@@ -1013,6 +1058,9 @@ class TorSettingsImpl {
case TorBridgeSource.BuiltIn:
this.bridges.builtin_type = settings.bridges.builtin_type;
break;
+ case TorBridgeSource.Lox:
+ this.bridges.lox_id = settings.bridges.lox_id;
+ break;
case TorBridgeSource.Invalid:
break;
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/a48e43…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/a48e43…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.7.0esr-13.5-1] 2 commits: fixup! Bug 40597: Implement TorSettings module
by richard (@richard) 26 Jan '24
by richard (@richard) 26 Jan '24
26 Jan '24
richard pushed to branch tor-browser-115.7.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
bb028644 by Pier Angelo Vendrame at 2024-01-26T11:31:15+01:00
fixup! Bug 40597: Implement TorSettings module
Bug 42384: Use TorSettings's initialization promise to handle race
conditions
We now have a race condition for which the notification about
TorSettings being ready is sent before TorConnect can observe this
topic.
A while ago, we introduced an initialization promise to TorSettings,
therefore we can use it instead of Service.obs and avoid this race
condition.
Also, fixed quicksettings ignored in TorSettings.setSettings.
- - - - -
a48e4389 by Pier Angelo Vendrame at 2024-01-26T11:35:10+01:00
fixup! Bug 42247: Android helpers for the TorProvider
Update the cached mSettings whenever someone one the GeckoView side
changes any TorSettings settings.
- - - - -
4 changed files:
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
- toolkit/modules/TorAndroidIntegration.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
- toolkit/modules/TorSettings.sys.mjs
Changes:
=====================================
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
=====================================
@@ -47,6 +47,8 @@ public class TorIntegrationAndroid implements BundleEventListener {
private static final String EVENT_BOOTSTRAP_COMPLETE = "GeckoView:Tor:BootstrapComplete";
private static final String EVENT_BOOTSTRAP_ERROR = "GeckoView:Tor:BootstrapError";
private static final String EVENT_SETTINGS_OPEN = "GeckoView:Tor:OpenSettings";
+ private static final String EVENT_SETTINGS_READY = "GeckoView:Tor:SettingsReady";
+ private static final String EVENT_SETTINGS_CHANGED = "GeckoView:Tor:SettingsChanged";
// Events we emit
private static final String EVENT_SETTINGS_GET = "GeckoView:Tor:SettingsGet";
@@ -57,7 +59,6 @@ public class TorIntegrationAndroid implements BundleEventListener {
private static final String EVENT_BOOTSTRAP_BEGIN_AUTO = "GeckoView:Tor:BootstrapBeginAuto";
private static final String EVENT_BOOTSTRAP_CANCEL = "GeckoView:Tor:BootstrapCancel";
private static final String EVENT_BOOTSTRAP_GET_STATE = "GeckoView:Tor:BootstrapGetState";
- private static final String EVENT_SETTINGS_READY = "GeckoView:Tor:SettingsReady";
private static final String CONTROL_PORT_FILE = "/control-ipc";
private static final String SOCKS_FILE = "/socks-ipc";
@@ -112,6 +113,7 @@ public class TorIntegrationAndroid implements BundleEventListener {
EVENT_MEEK_START,
EVENT_MEEK_STOP,
EVENT_SETTINGS_READY,
+ EVENT_SETTINGS_CHANGED,
EVENT_BOOTSTRAP_STATE_CHANGED,
EVENT_BOOTSTRAP_PROGRESS,
EVENT_BOOTSTRAP_COMPLETE,
@@ -136,6 +138,14 @@ public class TorIntegrationAndroid implements BundleEventListener {
} catch(Exception e) {
Log.e(TAG, "SettingsLoader error: "+ e.toString());
}
+ } else if (EVENT_SETTINGS_CHANGED.equals(event)) {
+ GeckoBundle newSettings = message.getBundle("settings");
+ if (newSettings != null) {
+ // TODO: Should we notify listeners?
+ mSettings = new TorSettings(newSettings);
+ } else {
+ Log.w(TAG, "Ignoring a settings changed event that did not have the new settings.");
+ }
} else if (EVENT_BOOTSTRAP_STATE_CHANGED.equals(event)) {
String state = message.getString("state");
for (BootstrapStateChangeListener listener: mBootstrapStateListeners) {
=====================================
toolkit/modules/TorAndroidIntegration.sys.mjs
=====================================
@@ -124,6 +124,15 @@ class TorAndroidIntegrationImpl {
settings: lazy.TorSettings.getSettings(),
});
break;
+ case lazy.TorSettingsTopics.SettingsChanged:
+ // For Android we push also the settings object to avoid a round trip on
+ // the event dispatcher.
+ lazy.EventDispatcher.instance.sendRequest({
+ type: EmittedEvents.settingsChanged,
+ changes: subj.wrappedJSObject.changes ?? [],
+ settings: lazy.TorSettings.getSettings(),
+ });
+ break;
}
}
=====================================
toolkit/modules/TorConnect.sys.mjs
=====================================
@@ -878,10 +878,11 @@ export const TorConnect = (() => {
console.log(`TorConnect: Observing topic '${addTopic}'`);
};
+ TorSettings.initializedPromise.then(() => this._settingsInitialized());
+
// register the Tor topics we always care about
observeTopic(TorTopics.ProcessExited);
observeTopic(TorTopics.LogHasWarnOrErr);
- observeTopic(TorSettingsTopics.Ready);
}
},
@@ -889,29 +890,6 @@ export const TorConnect = (() => {
console.log(`TorConnect: Observed ${topic}`);
switch (topic) {
- /* We need to wait until TorSettings have been loaded and applied before we can Quickstart */
- case TorSettingsTopics.Ready: {
- // tor-browser#41907: This is only a workaround to avoid users being
- // bounced back to the initial panel without any explanation.
- // Longer term we should disable the clickable elements, or find a UX
- // to prevent this from happening (e.g., allow buttons to be clicked,
- // but show an intermediate starting state, or a message that tor is
- // starting while the butons are disabled, etc...).
- if (this.state !== TorConnectState.Initial) {
- console.warn(
- "TorConnect: Seen the torsettings:ready after the state has already changed, ignoring the notification."
- );
- break;
- }
- if (this.shouldQuickStart) {
- // Quickstart
- this._changeState(TorConnectState.Bootstrapping);
- } else {
- // Configuring
- this._changeState(TorConnectState.Configuring);
- }
- break;
- }
case TorTopics.LogHasWarnOrErr: {
this._logHasWarningOrError = true;
break;
@@ -941,6 +919,28 @@ export const TorConnect = (() => {
}
},
+ _settingsInitialized() {
+ // tor-browser#41907: This is only a workaround to avoid users being
+ // bounced back to the initial panel without any explanation.
+ // Longer term we should disable the clickable elements, or find a UX
+ // to prevent this from happening (e.g., allow buttons to be clicked,
+ // but show an intermediate starting state, or a message that tor is
+ // starting while the butons are disabled, etc...).
+ if (this.state !== TorConnectState.Initial) {
+ console.warn(
+ "TorConnect: Seen the torsettings:ready after the state has already changed, ignoring the notification."
+ );
+ return;
+ }
+ if (this.shouldQuickStart) {
+ // Quickstart
+ this._changeState(TorConnectState.Bootstrapping);
+ } else {
+ // Configuring
+ this._changeState(TorConnectState.Configuring);
+ }
+ },
+
/*
Various getters
*/
=====================================
toolkit/modules/TorSettings.sys.mjs
=====================================
@@ -992,6 +992,10 @@ class TorSettingsImpl {
// Hold off on lots of notifications until all settings are changed.
this.freezeNotifications();
try {
+ if ("quickstart" in settings) {
+ this.quickstart.enabled = !!settings.quickstart.enabled;
+ }
+
if ("bridges" in settings) {
this.bridges.enabled = !!settings.bridges.enabled;
// Currently, disabling bridges in the UI does not remove the lines,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/db59cb…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/db59cb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/firefox-android][firefox-android-115.2.1-13.5-1] fixup! Add Tor integration and UI
by Dan Ballard (@dan) 26 Jan '24
by Dan Ballard (@dan) 26 Jan '24
26 Jan '24
Dan Ballard pushed to branch firefox-android-115.2.1-13.5-1 at The Tor Project / Applications / firefox-android
Commits:
6d0a6453 by Dan Ballard at 2024-01-25T16:15:45-08:00
fixup! Add Tor integration and UI
Bug 42252: Make TorController and interface and add a Geckoview implementation
- - - - -
4 changed files:
- fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt
- fenix/app/src/main/java/org/mozilla/fenix/tor/TorController.kt
- + fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt
- + fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerTAS.kt
Changes:
=====================================
fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt
=====================================
@@ -43,7 +43,8 @@ import org.mozilla.fenix.perf.StartupActivityLog
import org.mozilla.fenix.perf.StartupStateProvider
import org.mozilla.fenix.perf.StrictModeManager
import org.mozilla.fenix.perf.lazyMonitored
-import org.mozilla.fenix.tor.TorController
+import org.mozilla.fenix.tor.TorControllerGV
+import org.mozilla.fenix.tor.TorControllerTAS
import org.mozilla.fenix.utils.ClipboardHandler
import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.wifi.WifiConnectionMonitor
@@ -201,7 +202,7 @@ class Components(private val context: Context) {
),
)
}
- val torController by lazyMonitored { TorController(context) }
+ val torController by lazyMonitored { if (settings.useNewBootstrap) TorControllerGV(context) else TorControllerTAS(context) }
}
/**
=====================================
fenix/app/src/main/java/org/mozilla/fenix/tor/TorController.kt
=====================================
@@ -4,22 +4,7 @@
package org.mozilla.fenix.tor
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
import androidx.lifecycle.LifecycleCoroutineScope
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withTimeoutOrNull
-
-import org.mozilla.fenix.BuildConfig
-
-import org.torproject.android.service.TorService
-import org.torproject.android.service.TorServiceConstants
-import org.torproject.android.service.util.Prefs
interface TorEvents {
fun onTorConnecting()
@@ -28,347 +13,59 @@ interface TorEvents {
fun onTorStopped()
}
-private enum class TorStatus {
- OFF,
- STARTING,
- ON,
- STOPPING,
- UNKNOWN;
+internal enum class TorStatus(val status: String) {
+ OFF("OFF"),
+ STARTING("STARTING"),
+ ON("ON"),
+ STOPPING("STOPPING"),
+ UNKNOWN("UNKNOWN");
- fun getStateFromString(status: String): TorStatus {
- return when (status) {
- TorServiceConstants.STATUS_ON -> ON
- TorServiceConstants.STATUS_STARTING -> STARTING
- TorServiceConstants.STATUS_STOPPING -> STOPPING
- TorServiceConstants.STATUS_OFF -> OFF
- else -> UNKNOWN
+ companion object {
+ fun fromString(status: String): TorStatus {
+ return when (status) {
+ "ON" -> ON
+ "STARTING" -> STARTING
+ "STOPPING" -> STOPPING
+ "OFF" -> OFF
+ else -> UNKNOWN
+ }
}
}
fun isOff() = this == OFF
fun isOn() = this == ON
fun isStarting() = this == STARTING
- fun isStarted() = ((this == TorStatus.STARTING) || (this == TorStatus.ON))
+ fun isStarted() = ((this == STARTING) || (this == ON))
fun isStopping() = this == STOPPING
fun isUnknown() = this == UNKNOWN
}
-@SuppressWarnings("TooManyFunctions")
-class TorController(
- private val context: Context
-) : TorEvents {
-
- private val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
- private val entries = mutableListOf<Pair<String?, String?>>()
- val logEntries get() = entries
-
- private var torListeners = mutableListOf<TorEvents>()
-
- private var pendingRegisterChangeList = mutableListOf<Pair<TorEvents, Boolean>>()
- private var lockTorListenersMutation = false
-
- private var lastKnownStatus = TorStatus.OFF
- private var wasTorBootstrapped = false
- private var isTorRestarting = false
-
- // This may be a lie
- private var isTorBootstrapped = false
- get() = ((lastKnownStatus == TorStatus.ON) && wasTorBootstrapped)
-
- val isDebugLoggingEnabled get() =
- context
- .getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
- .getBoolean("pref_enable_logging", false)
-
- val isStarting get() = lastKnownStatus.isStarting()
- val isRestarting get() = isTorRestarting
- val isBootstrapped get() = isTorBootstrapped
- val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
-
+interface TorController: TorEvents {
+ val logEntries: MutableList<Pair<String?, String?>>
+ val isStarting: Boolean
+ val isRestarting: Boolean
+ val isBootstrapped: Boolean
+ val isConnected: Boolean
var bridgesEnabled: Boolean
- get() = Prefs.bridgesEnabled()
- set(value) { Prefs.putBridgesEnabled(value) }
-
var bridgeTransport: TorBridgeTransportConfig
- get() {
- return TorBridgeTransportConfigUtil.getStringToBridgeTransport(
- Prefs.getBridgesList()
- )
- }
- set(value) {
- if (value == TorBridgeTransportConfig.USER_PROVIDED) {
- // Don't set the pref when the value is USER_PROVIDED because
- // "user_provided" is not a valid bridge or transport type.
- // This call should be followed by setting userProvidedBridges.
- return
- }
- Prefs.setBridgesList(value.transportName)
- }
-
var userProvidedBridges: String?
- get() {
- val bridges = Prefs.getBridgesList()
- val bridgeType =
- TorBridgeTransportConfigUtil.getStringToBridgeTransport(bridges)
- return when (bridgeType) {
- TorBridgeTransportConfig.USER_PROVIDED -> bridges
- else -> null
- }
- }
- set(value) {
- Prefs.setBridgesList(value)
- }
-
- fun start() {
- // Register receiver
- lbm.registerReceiver(
- persistentBroadcastReceiver,
- IntentFilter(TorServiceConstants.ACTION_STATUS)
- )
- lbm.registerReceiver(
- persistentBroadcastReceiver,
- IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG)
- )
- }
- fun stop() {
- lbm.unregisterReceiver(persistentBroadcastReceiver)
- }
+ fun start()
+ fun stop()
- private val persistentBroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action == null ||
- (intent.action != TorServiceConstants.ACTION_STATUS &&
- intent.action != TorServiceConstants.LOCAL_ACTION_LOG)
- ) {
- return
- }
- val action = intent.action
+ override fun onTorConnecting()
+ override fun onTorConnected()
+ override fun onTorStatusUpdate(entry: String?, status: String?)
+ override fun onTorStopped()
- val logentry: String?
- val status: String?
- if (action == TorServiceConstants.LOCAL_ACTION_LOG) {
- logentry = intent.getExtras()
- ?.getCharSequence(TorServiceConstants.LOCAL_EXTRA_LOG) as? String?
- } else {
- logentry = null
- }
-
- status = intent.getExtras()
- ?.getCharSequence(TorServiceConstants.EXTRA_STATUS) as? String?
-
- if (logentry == null && status == null) {
- return
- }
-
- onTorStatusUpdate(logentry, status)
-
- if (status == null) {
- return
- }
-
- val newStatus = lastKnownStatus.getStateFromString(status)
-
- if (newStatus.isUnknown() && wasTorBootstrapped) {
- stopTor()
- }
-
- entries.add(Pair(logentry, status))
-
- if (logentry != null && logentry.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) {
- wasTorBootstrapped = true
- onTorConnected()
- }
-
- if (lastKnownStatus.isStopping() && newStatus.isOff()) {
- if (isTorRestarting) {
- initiateTorBootstrap()
- } else {
- onTorStopped()
- }
- }
-
- if (lastKnownStatus.isOff() && newStatus.isStarting()) {
- isTorRestarting = false
- }
-
- lastKnownStatus = newStatus
- }
- }
-
- override fun onTorConnecting() {
- lockTorListenersMutation = true
- torListeners.forEach { it.onTorConnecting() }
- lockTorListenersMutation = false
-
- handlePendingRegistrationChanges()
- }
-
- override fun onTorConnected() {
- lockTorListenersMutation = true
- torListeners.forEach { it.onTorConnected() }
- lockTorListenersMutation = false
-
- handlePendingRegistrationChanges()
- }
+ fun registerTorListener(l: TorEvents)
+ fun unregisterTorListener(l: TorEvents)
- override fun onTorStatusUpdate(entry: String?, status: String?) {
- lockTorListenersMutation = true
- torListeners.forEach { it.onTorStatusUpdate(entry, status) }
- lockTorListenersMutation = false
-
- handlePendingRegistrationChanges()
- }
-
- override fun onTorStopped() {
- lockTorListenersMutation = true
- torListeners.forEach { it.onTorStopped() }
- lockTorListenersMutation = false
-
- handlePendingRegistrationChanges()
- }
-
- fun registerTorListener(l: TorEvents) {
- if (torListeners.contains(l)) {
- return
- }
-
- if (lockTorListenersMutation) {
- pendingRegisterChangeList.add(Pair(l, true))
- } else {
- torListeners.add(l)
- }
- }
-
- fun unregisterTorListener(l: TorEvents) {
- if (!torListeners.contains(l)) {
- return
- }
-
- if (lockTorListenersMutation) {
- pendingRegisterChangeList.add(Pair(l, false))
- } else {
- torListeners.remove(l)
- }
- }
-
- private fun handlePendingRegistrationChanges() {
- pendingRegisterChangeList.forEach {
- if (it.second) {
- registerTorListener(it.first)
- } else {
- unregisterTorListener(it.first)
- }
- }
-
- pendingRegisterChangeList.clear()
- }
-
- /**
- * Receive the current Tor status.
- *
- * Send a request for the current status and receive the response.
- * Returns true if Tor is running, false otherwise.
- *
- */
- private suspend fun checkTorIsStarted(): Boolean {
- val channel = Channel<Boolean>()
-
- // Register receiver
- val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
- val localBroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- val action = intent.action ?: return
- // We only want ACTION_STATUS messages
- if (action != TorServiceConstants.ACTION_STATUS) {
- return
- }
- // The current status has the EXTRA_STATUS key
- val currentStatus =
- intent.getStringExtra(TorServiceConstants.EXTRA_STATUS)
- channel.trySend(currentStatus === TorServiceConstants.STATUS_ON)
- }
- }
- lbm.registerReceiver(
- localBroadcastReceiver,
- IntentFilter(TorServiceConstants.ACTION_STATUS)
- )
-
- // Request service status
- sendServiceAction(TorServiceConstants.ACTION_STATUS)
-
- // Wait for response and unregister receiver
- var torIsStarted = false
- withTimeoutOrNull(torServiceResponseTimeout) {
- torIsStarted = channel.receive()
- }
- lbm.unregisterReceiver(localBroadcastReceiver)
- return torIsStarted
- }
-
- fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope? = null, withDebugLogging: Boolean = false) {
- if (BuildConfig.DISABLE_TOR) {
- return
- }
-
- context.getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
- .edit().putBoolean("pref_enable_logging", withDebugLogging).apply()
-
- if (lifecycleScope == null) {
- sendServiceAction(TorServiceConstants.ACTION_START)
- } else {
- lifecycleScope.launch {
- val torNeedsStart = !checkTorIsStarted()
- if (torNeedsStart) {
- sendServiceAction(TorServiceConstants.ACTION_START)
- }
- }
- }
- }
-
- fun stopTor() {
- if (BuildConfig.DISABLE_TOR) {
- return
- }
-
- val torService = Intent(context, TorService::class.java)
- context.stopService(torService)
- }
-
- fun setTorStopped() {
- lastKnownStatus = TorStatus.OFF
- onTorStopped()
- }
-
- fun restartTor() {
- // tor-android-service doesn't dynamically update the torrc file,
- // and it doesn't use SETCONF, so we completely restart the service.
- // However, don't restart if we aren't started and we weren't
- // previously started.
- if (!lastKnownStatus.isStarted() && !wasTorBootstrapped) {
- return
- }
+ fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope? = null, withDebugLogging: Boolean = false)
+ fun stopTor()
+ fun setTorStopped()
+ fun restartTor()
+}
- if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
- // If we aren't started, but we were previously bootstrapped,
- // then we handle a "restart" request as a "start" restart
- initiateTorBootstrap()
- } else {
- // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
- // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
- // service.
- isTorRestarting = true
- stopTor()
- }
- }
- private fun sendServiceAction(action: String) {
- val torServiceStatus = Intent(context, TorService::class.java)
- torServiceStatus.action = action
- context.startService(torServiceStatus)
- }
- companion object {
- const val torServiceResponseTimeout = 5000L
- }
-}
=====================================
fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt
=====================================
@@ -0,0 +1,289 @@
+package org.mozilla.fenix.tor
+
+
+import android.content.Context
+import android.util.Log
+import androidx.lifecycle.LifecycleCoroutineScope
+import mozilla.components.browser.engine.gecko.GeckoEngine
+import org.mozilla.fenix.ext.components
+import org.mozilla.geckoview.TorIntegrationAndroid
+import org.mozilla.geckoview.TorIntegrationAndroid.BootstrapStateChangeListener
+import org.mozilla.geckoview.TorSettings
+import org.mozilla.geckoview.TorSettings.BridgeBuiltinType
+import org.mozilla.geckoview.TorSettings.BridgeSource
+
+// Enum matching TorConnectState from TorConnect.sys.mjs that we get from onBootstrapStateChange
+internal enum class TorConnectState(val state: String) {
+ Initial("Initial"),
+ Configuring("Configuring"),
+ AutoBootstrapping("AutoBootstrapping"),
+ Bootstrapping("Bootstrapping"),
+ Error("Error"),
+ Bootstrapped("Bootstrapped"),
+ Disabled("Disabled");
+
+ fun isStarting() = this == Bootstrapping || this == AutoBootstrapping
+ fun isError() = this == Error
+
+ fun isStarted() = this == Bootstrapped
+
+ fun isOff() = this == Initial || this == Configuring || this == Disabled || this == Error
+
+
+ // Convert to TorStatus that firefox-android uses based on tor-android-service
+ fun toTorStatus(): TorStatus {
+ return when (this) {
+ Initial -> TorStatus.OFF
+ Configuring -> TorStatus.OFF
+ AutoBootstrapping -> TorStatus.STARTING
+ Bootstrapping -> TorStatus.STARTING
+ Error -> TorStatus.UNKNOWN
+ Bootstrapped -> TorStatus.ON
+ Disabled -> TorStatus.OFF
+ }
+ }
+}
+
+class TorControllerGV(
+ private val context: Context,
+) : TorController, TorEvents, BootstrapStateChangeListener {
+
+ private val TAG = "TorControllerGV"
+
+ private var torListeners = mutableListOf<TorEvents>()
+
+ private var lastKnownStatus = TorConnectState.Initial
+ private var wasTorBootstrapped = false
+ private var isTorRestarting = false
+
+ private var isTorBootstrapped = false
+ get() = ((lastKnownStatus.isStarted()) && wasTorBootstrapped)
+
+ private val entries = mutableListOf<Pair<String?, String?>>()
+ override val logEntries get() = entries
+ override val isStarting get() = lastKnownStatus.isStarting()
+ override val isRestarting get() = isTorRestarting
+ override val isBootstrapped get() = isTorBootstrapped
+ override val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
+
+ private fun getTorIntegration(): TorIntegrationAndroid {
+ return (context.components.core.engine as GeckoEngine).getTorIntegrationController()
+ }
+
+ private fun getTorSettings(): TorSettings? {
+ return getTorIntegration().getSettings()
+ }
+
+ override var bridgesEnabled: Boolean
+ get() {
+ return getTorSettings()?.bridgesEnabled ?: false
+ }
+ set(value) {
+ getTorSettings()?.let {
+ it.bridgesEnabled = value
+ getTorIntegration().setSettings(it, true, true)
+ }
+ }
+
+
+ override var bridgeTransport: TorBridgeTransportConfig
+ get() {
+ return when (getTorSettings()?.bridgesSource) {
+ BridgeSource.BuiltIn -> {
+ when (getTorSettings()?.bridgesBuiltinType) {
+ BridgeBuiltinType.Obfs4 -> TorBridgeTransportConfig.BUILTIN_OBFS4
+ BridgeBuiltinType.MeekAzure -> TorBridgeTransportConfig.BUILTIN_MEEK_AZURE
+ BridgeBuiltinType.Snowflake -> TorBridgeTransportConfig.BUILTIN_SNOWFLAKE
+ else -> TorBridgeTransportConfig.USER_PROVIDED
+ }
+
+ }
+
+ BridgeSource.UserProvided -> TorBridgeTransportConfig.USER_PROVIDED
+ else -> TorBridgeTransportConfig.USER_PROVIDED
+ }
+ }
+ set(value) {
+ getTorSettings()?.let {
+ if (value == TorBridgeTransportConfig.USER_PROVIDED) {
+ it.bridgesSource = BridgeSource.BuiltIn
+ } else {
+ val bbt: BridgeBuiltinType = when (value) {
+ TorBridgeTransportConfig.BUILTIN_OBFS4 -> BridgeBuiltinType.Obfs4
+ TorBridgeTransportConfig.BUILTIN_MEEK_AZURE -> BridgeBuiltinType.MeekAzure
+ TorBridgeTransportConfig.BUILTIN_SNOWFLAKE -> BridgeBuiltinType.Snowflake
+ else -> BridgeBuiltinType.Invalid
+ }
+ it.bridgesBuiltinType = bbt
+ }
+ getTorIntegration().setSettings(it, true, true)
+ }
+ }
+
+
+ override var userProvidedBridges: String?
+ get() {
+ return getTorSettings()?.bridgeBridgeStrings?.joinToString("\r\n")
+ }
+ set(value) {
+ getTorSettings()?.let {
+ it.bridgeBridgeStrings = value?.split("\r\n")?.toTypedArray() ?: arrayOf<String>()
+ getTorIntegration().setSettings(it, true, true)
+ }
+ }
+
+ override fun start() {
+ getTorIntegration().registerBootstrapStateChangeListener(this)
+ }
+
+ override fun stop() {
+ getTorIntegration().unregisterBootstrapStateChangeListener(this)
+ }
+
+ // TorEvents
+ override fun onTorConnecting() {
+ synchronized(torListeners) {
+ torListeners.forEach { it.onTorConnecting() }
+ }
+ }
+
+ // TorEvents
+ override fun onTorConnected() {
+ synchronized(torListeners) {
+ torListeners.forEach { it.onTorConnected() }
+ }
+ }
+
+ // TorEvents
+ override fun onTorStatusUpdate(entry: String?, status: String?) {
+ synchronized(torListeners) {
+ torListeners.forEach { it.onTorStatusUpdate(entry, status) }
+ }
+ }
+
+ // TorEvents
+ override fun onTorStopped() {
+ synchronized(torListeners) {
+ torListeners.forEach { it.onTorStopped() }
+ }
+ }
+
+ override fun registerTorListener(l: TorEvents) {
+ synchronized(torListeners) {
+ if (torListeners.contains(l)) {
+ return
+ }
+ torListeners.add(l)
+ }
+ }
+
+ override fun unregisterTorListener(l: TorEvents) {
+ synchronized(torListeners) {
+ if (!torListeners.contains(l)) {
+ return
+ }
+ torListeners.remove(l)
+ }
+ }
+
+ override fun initiateTorBootstrap(
+ lifecycleScope: LifecycleCoroutineScope?,
+ withDebugLogging: Boolean,
+ ) {
+ getTorIntegration().beginBootstrap()
+ }
+
+ override fun stopTor() {
+ getTorIntegration().cancelBootstrap()
+ }
+
+ override fun setTorStopped() {
+ lastKnownStatus = TorConnectState.Disabled
+ onTorStopped()
+ }
+
+ override fun restartTor() {
+ if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
+ // If we aren't started, but we were previously bootstrapped,
+ // then we handle a "restart" request as a "start" restart
+ initiateTorBootstrap()
+ } else {
+ // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
+ // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
+ // service.
+ isTorRestarting = true
+ stopTor()
+ }
+ }
+
+ // TorEventsBootstrapStateChangeListener -> (lastKnowStatus, TorEvents)
+ // Handle events from GeckoView TorAndroidIntegration and map to TorEvents based events
+ // and state for firefox-android (designed for tor-android-service)
+ // fun onTorConnecting()
+ // fun onTorConnected()
+ // fun onTorStatusUpdate(entry: String?, status: String?)
+ // fun onTorStopped()
+
+ // TorEventsBootstrapStateChangeListener
+ override fun onBootstrapStateChange(newStateVal: String?) {
+ Log.d(TAG, "onBootstrapStateChange($newStateVal)")
+ val newState: TorConnectState = TorConnectState.valueOf(newStateVal ?: "Error")
+
+ if (newState.isError() && wasTorBootstrapped) {
+ stopTor()
+ }
+
+ if (newState.isStarted()) {
+ wasTorBootstrapped = true
+ onTorConnected()
+ }
+
+ if (wasTorBootstrapped && newState == TorConnectState.Configuring) {
+ wasTorBootstrapped = false
+ if (isTorRestarting) {
+ initiateTorBootstrap()
+ } else {
+ onTorStopped()
+ }
+ }
+
+ if (lastKnownStatus.isOff() && newState.isStarting()) {
+ isTorRestarting = false
+ }
+
+ lastKnownStatus = newState
+
+ }
+
+ // TorEventsBootstrapStateChangeListener
+ override fun onBootstrapProgress(progress: Double, status: String?, hasWarnings: Boolean) {
+ Log.d(TAG, "onBootstrapProgress($progress, $status, $hasWarnings)")
+ if (progress == 100.0) {
+ lastKnownStatus = TorConnectState.Bootstrapped
+ wasTorBootstrapped = true
+ onTorConnected()
+ } else {
+ lastKnownStatus = TorConnectState.Bootstrapping
+ onTorConnecting()
+
+ }
+ entries.add(Pair(status, lastKnownStatus.toTorStatus().status))
+ onTorStatusUpdate(status, lastKnownStatus.toTorStatus().status)
+ }
+
+ // TorEventsBootstrapStateChangeListener
+ override fun onBootstrapComplete() {
+ lastKnownStatus = TorConnectState.Bootstrapped
+ this.onTorConnected()
+ }
+
+ // TorEventsBootstrapStateChangeListener
+ override fun onBootstrapError(message: String?, details: String?) {
+ lastKnownStatus = TorConnectState.Error
+ }
+
+ // TorEventsBootstrapStateChangeListener
+ override fun onSettingsRequested() {
+ // noop
+ }
+}
=====================================
fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerTAS.kt
=====================================
@@ -0,0 +1,332 @@
+package org.mozilla.fenix.tor
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeoutOrNull
+import org.mozilla.fenix.BuildConfig
+import org.torproject.android.service.TorService
+import org.torproject.android.service.TorServiceConstants
+import org.torproject.android.service.util.Prefs
+
+@SuppressWarnings("TooManyFunctions")
+class TorControllerTAS (private val context: Context): TorController {
+ private val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
+ private val entries = mutableListOf<Pair<String?, String?>>()
+ override val logEntries get() = entries
+
+ private var torListeners = mutableListOf<TorEvents>()
+
+ private var pendingRegisterChangeList = mutableListOf<Pair<TorEvents, Boolean>>()
+ private var lockTorListenersMutation = false
+
+ private var lastKnownStatus = TorStatus.OFF
+ private var wasTorBootstrapped = false
+ private var isTorRestarting = false
+
+ // This may be a lie
+ private var isTorBootstrapped = false
+ get() = ((lastKnownStatus == TorStatus.ON) && wasTorBootstrapped)
+
+ val isDebugLoggingEnabled get() =
+ context
+ .getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
+ .getBoolean("pref_enable_logging", false)
+
+ override val isStarting get() = lastKnownStatus.isStarting()
+ override val isRestarting get() = isTorRestarting
+ override val isBootstrapped get() = isTorBootstrapped
+ override val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
+
+ override var bridgesEnabled: Boolean
+ get() = Prefs.bridgesEnabled()
+ set(value) { Prefs.putBridgesEnabled(value) }
+
+ override var bridgeTransport: TorBridgeTransportConfig
+ get() {
+ return TorBridgeTransportConfigUtil.getStringToBridgeTransport(
+ Prefs.getBridgesList()
+ )
+ }
+ set(value) {
+ if (value == TorBridgeTransportConfig.USER_PROVIDED) {
+ // Don't set the pref when the value is USER_PROVIDED because
+ // "user_provided" is not a valid bridge or transport type.
+ // This call should be followed by setting userProvidedBridges.
+ return
+ }
+ Prefs.setBridgesList(value.transportName)
+ }
+
+ override var userProvidedBridges: String?
+ get() {
+ val bridges = Prefs.getBridgesList()
+ val bridgeType =
+ TorBridgeTransportConfigUtil.getStringToBridgeTransport(bridges)
+ return when (bridgeType) {
+ TorBridgeTransportConfig.USER_PROVIDED -> bridges
+ else -> null
+ }
+ }
+ set(value) {
+ Prefs.setBridgesList(value)
+ }
+
+ override fun start() {
+ // Register receiver
+ lbm.registerReceiver(
+ persistentBroadcastReceiver,
+ IntentFilter(TorServiceConstants.ACTION_STATUS)
+ )
+ lbm.registerReceiver(
+ persistentBroadcastReceiver,
+ IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG)
+ )
+ }
+
+ override fun stop() {
+ lbm.unregisterReceiver(persistentBroadcastReceiver)
+ }
+
+ private val persistentBroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == null ||
+ (intent.action != TorServiceConstants.ACTION_STATUS &&
+ intent.action != TorServiceConstants.LOCAL_ACTION_LOG)
+ ) {
+ return
+ }
+ val action = intent.action
+
+ val logentry: String?
+ val status: String?
+ if (action == TorServiceConstants.LOCAL_ACTION_LOG) {
+ logentry = intent.getExtras()
+ ?.getCharSequence(TorServiceConstants.LOCAL_EXTRA_LOG) as? String?
+ } else {
+ logentry = null
+ }
+
+ status = intent.getExtras()
+ ?.getCharSequence(TorServiceConstants.EXTRA_STATUS) as? String?
+
+ if (logentry == null && status == null) {
+ return
+ }
+
+ onTorStatusUpdate(logentry, status)
+
+ if (status == null) {
+ return
+ }
+
+ val newStatus = TorStatus.fromString(status)
+
+ if (newStatus.isUnknown() && wasTorBootstrapped) {
+ stopTor()
+ }
+
+ entries.add(Pair(logentry, status))
+
+ if (logentry != null && logentry.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) {
+ wasTorBootstrapped = true
+ onTorConnected()
+ }
+
+ if (lastKnownStatus.isStopping() && newStatus.isOff()) {
+ if (isTorRestarting) {
+ initiateTorBootstrap()
+ } else {
+ onTorStopped()
+ }
+ }
+
+ if (lastKnownStatus.isOff() && newStatus.isStarting()) {
+ isTorRestarting = false
+ }
+
+ lastKnownStatus = newStatus
+ }
+ }
+
+ override fun onTorConnecting() {
+ lockTorListenersMutation = true
+ torListeners.forEach { it.onTorConnecting() }
+ lockTorListenersMutation = false
+
+ handlePendingRegistrationChanges()
+ }
+
+ override fun onTorConnected() {
+ lockTorListenersMutation = true
+ torListeners.forEach { it.onTorConnected() }
+ lockTorListenersMutation = false
+
+ handlePendingRegistrationChanges()
+ }
+
+ override fun onTorStatusUpdate(entry: String?, status: String?) {
+ lockTorListenersMutation = true
+ torListeners.forEach { it.onTorStatusUpdate(entry, status) }
+ lockTorListenersMutation = false
+
+ handlePendingRegistrationChanges()
+ }
+
+ override fun onTorStopped() {
+ lockTorListenersMutation = true
+ torListeners.forEach { it.onTorStopped() }
+ lockTorListenersMutation = false
+
+ handlePendingRegistrationChanges()
+ }
+
+ override fun registerTorListener(l: TorEvents) {
+ if (torListeners.contains(l)) {
+ return
+ }
+
+ if (lockTorListenersMutation) {
+ pendingRegisterChangeList.add(Pair(l, true))
+ } else {
+ torListeners.add(l)
+ }
+ }
+
+ override fun unregisterTorListener(l: TorEvents) {
+ if (!torListeners.contains(l)) {
+ return
+ }
+
+ if (lockTorListenersMutation) {
+ pendingRegisterChangeList.add(Pair(l, false))
+ } else {
+ torListeners.remove(l)
+ }
+ }
+
+ private fun handlePendingRegistrationChanges() {
+ pendingRegisterChangeList.forEach {
+ if (it.second) {
+ registerTorListener(it.first)
+ } else {
+ unregisterTorListener(it.first)
+ }
+ }
+
+ pendingRegisterChangeList.clear()
+ }
+
+ /**
+ * Receive the current Tor status.
+ *
+ * Send a request for the current status and receive the response.
+ * Returns true if Tor is running, false otherwise.
+ *
+ */
+ private suspend fun checkTorIsStarted(): Boolean {
+ val channel = Channel<Boolean>()
+
+ // Register receiver
+ val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
+ val localBroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.action ?: return
+ // We only want ACTION_STATUS messages
+ if (action != TorServiceConstants.ACTION_STATUS) {
+ return
+ }
+ // The current status has the EXTRA_STATUS key
+ val currentStatus =
+ intent.getStringExtra(TorServiceConstants.EXTRA_STATUS)
+ channel.trySend(currentStatus === TorServiceConstants.STATUS_ON)
+ }
+ }
+ lbm.registerReceiver(
+ localBroadcastReceiver,
+ IntentFilter(TorServiceConstants.ACTION_STATUS)
+ )
+
+ // Request service status
+ sendServiceAction(TorServiceConstants.ACTION_STATUS)
+
+ // Wait for response and unregister receiver
+ var torIsStarted = false
+ withTimeoutOrNull(torServiceResponseTimeout) {
+ torIsStarted = channel.receive()
+ }
+ lbm.unregisterReceiver(localBroadcastReceiver)
+ return torIsStarted
+ }
+
+ override fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope?, withDebugLogging: Boolean) {
+ if (BuildConfig.DISABLE_TOR) {
+ return
+ }
+
+ context.getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
+ .edit().putBoolean("pref_enable_logging", withDebugLogging).apply()
+
+ if (lifecycleScope == null) {
+ sendServiceAction(TorServiceConstants.ACTION_START)
+ } else {
+ lifecycleScope.launch {
+ val torNeedsStart = !checkTorIsStarted()
+ if (torNeedsStart) {
+ sendServiceAction(TorServiceConstants.ACTION_START)
+ }
+ }
+ }
+ }
+
+ override fun stopTor() {
+ if (BuildConfig.DISABLE_TOR) {
+ return
+ }
+
+ val torService = Intent(context, TorService::class.java)
+ context.stopService(torService)
+ }
+
+ override fun setTorStopped() {
+ lastKnownStatus = TorStatus.OFF
+ onTorStopped()
+ }
+
+ override fun restartTor() {
+ // tor-android-service doesn't dynamically update the torrc file,
+ // and it doesn't use SETCONF, so we completely restart the service.
+ // However, don't restart if we aren't started and we weren't
+ // previously started.
+ if (!lastKnownStatus.isStarted() && !wasTorBootstrapped) {
+ return
+ }
+
+ if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
+ // If we aren't started, but we were previously bootstrapped,
+ // then we handle a "restart" request as a "start" restart
+ initiateTorBootstrap()
+ } else {
+ // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
+ // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
+ // service.
+ isTorRestarting = true
+ stopTor()
+ }
+ }
+
+ private fun sendServiceAction(action: String) {
+ val torServiceStatus = Intent(context, TorService::class.java)
+ torServiceStatus.action = action
+ context.startService(torServiceStatus)
+ }
+
+ companion object {
+ const val torServiceResponseTimeout = 5000L
+ }
+}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/commit/6d0…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/commit/6d0…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.7.0esr-13.5-1] 2 commits: fixup! Bug 40597: Implement TorSettings module
by Pier Angelo Vendrame (@pierov) 25 Jan '24
by Pier Angelo Vendrame (@pierov) 25 Jan '24
25 Jan '24
Pier Angelo Vendrame pushed to branch tor-browser-115.7.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
f49834e2 by Dan Ballard at 2024-01-25T17:05:18+00:00
fixup! Bug 40597: Implement TorSettings module
Bug 42252: Fix typo in documents
- - - - -
db59cb25 by Dan Ballard at 2024-01-25T17:05:18+00:00
fixup! Bug 42247: Android helpers for the TorProvider
Bug 42252: Add support for TorController in firefox-android
- - - - -
4 changed files:
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorSettings.java
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/TorLegacyAndroidSettings.java
- toolkit/modules/TorSettings.sys.mjs
Changes:
=====================================
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
=====================================
@@ -82,6 +82,10 @@ public class TorIntegrationAndroid implements BundleEventListener {
private final HashMap<Integer, MeekTransport> mMeeks = new HashMap<>();
private int mMeekCounter;
+ // mSettings is a java side copy of the authoritative settings in the JS code.
+ // it's useful to maintain as the ui may be fetching these options often and
+ // we don't watch each fetch to be a passthrough to JS with JSON serialization and
+ // deserialization each time
private TorSettings mSettings = null;
/* package */ TorIntegrationAndroid(Context context) {
@@ -557,11 +561,38 @@ public class TorIntegrationAndroid implements BundleEventListener {
void onSettingsRequested();
}
- public @NonNull GeckoResult<GeckoBundle> getSettings() {
- return EventDispatcher.getInstance().queryBundle(EVENT_SETTINGS_GET);
+ private @NonNull void reloadSettings() {
+ EventDispatcher.getInstance().queryBundle(EVENT_SETTINGS_GET).then( new GeckoResult.OnValueListener<GeckoBundle, Void>() {
+ public GeckoResult<Void> onValue(final GeckoBundle bundle) {
+ mSettings = new TorSettings(bundle);
+ return new GeckoResult<Void>();
+ }
+ });
+ }
+
+ public TorSettings getSettings() {
+ return mSettings;
+ }
+
+ public void setSettings(final TorSettings settings, boolean save, boolean apply) {
+ mSettings = settings;
+
+ emitSetSettings(settings, save, apply).then(
+ new GeckoResult.OnValueListener<Void, Void>() {
+ public GeckoResult<Void> onValue(Void v) {
+ return new GeckoResult<Void>();
+ }
+ },
+ new GeckoResult.OnExceptionListener<Void>() {
+ public GeckoResult<Void> onException(final Throwable e) {
+ Log.e(TAG, "Failed to set settings", e);
+ reloadSettings();
+ return new GeckoResult<Void>();
+ }
+ });
}
- public @NonNull GeckoResult<Void> setSettings(final TorSettings settings, boolean save, boolean apply) {
+ private @NonNull GeckoResult<Void> emitSetSettings(final TorSettings settings, boolean save, boolean apply) {
GeckoBundle bundle = new GeckoBundle(3);
bundle.putBoolean("save", save);
bundle.putBoolean("apply", apply);
=====================================
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorSettings.java
=====================================
@@ -2,24 +2,8 @@ package org.mozilla.geckoview;
import android.util.Log;
-import org.json.JSONArray;
-import org.json.JSONObject;
import org.mozilla.gecko.util.GeckoBundle;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.io.SequenceInputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.stream.Collectors;
-
public class TorSettings {
public enum BridgeSource {
@@ -76,6 +60,35 @@ public class TorSettings {
}
}
+ public enum BridgeBuiltinType {
+ /* TorSettings.sys.mjs ~ln43: string: obfs4|meek-azure|snowflake|etc */
+ Invalid("invalid"),
+ Obfs4("obfs4"),
+ MeekAzure("meek-azure"),
+ Snowflake("snowflake");
+
+
+ private String type;
+
+ BridgeBuiltinType(String type) {
+ this.type = type;
+ }
+
+ public String toString() {
+ return type;
+ }
+
+ public static BridgeBuiltinType fromString(String s) {
+ switch (s) {
+ case "obfs4": return Obfs4;
+ case "meek-azure": return MeekAzure;
+ case "snowflake": return Snowflake;
+ }
+ return Invalid;
+ }
+
+ }
+
private boolean loaded = false;
public boolean enabled = true;
@@ -85,7 +98,7 @@ public class TorSettings {
// bridges section
public boolean bridgesEnabled = false;
public BridgeSource bridgesSource = BridgeSource.Invalid;
- public String bridgesBuiltinType = "";
+ public BridgeBuiltinType bridgesBuiltinType = BridgeBuiltinType.Invalid;
public String[] bridgeBridgeStrings;
// proxy section
@@ -112,7 +125,7 @@ public class TorSettings {
bridgesEnabled = bridges.getBoolean("enabled");
bridgesSource = BridgeSource.fromInt(bridges.getInt("source"));
- bridgesBuiltinType = bridges.getString("builtin_type");
+ bridgesBuiltinType = BridgeBuiltinType.fromString(bridges.getString("builtin_type"));
bridgeBridgeStrings = bridges.getStringArray("bridge_strings");
quickstart = qs.getBoolean("enabled");
@@ -143,7 +156,7 @@ public class TorSettings {
bridges.putBoolean("enabled", bridgesEnabled);
bridges.putInt("source", bridgesSource.toInt());
- bridges.putString("builtin_type", bridgesBuiltinType);
+ bridges.putString("builtin_type", bridgesBuiltinType.toString());
bridges.putStringArray("bridge_strings", bridgeBridgeStrings);
qs.putBoolean("enabled", quickstart);
=====================================
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/TorLegacyAndroidSettings.java
=====================================
@@ -1,11 +1,5 @@
package org.mozilla.geckoview.androidlegacysettings;
-import java.io.IOException;
-
-import android.content.SharedPreferences;
-
-import org.mozilla.gecko.GeckoAppShell;
-
import org.mozilla.geckoview.TorSettings;
public class TorLegacyAndroidSettings {
@@ -54,10 +48,10 @@ public class TorLegacyAndroidSettings {
switch (userDefinedBridgeList) {
case "obfs4":
case "snowflake":
- settings.bridgesBuiltinType = userDefinedBridgeList;
+ settings.bridgesBuiltinType = TorSettings.BridgeBuiltinType.fromString(userDefinedBridgeList);
break;
case "meek":
- settings.bridgesBuiltinType = "meek-azure";
+ settings.bridgesBuiltinType = TorSettings.BridgeBuiltinType.MeekAzure;
break;
default:
settings.bridgesSource = TorSettings.BridgeSource.Invalid;
=====================================
toolkit/modules/TorSettings.sys.mjs
=====================================
@@ -40,7 +40,7 @@ const TorSettingsPrefs = Object.freeze({
enabled: "torbrowser.settings.bridges.enabled",
/* int: See TorBridgeSource */
source: "torbrowser.settings.bridges.source",
- /* string: obfs4|meek_azure|snowflake|etc */
+ /* string: obfs4|meek-azure|snowflake|etc */
builtin_type: "torbrowser.settings.bridges.builtin_type",
/* preference branch: each child branch should be a bridge string */
bridge_strings: "torbrowser.settings.bridges.bridge_strings",
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/308d6a…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/308d6a…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.7.0esr-13.5-1] fixup! Bug 42247: Android helpers for the TorProvider
by Pier Angelo Vendrame (@pierov) 25 Jan '24
by Pier Angelo Vendrame (@pierov) 25 Jan '24
25 Jan '24
Pier Angelo Vendrame pushed to branch tor-browser-115.7.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
308d6a64 by Pier Angelo Vendrame at 2024-01-25T17:44:22+01:00
fixup! Bug 42247: Android helpers for the TorProvider
Bug 42368: Do not use API26+ java.nio features
- - - - -
1 changed file:
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
Changes:
=====================================
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
=====================================
@@ -16,15 +16,10 @@ import androidx.annotation.Nullable;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -69,9 +64,9 @@ public class TorIntegrationAndroid implements BundleEventListener {
private static final String COOKIE_AUTH_FILE = "/auth-file";
private final String mLibraryDir;
- private final Path mCacheDir;
+ private final String mCacheDir;
private final String mIpcDirectory;
- private final String mDataDir;
+ private final File mDataDir;
private TorProcess mTorProcess = null;
/**
@@ -91,9 +86,9 @@ public class TorIntegrationAndroid implements BundleEventListener {
/* package */ TorIntegrationAndroid(Context context) {
mLibraryDir = context.getApplicationInfo().nativeLibraryDir;
- mCacheDir = context.getCacheDir().toPath();
+ mCacheDir = context.getCacheDir().getAbsolutePath();
mIpcDirectory = mCacheDir + "/tor-private";
- mDataDir = context.getDataDir().getAbsolutePath() + "/tor";
+ mDataDir = new File(context.getFilesDir(), "tor");
registerListener();
}
@@ -260,7 +255,7 @@ public class TorIntegrationAndroid implements BundleEventListener {
args.add("CookieAuthFile");
args.add(ipcDir + COOKIE_AUTH_FILE);
args.add("DataDirectory");
- args.add(mDataDir);
+ args.add(mDataDir.getAbsolutePath());
boolean copied = true;
try {
copyAndUseConfigFile("--defaults-torrc", "torrc-defaults", args);
@@ -322,15 +317,19 @@ public class TorIntegrationAndroid implements BundleEventListener {
private void cleanIpcDirectory() {
File directory = new File(TorIntegrationAndroid.this.mIpcDirectory);
- if (!Files.isDirectory(directory.toPath())) {
+ if (!directory.isDirectory()) {
if (!directory.mkdirs()) {
Log.e(TAG, "Failed to create the IPC directory.");
return;
}
try {
- Set<PosixFilePermission> chmod = PosixFilePermissions.fromString("rwx------");
- Files.setPosixFilePermissions(directory.toPath(), chmod);
- } catch (IOException e) {
+ directory.setReadable(false, false);
+ directory.setReadable(true, true);
+ directory.setWritable(false, false);
+ directory.setWritable(true, true);
+ directory.setExecutable(false, false);
+ directory.setExecutable(true, true);
+ } catch (SecurityException e) {
Log.e(TAG, "Could not set the permissions to the IPC directory.", e);
}
return;
@@ -347,15 +346,46 @@ public class TorIntegrationAndroid implements BundleEventListener {
}
private void copyAndUseConfigFile(String option, String name, ArrayList<String> args) throws IOException {
- final Path path = Paths.get(mCacheDir.toFile().getAbsolutePath(), name);
- if (!mCopiedConfigFiles || !path.toFile().exists()) {
- final Context context = GeckoAppShell.getApplicationContext();
- final InputStream in = context.getAssets().open("common/" + name);
- Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
+ File file = copyConfigFile(name);
+ args.add(option);
+ args.add(file.getAbsolutePath());
+ }
+
+ private File copyConfigFile(String name) throws IOException {
+ final File file = new File(mCacheDir, name);
+ if (mCopiedConfigFiles && file.exists()) {
+ return file;
+ }
+
+ final Context context = GeckoAppShell.getApplicationContext();
+ final InputStream in = context.getAssets().open("common/" + name);
+ // Files.copy is API 26+, so use java.io and a loop for now.
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(file);
+ } catch (IOException e) {
in.close();
+ throw e;
}
- args.add(option);
- args.add(path.toString());
+ try {
+ byte buffer[] = new byte[4096];
+ int read;
+ while ((read = in.read(buffer)) >= 0) {
+ out.write(buffer, 0, read);
+ }
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Cannot close the input stream for " + name);
+ }
+ try {
+ out.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Cannot close the output stream for " + name);
+ }
+ }
+ return file;
}
public void shutdown() {
@@ -406,9 +436,10 @@ public class TorIntegrationAndroid implements BundleEventListener {
setName("meek-" + id);
final ProcessBuilder builder = new ProcessBuilder(mLibraryDir + "/libObfs4proxy.so");
{
+ File ptStateDir = new File(mDataDir, "pt_state");
final Map<String, String> env = builder.environment();
env.put("TOR_PT_MANAGED_TRANSPORT_VER", "1");
- env.put("TOR_PT_STATE_LOCATION", mDataDir + "/pt_state");
+ env.put("TOR_PT_STATE_LOCATION", ptStateDir.getAbsolutePath());
env.put("TOR_PT_EXIT_ON_STDIN_CLOSE", "1");
env.put("TOR_PT_CLIENT_TRANSPORTS", TRANSPORT);
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/308d6a6…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/308d6a6…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/rbm][main] Bug 40068: Use Capture::Tiny instead of IO::CaptureOutput
by richard (@richard) 25 Jan '24
by richard (@richard) 25 Jan '24
25 Jan '24
richard pushed to branch main at The Tor Project / Applications / RBM
Commits:
9336d42f by Nicolas Vigier at 2024-01-25T11:42:58+01:00
Bug 40068: Use Capture::Tiny instead of IO::CaptureOutput
The IO::CaptureOutput perl module is deprecated, so we switch to
Capture::Tiny.
The Capture::Tiny module does not provide a capture_exec function
similar to the one from IO::CaptureOutput, so we implement one using the
same name, which avoids changing all the places where we were using it.
- - - - -
2 changed files:
- lib/RBM.pm
- lib/RBM/DefaultConfig.pm
Changes:
=====================================
lib/RBM.pm
=====================================
@@ -10,7 +10,7 @@ use YAML::XS qw(LoadFile);
use Template;
use File::Basename;
use IO::Handle;
-use IO::CaptureOutput qw(capture_exec);
+use Capture::Tiny qw(capture);
use File::Temp;
use File::Copy;
use File::Copy::Recursive qw(fcopy);
@@ -29,7 +29,7 @@ use feature "state";
BEGIN {
require Exporter;
our @ISA = qw(Exporter);
- our @EXPORT = qw(exit_error);
+ our @EXPORT = qw(exit_error capture_exec);
}
our $config;
@@ -308,6 +308,15 @@ sub exit_error {
exit (exists $_[1] ? $_[1] : 1);
}
+sub capture_exec {
+ my @cmd = @_;
+ my ($stdout, $stderr, $exit) = capture {
+ system(@cmd);
+ };
+ return ($stdout, $stderr, $exit == 0, $exit) if wantarray();
+ return $stdout;
+}
+
sub set_git_gpg_wrapper {
my ($project) = @_;
my $w = project_config($project, 'gpg_wrapper');
=====================================
lib/RBM/DefaultConfig.pm
=====================================
@@ -10,9 +10,8 @@ BEGIN {
}
use File::Basename;
-use RBM;
+use RBM qw(capture_exec);
use Cwd qw(getcwd);
-use IO::CaptureOutput qw(capture_exec);
use File::Temp;
use File::Path qw(make_path);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/rbm/-/commit/9336d42fdd669af…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/rbm/-/commit/9336d42fdd669af…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][maint-13.0] Update rbm for rbm#40067
by boklm (@boklm) 25 Jan '24
by boklm (@boklm) 25 Jan '24
25 Jan '24
boklm pushed to branch maint-13.0 at The Tor Project / Applications / tor-browser-build
Commits:
af1c78b5 by Nicolas Vigier at 2024-01-25T11:19:16+01:00
Update rbm for rbm#40067
- - - - -
1 changed file:
- rbm
Changes:
=====================================
rbm
=====================================
@@ -1 +1 @@
-Subproject commit 40acf540fe75055df2eb78454f070f57d0804729
+Subproject commit b5e5b04aaf677c4bacfb5ace45598313286bfdf6
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Update rbm for rbm#40067
by boklm (@boklm) 25 Jan '24
by boklm (@boklm) 25 Jan '24
25 Jan '24
boklm pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
83a1a381 by Nicolas Vigier at 2024-01-25T11:17:52+01:00
Update rbm for rbm#40067
- - - - -
1 changed file:
- rbm
Changes:
=====================================
rbm
=====================================
@@ -1 +1 @@
-Subproject commit 40acf540fe75055df2eb78454f070f57d0804729
+Subproject commit b5e5b04aaf677c4bacfb5ace45598313286bfdf6
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/8…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/8…
You're receiving this email because of your account on gitlab.torproject.org.
1
0