tbb-commits
Threads by month
- ----- 2025 -----
- 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
- 1 participants
- 19540 discussions
[tor-browser/geckoview-96.0-11.5-1] Bug 32658: Create a new MAR signing key
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit 6766fa12aafaaae13a6039e2dbfc5b66307ecf7a
Author: Georg Koppen <gk(a)torproject.org>
Date: Fri Jan 17 12:54:31 2020 +0000
Bug 32658: Create a new MAR signing key
It's time for our rotation again: Move the backup key in the front
position and add a new backup key.
Bug 33803: Move our primary nightly MAR signing key to tor-browser
Bug 33803: Add a secondary nightly MAR signing key
---
.../update/updater/nightly_aurora_level3_primary.der | Bin 1225 -> 1245 bytes
.../updater/nightly_aurora_level3_secondary.der | Bin 1225 -> 1245 bytes
toolkit/mozapps/update/updater/release_primary.der | Bin 1225 -> 1229 bytes
toolkit/mozapps/update/updater/release_secondary.der | Bin 1225 -> 1229 bytes
4 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der b/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
index 44fd95dcff89..d579cf801e1a 100644
Binary files a/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der and b/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der differ
diff --git a/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der b/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der
index 90f8e6e82c63..7cbfa77d06e7 100644
Binary files a/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der and b/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der differ
diff --git a/toolkit/mozapps/update/updater/release_primary.der b/toolkit/mozapps/update/updater/release_primary.der
index 1d94f88ad73b..0103a171de88 100644
Binary files a/toolkit/mozapps/update/updater/release_primary.der and b/toolkit/mozapps/update/updater/release_primary.der differ
diff --git a/toolkit/mozapps/update/updater/release_secondary.der b/toolkit/mozapps/update/updater/release_secondary.der
index 474706c4b73c..fcee3944e9b7 100644
Binary files a/toolkit/mozapps/update/updater/release_secondary.der and b/toolkit/mozapps/update/updater/release_secondary.der differ
1
0
[tor-browser/geckoview-96.0-11.5-1] Bug 24796 - Comment out excess permissions from GeckoView
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit 92ccc492cee61adc1ac3cc799e1ed4626a7a35ed
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Wed Apr 11 17:52:59 2018 +0000
Bug 24796 - Comment out excess permissions from GeckoView
The GeckoView AndroidManifest.xml is not preprocessed unlike Fennec's
manifest, so we can't use the ifdef preprocessor guards around the
permissions we do not want. Commenting the permissions is the
next-best-thing.
---
.../android/geckoview/src/main/AndroidManifest.xml | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/mobile/android/geckoview/src/main/AndroidManifest.xml b/mobile/android/geckoview/src/main/AndroidManifest.xml
index 0e350a0f526e..72b9f4ca374d 100644
--- a/mobile/android/geckoview/src/main/AndroidManifest.xml
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -6,20 +6,32 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mozilla.geckoview">
+<!--#ifdef MOZ_ANDROID_NETWORK_STATE-->
+ <!--
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ -->
+<!--#endif-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+<!--#ifdef MOZ_ANDROID_LOCATION-->
+ <!--
<uses-feature
android:name="android.hardware.location"
android:required="false"/>
<uses-feature
android:name="android.hardware.location.gps"
android:required="false"/>
+ -->
+<!--#endif-->
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
+<!--#ifdef MOZ_WEBRTC-->
+ <!-- TODO preprocess AndroidManifest.xml so that we can
+ conditionally include WebRTC permissions based on MOZ_WEBRTC. -->
+ <!--
<uses-feature
android:name="android.hardware.camera"
android:required="false"/>
@@ -28,14 +40,16 @@
android:required="false"/>
<uses-feature
- android:name="android.hardware.audio.low_latency"
+ android:name="android.hardware.camera.any"
android:required="false"/>
<uses-feature
- android:name="android.hardware.microphone"
+ android:name="android.hardware.audio.low_latency"
android:required="false"/>
<uses-feature
- android:name="android.hardware.camera.any"
+ android:name="android.hardware.microphone"
android:required="false"/>
+ -->
+<!--#endif-->
<!-- GeckoView requires OpenGL ES 2.0 -->
<uses-feature
1
0
[tor-browser/geckoview-96.0-11.5-1] Bug 32220: Improve the letterboxing experience
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit cd4c8a7e928c0b7e50522c8f6869ee4d24e68a63
Author: Richard Pospesel <richard(a)torproject.org>
Date: Mon Oct 28 17:42:17 2019 -0700
Bug 32220: Improve the letterboxing experience
CSS and JS changes to alter the UX surrounding letterboxing. The
browser element containing page content is now anchored to the bottom
of the toolbar, and the remaining letterbox margin is the same color
as the firefox chrome. The letterbox margin and border are tied to
the currently selected theme.
Also adds a 'needsLetterbox' property to tabbrowser.xml to fix a race
condition present when using the 'isEmpty' property. Using 'isEmpty'
as a proxy for 'needsLetterbox' resulted in over-zealous/unnecessary
letterboxing of about:blank tabs.
---
browser/base/content/browser.css | 7 ++
browser/base/content/tabbrowser-tab.js | 9 +++
browser/themes/shared/tabs.inc.css | 6 ++
.../components/resistfingerprinting/RFPHelper.jsm | 94 +++++++++++++++++++---
4 files changed, 104 insertions(+), 12 deletions(-)
diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
index 0784af042d69..c89d6c04545f 100644
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -94,6 +94,13 @@ body {
}
}
+.browserStack > browser.letterboxing {
+ border-color: var(--chrome-content-separator-color);
+ border-style: solid;
+ border-width : 1px;
+ border-top: none;
+}
+
%ifdef MENUBAR_CAN_AUTOHIDE
#toolbar-menubar[autohide="true"] {
overflow: hidden;
diff --git a/browser/base/content/tabbrowser-tab.js b/browser/base/content/tabbrowser-tab.js
index 295b9d13c193..ad6b3f4df4f6 100644
--- a/browser/base/content/tabbrowser-tab.js
+++ b/browser/base/content/tabbrowser-tab.js
@@ -229,6 +229,15 @@
return true;
}
+ get needsLetterbox() {
+ let browser = this.linkedBrowser;
+ if (isBlankPageURL(browser.currentURI.spec)) {
+ return false;
+ }
+
+ return true;
+ }
+
get lastAccessed() {
return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
}
diff --git a/browser/themes/shared/tabs.inc.css b/browser/themes/shared/tabs.inc.css
index 9fec6af1a718..591c207e9f9b 100644
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -53,6 +53,12 @@
background-color: var(--tabpanel-background-color);
}
+/* extend down the toolbar's colors when letterboxing is enabled*/
+#tabbrowser-tabpanels.letterboxing {
+ background-color: var(--toolbar-bgcolor);
+ background-image: var(--toolbar-bgimage);
+}
+
#tabbrowser-tabs,
#tabbrowser-arrowscrollbox,
#tabbrowser-tabs[positionpinnedtabs] > #tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned] {
diff --git a/toolkit/components/resistfingerprinting/RFPHelper.jsm b/toolkit/components/resistfingerprinting/RFPHelper.jsm
index 166ad21e9013..9520d8720631 100644
--- a/toolkit/components/resistfingerprinting/RFPHelper.jsm
+++ b/toolkit/components/resistfingerprinting/RFPHelper.jsm
@@ -40,6 +40,7 @@ class _RFPHelper {
// ============================================================================
constructor() {
this._initialized = false;
+ this._borderDimensions = null;
}
init() {
@@ -361,6 +362,24 @@ class _RFPHelper {
});
}
+ getBorderDimensions(aBrowser) {
+ if (this._borderDimensions) {
+ return this._borderDimensions;
+ }
+
+ const win = aBrowser.ownerGlobal;
+ const browserStyle = win.getComputedStyle(aBrowser);
+
+ this._borderDimensions = {
+ top : parseInt(browserStyle.borderTopWidth),
+ right: parseInt(browserStyle.borderRightWidth),
+ bottom : parseInt(browserStyle.borderBottomWidth),
+ left : parseInt(browserStyle.borderLeftWidth),
+ };
+
+ return this._borderDimensions;
+ }
+
_addOrClearContentMargin(aBrowser) {
let tab = aBrowser.getTabBrowser().getTabForBrowser(aBrowser);
@@ -369,9 +388,13 @@ class _RFPHelper {
return;
}
+ // we add the letterboxing class even if the content does not need letterboxing
+ // in which case margins are set such that the borders are hidden
+ aBrowser.classList.add("letterboxing");
+
// We should apply no margin around an empty tab or a tab with system
// principal.
- if (tab.isEmpty || aBrowser.contentPrincipal.isSystemPrincipal) {
+ if (!tab.needsLetterbox || aBrowser.contentPrincipal.isSystemPrincipal) {
this._clearContentViewMargin(aBrowser);
} else {
this._roundContentView(aBrowser);
@@ -539,10 +562,29 @@ class _RFPHelper {
// Calculating the margins around the browser element in order to round the
// content viewport. We will use a 200x100 stepping if the dimension set
// is not given.
- let margins = calcMargins(containerWidth, containerHeight);
+
+ const borderDimensions = this.getBorderDimensions(aBrowser);
+ const marginDims = calcMargins(containerWidth, containerHeight - borderDimensions.top);
+
+ let margins = {
+ top : 0,
+ right : 0,
+ bottom : 0,
+ left : 0,
+ };
+
+ // snap browser element to top
+ margins.top = 0;
+ // and leave 'double' margin at the bottom
+ margins.bottom = 2 * marginDims.height - borderDimensions.bottom;
+ // identical margins left and right
+ margins.right = marginDims.width - borderDimensions.right;
+ margins.left = marginDims.width - borderDimensions.left;
+
+ const marginStyleString = `${margins.top}px ${margins.right}px ${margins.bottom}px ${margins.left}px`;
// If the size of the content is already quantized, we do nothing.
- if (aBrowser.style.margin == `${margins.height}px ${margins.width}px`) {
+ if (aBrowser.style.margin === marginStyleString) {
log("_roundContentView[" + logId + "] is_rounded == true");
if (this._isLetterboxingTesting) {
log(
@@ -563,19 +605,35 @@ class _RFPHelper {
"_roundContentView[" +
logId +
"] setting margins to " +
- margins.width +
- " x " +
- margins.height
+ marginStyleString
);
- // One cannot (easily) control the color of a margin unfortunately.
- // An initial attempt to use a border instead of a margin resulted
- // in offset event dispatching; so for now we use a colorless margin.
- aBrowser.style.margin = `${margins.height}px ${margins.width}px`;
+
+ // The margin background color is determined by the background color of the
+ // window's tabpanels#tabbrowser-tabpanels element
+ aBrowser.style.margin = marginStyleString;
});
}
_clearContentViewMargin(aBrowser) {
+ const borderDimensions = this.getBorderDimensions(aBrowser);
+ // set the margins such that the browser elements border is visible up top, but
+ // are rendered off-screen on the remaining sides
+ let margins = {
+ top : 0,
+ right : -borderDimensions.right,
+ bottom : -borderDimensions.bottom,
+ left : -borderDimensions.left,
+ };
+ const marginStyleString = `${margins.top}px ${margins.right}px ${margins.bottom}px ${margins.left}px`;
+
+ aBrowser.ownerGlobal.requestAnimationFrame(() => {
+ aBrowser.style.margin = marginStyleString;
+ });
+ }
+
+ _removeLetterboxing(aBrowser) {
aBrowser.ownerGlobal.requestAnimationFrame(() => {
+ aBrowser.classList.remove("letterboxing");
aBrowser.style.margin = "";
});
}
@@ -593,6 +651,11 @@ class _RFPHelper {
aWindow.gBrowser.addTabsProgressListener(this);
aWindow.addEventListener("TabOpen", this);
+ const tabPanel = aWindow.document.getElementById("tabbrowser-tabpanels");
+ if (tabPanel) {
+ tabPanel.classList.add("letterboxing");
+ }
+
// Rounding the content viewport.
this._updateMarginsForTabsInWindow(aWindow);
}
@@ -616,10 +679,17 @@ class _RFPHelper {
tabBrowser.removeTabsProgressListener(this);
aWindow.removeEventListener("TabOpen", this);
- // Clear all margins and tooltip for all browsers.
+ // revert tabpanel's background colors to default
+ const tabPanel = aWindow.document.getElementById("tabbrowser-tabpanels");
+ if (tabPanel) {
+ tabPanel.classList.remove("letterboxing");
+ }
+
+ // and revert each browser element to default,
+ // restore default margins and remove letterboxing class
for (let tab of tabBrowser.tabs) {
let browser = tab.linkedBrowser;
- this._clearContentViewMargin(browser);
+ this._removeLetterboxing(browser);
}
}
1
0
[tor-browser/geckoview-96.0-11.5-1] Bug 25741 - TBA: Disable GeckoNetworkManager
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit 89d934922f8d5060e93b4a6f65f6a7e84f4f1a05
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Thu Apr 26 22:22:51 2018 +0000
Bug 25741 - TBA: Disable GeckoNetworkManager
The browser should not need information related to the network
interface or network state, tor should take care of that.
---
.../src/main/java/org/mozilla/geckoview/GeckoRuntime.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
index 59b1ff2291a9..10e4569bd386 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
@@ -124,7 +124,9 @@ public final class GeckoRuntime implements Parcelable {
mPaused = false;
// Monitor network status and send change notifications to Gecko
// while active.
- GeckoNetworkManager.getInstance().start(GeckoAppShell.getApplicationContext());
+ if (BuildConfig.TOR_BROWSER_VERSION == "") {
+ GeckoNetworkManager.getInstance().start(GeckoAppShell.getApplicationContext());
+ }
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
@@ -132,7 +134,9 @@ public final class GeckoRuntime implements Parcelable {
Log.d(LOGTAG, "Lifecycle: onPause");
mPaused = true;
// Stop monitoring network status while inactive.
- GeckoNetworkManager.getInstance().stop();
+ if (BuildConfig.TOR_BROWSER_VERSION == "") {
+ GeckoNetworkManager.getInstance().stop();
+ }
GeckoThread.onPause();
}
}
1
0
[tor-browser/geckoview-96.0-11.5-1] Orfox: Centralized proxy applied to AbstractCommunicator and BaseResources.
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit 730ce09eb9ed9e68fb56526db118338de633ff5e
Author: Amogh Pradeep <amoghbl1(a)gmail.com>
Date: Fri Jun 12 02:07:45 2015 -0400
Orfox: Centralized proxy applied to AbstractCommunicator and BaseResources.
See Bug 1357997 for partial uplift.
Also:
Bug 28051 - Use our Orbot for proxying our connections
Bug 31144 - ESR68 Network Code Review
---
.../java/org/mozilla/gecko/util/ProxySelector.java | 25 +++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
index 2fb4015f4126..5925da91d6da 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
@@ -28,6 +28,10 @@ import java.net.URLConnection;
import java.util.List;
public class ProxySelector {
+ private static final String TOR_PROXY_ADDRESS = "127.0.0.1";
+ private static final int TOR_SOCKS_PROXY_PORT = 9150;
+ private static final int TOR_HTTP_PROXY_PORT = 8218;
+
public static URLConnection openConnectionWithProxy(final URI uri) throws IOException {
final java.net.ProxySelector ps = java.net.ProxySelector.getDefault();
Proxy proxy = Proxy.NO_PROXY;
@@ -38,7 +42,26 @@ public class ProxySelector {
}
}
- return uri.toURL().openConnection(proxy);
+ /* Ignore the proxy we found from the VM, only use Tor. We can probably
+ * safely use the logic in this class in the future. */
+ return uri.toURL().openConnection(getProxy());
+ }
+
+ public static Proxy getProxy() {
+ // TODO make configurable
+ return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(TOR_PROXY_ADDRESS, TOR_SOCKS_PROXY_PORT));
+ }
+
+ public static String getProxyHostAddress() {
+ return TOR_PROXY_ADDRESS;
+ }
+
+ public static int getSocksProxyPort() {
+ return TOR_SOCKS_PROXY_PORT;
+ }
+
+ public static int getHttpProxyPort() {
+ return TOR_HTTP_PROXY_PORT;
}
public ProxySelector() {}
1
0
[tor-browser/geckoview-96.0-11.5-1] Bug 31607: App menu items stop working on macOS
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit 7c420a2a23556a01932f08a5681ec1a575d499cc
Author: Kathy Brade <brade(a)pearlcrescent.com>
Date: Thu Oct 3 10:53:43 2019 -0400
Bug 31607: App menu items stop working on macOS
Avoid re-creating the hidden window, since this causes the nsMenuBarX
object that is associated with the app menu to be freed (which in
turn causes all of the app menu items to stop working).
More detail: There should only be one hidden window.
XREMain::XRE_mainRun() contains an explicit call to create the
hidden window and that is the normal path by which it is created.
However, when Tor Launcher's wizard/progress window is opened during
startup, a hidden window is created earlier as a side effect of
calls to nsAppShellService::GetHiddenWindow(). Then, when
XREMain::XRE_mainRun() creates its hidden window, the original one
is freed which also causes the app menu's nsMenuBarX object which
is associated with that window to be destroyed. When that happens,
the menuGroupOwner property within each Cocoa menu items's MenuItemInfo
object is cleared. This breaks the link that is necessary for
NativeMenuItemTarget's menuItemHit method to dispatch a menu item
event.
---
xpfe/appshell/nsAppShellService.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/xpfe/appshell/nsAppShellService.cpp b/xpfe/appshell/nsAppShellService.cpp
index 71a9266caaf7..d74a5614e8eb 100644
--- a/xpfe/appshell/nsAppShellService.cpp
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -93,6 +93,10 @@ void nsAppShellService::EnsureHiddenWindow() {
NS_IMETHODIMP
nsAppShellService::CreateHiddenWindow() {
+ if (mHiddenWindow) {
+ return NS_OK;
+ }
+
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_IMPLEMENTED;
}
1
0
[tor-browser/geckoview-96.0-11.5-1] Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit 62810bc5e73871e6536ef49173372c50d11783f4
Author: Richard Pospesel <richard(a)torproject.org>
Date: Mon Sep 16 15:25:39 2019 -0700
Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#tor
This patch adds a new about:preferences#tor page which allows modifying
bridge, proxy, and firewall settings from within Tor Browser. All of the
functionality present in tor-launcher's Network Configuration panel is
present:
- Setting built-in bridges
- Requesting bridges from BridgeDB via moat
- Using user-provided bridges
- Configuring SOCKS4, SOCKS5, and HTTP/HTTPS proxies
- Setting firewall ports
- Viewing and Copying Tor's logs
- The Networking Settings in General preferences has been removed
---
browser/components/moz.build | 1 +
browser/components/preferences/main.inc.xhtml | 54 --
browser/components/preferences/main.js | 14 -
browser/components/preferences/preferences.js | 9 +
browser/components/preferences/preferences.xhtml | 5 +
browser/components/preferences/privacy.js | 1 +
.../torpreferences/content/parseFunctions.jsm | 89 +++
.../torpreferences/content/requestBridgeDialog.jsm | 204 +++++
.../content/requestBridgeDialog.xhtml | 35 +
.../torpreferences/content/torBridgeSettings.jsm | 325 ++++++++
.../torpreferences/content/torCategory.inc.xhtml | 9 +
.../torpreferences/content/torFirewallSettings.jsm | 72 ++
.../torpreferences/content/torLogDialog.jsm | 66 ++
.../torpreferences/content/torLogDialog.xhtml | 23 +
.../components/torpreferences/content/torPane.js | 857 +++++++++++++++++++++
.../torpreferences/content/torPane.xhtml | 123 +++
.../torpreferences/content/torPreferences.css | 77 ++
.../torpreferences/content/torPreferencesIcon.svg | 5 +
.../torpreferences/content/torProxySettings.jsm | 245 ++++++
browser/components/torpreferences/jar.mn | 14 +
browser/components/torpreferences/moz.build | 1 +
browser/modules/BridgeDB.jsm | 110 +++
browser/modules/TorProtocolService.jsm | 212 +++++
browser/modules/moz.build | 2 +
24 files changed, 2485 insertions(+), 68 deletions(-)
diff --git a/browser/components/moz.build b/browser/components/moz.build
index 70e5b153cfb3..53175e0186bd 100644
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -58,6 +58,7 @@ DIRS += [
"translation",
"uitour",
"urlbar",
+ "torpreferences",
]
DIRS += ["build"]
diff --git a/browser/components/preferences/main.inc.xhtml b/browser/components/preferences/main.inc.xhtml
index c537a22888d4..7957655b4784 100644
--- a/browser/components/preferences/main.inc.xhtml
+++ b/browser/components/preferences/main.inc.xhtml
@@ -689,58 +689,4 @@
<label id="cfrFeaturesLearnMore" class="learnMore" data-l10n-id="browsing-cfr-recommendations-learn-more" is="text-link"/>
</hbox>
</groupbox>
-
-<hbox id="networkProxyCategory"
- class="subcategory"
- hidden="true"
- data-category="paneGeneral">
- <html:h1 data-l10n-id="network-settings-title"/>
-</hbox>
-
-<!-- Network Settings-->
-<groupbox id="connectionGroup" data-category="paneGeneral" hidden="true">
- <label class="search-header" hidden="true"><html:h2 data-l10n-id="network-settings-title"/></label>
-
- <hbox align="center">
- <hbox align="center" flex="1">
- <description id="connectionSettingsDescription" control="connectionSettings"/>
- <spacer width="5"/>
- <label id="connectionSettingsLearnMore" class="learnMore" is="text-link"
- data-l10n-id="network-proxy-connection-learn-more">
- </label>
- <separator orient="vertical"/>
- </hbox>
-
- <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
- <hbox>
- <button id="connectionSettings"
- is="highlightable-button"
- class="accessory-button"
- data-l10n-id="network-proxy-connection-settings"
- searchkeywords="doh trr"
- search-l10n-ids="
- connection-window.title,
- connection-proxy-option-no.label,
- connection-proxy-option-auto.label,
- connection-proxy-option-system.label,
- connection-proxy-option-manual.label,
- connection-proxy-http,
- connection-proxy-https,
- connection-proxy-http-port,
- connection-proxy-socks,
- connection-proxy-socks4,
- connection-proxy-socks5,
- connection-proxy-noproxy,
- connection-proxy-noproxy-desc,
- connection-proxy-https-sharing.label,
- connection-proxy-autotype.label,
- connection-proxy-reload.label,
- connection-proxy-autologin.label,
- connection-proxy-socks-remote-dns.label,
- connection-dns-over-https.label,
- connection-dns-over-https-url-custom.label,
- " />
- </hbox>
- </hbox>
-</groupbox>
</html:template>
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
index ceee50df3743..2643b3715ba6 100644
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -317,15 +317,6 @@ var gMainPane = {
});
this.updatePerformanceSettingsBox({ duringChangeEvent: false });
this.displayUseSystemLocale();
- let connectionSettingsLink = document.getElementById(
- "connectionSettingsLearnMore"
- );
- let connectionSettingsUrl =
- Services.urlFormatter.formatURLPref("app.support.baseURL") +
- "prefs-connection-settings";
- connectionSettingsLink.setAttribute("href", connectionSettingsUrl);
- this.updateProxySettingsUI();
- initializeProxyUI(gMainPane);
if (Services.prefs.getBoolPref("intl.multilingual.enabled")) {
gMainPane.initBrowserLocale();
@@ -478,11 +469,6 @@ var gMainPane = {
"change",
gMainPane.updateHardwareAcceleration.bind(gMainPane)
);
- setEventListener(
- "connectionSettings",
- "command",
- gMainPane.showConnections
- );
setEventListener(
"browserContainersCheckbox",
"command",
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js
index cdbd6e135dae..4b0a9eb24063 100644
--- a/browser/components/preferences/preferences.js
+++ b/browser/components/preferences/preferences.js
@@ -14,6 +14,7 @@
/* import-globals-from findInPage.js */
/* import-globals-from ../../base/content/utilityOverlay.js */
/* import-globals-from ../../../toolkit/content/preferencesBindings.js */
+/* import-globals-from ../torpreferences/content/torPane.js */
"use strict";
@@ -218,6 +219,14 @@ function init_all() {
register_module("paneSync", gSyncPane);
}
register_module("paneSearchResults", gSearchResultsPane);
+ if (gTorPane.enabled) {
+ document.getElementById("category-tor").hidden = false;
+ register_module("paneTor", gTorPane);
+ } else {
+ // Remove the pane from the DOM so it doesn't get incorrectly included in search results.
+ document.getElementById("template-paneTor").remove();
+ }
+
gSearchResultsPane.init();
gMainPane.preInit();
diff --git a/browser/components/preferences/preferences.xhtml b/browser/components/preferences/preferences.xhtml
index 38e700c9d881..d57e09838fbf 100644
--- a/browser/components/preferences/preferences.xhtml
+++ b/browser/components/preferences/preferences.xhtml
@@ -14,6 +14,7 @@
<?xml-stylesheet href="chrome://browser/skin/preferences/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/privacy.css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?>
+<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?>
<!DOCTYPE html [
<!ENTITY % aboutTorDTD SYSTEM "chrome://torbutton/locale/aboutTor.dtd">
@@ -169,6 +170,9 @@
<image class="category-icon"/>
<label class="category-name" flex="1" data-l10n-id="pane-more-from-mozilla-title"></label>
</richlistitem>
+
+#include ../torpreferences/content/torCategory.inc.xhtml
+
</richlistbox>
<spacer flex="1"/>
@@ -222,6 +226,7 @@
#include sync.inc.xhtml
#include experimental.inc.xhtml
#include moreFromMozilla.inc.xhtml
+#include ../torpreferences/content/torPane.xhtml
</vbox>
</vbox>
</vbox>
diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js
index 75cc423538b0..6deab109041f 100644
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -48,6 +48,7 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
}
});
+// TODO: module import via ChromeUtils.defineModuleGetter
XPCOMUtils.defineLazyScriptGetter(
this,
["SecurityLevelPreferences"],
diff --git a/browser/components/torpreferences/content/parseFunctions.jsm b/browser/components/torpreferences/content/parseFunctions.jsm
new file mode 100644
index 000000000000..954759de63a5
--- /dev/null
+++ b/browser/components/torpreferences/content/parseFunctions.jsm
@@ -0,0 +1,89 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "parsePort",
+ "parseAddrPort",
+ "parseUsernamePassword",
+ "parseAddrPortList",
+ "parseBridgeStrings",
+ "parsePortList",
+];
+
+// expects a string representation of an integer from 1 to 65535
+let parsePort = function(aPort) {
+ // ensure port string is a valid positive integer
+ const validIntRegex = /^[0-9]+$/;
+ if (!validIntRegex.test(aPort)) {
+ throw new Error(`Invalid PORT string : '${aPort}'`);
+ }
+
+ // ensure port value is on valid range
+ let port = Number.parseInt(aPort);
+ if (port < 1 || port > 65535) {
+ throw new Error(
+ `Invalid PORT value, needs to be on range [1,65535] : '${port}'`
+ );
+ }
+
+ return port;
+};
+// expects a string in the format: "ADDRESS:PORT"
+let parseAddrPort = function(aAddrColonPort) {
+ let tokens = aAddrColonPort.split(":");
+ if (tokens.length != 2) {
+ throw new Error(`Invalid ADDRESS:PORT string : '${aAddrColonPort}'`);
+ }
+ let address = tokens[0];
+ let port = parsePort(tokens[1]);
+ return [address, port];
+};
+
+// expects a string in the format: "USERNAME:PASSWORD"
+// split on the first colon and any subsequent go into password
+let parseUsernamePassword = function(aUsernameColonPassword) {
+ let colonIndex = aUsernameColonPassword.indexOf(":");
+ if (colonIndex < 0) {
+ // we don't log the contents of the potentially password containing string
+ throw new Error("Invalid USERNAME:PASSWORD string");
+ }
+
+ let username = aUsernameColonPassword.substring(0, colonIndex);
+ let password = aUsernameColonPassword.substring(colonIndex + 1);
+
+ return [username, password];
+};
+
+// expects a string in the format: ADDRESS:PORT,ADDRESS:PORT,...
+// returns array of ports (as ints)
+let parseAddrPortList = function(aAddrPortList) {
+ let addrPorts = aAddrPortList.split(",");
+ // parse ADDRESS:PORT string and only keep the port (second element in returned array)
+ let retval = addrPorts.map(addrPort => parseAddrPort(addrPort)[1]);
+ return retval;
+};
+
+// expects a '/n' or '/r/n' delimited bridge string, which we split and trim
+// each bridge string can also optionally have 'bridge' at the beginning ie:
+// bridge $(type) $(address):$(port) $(certificate)
+// we strip out the 'bridge' prefix here
+let parseBridgeStrings = function(aBridgeStrings) {
+
+ // replace carriage returns ('\r') with new lines ('\n')
+ aBridgeStrings = aBridgeStrings.replace(/\r/g, "\n");
+ // then replace contiguous new lines ('\n') with a single one
+ aBridgeStrings = aBridgeStrings.replace(/[\n]+/g, "\n");
+
+ // split on the newline and for each bridge string: trim, remove starting 'bridge' string
+ // finally discard entries that are empty strings; empty strings could occur if we receive
+ // a new line containing only whitespace
+ let splitStrings = aBridgeStrings.split("\n");
+ return splitStrings.map(val => val.trim().replace(/^bridge\s+/i, ""))
+ .filter(bridgeString => bridgeString != "");
+};
+
+// expecting a ',' delimited list of ints with possible white space between
+// returns an array of ints
+let parsePortList = function(aPortListString) {
+ let splitStrings = aPortListString.split(",");
+ return splitStrings.map(val => parsePort(val.trim()));
+};
diff --git a/browser/components/torpreferences/content/requestBridgeDialog.jsm b/browser/components/torpreferences/content/requestBridgeDialog.jsm
new file mode 100644
index 000000000000..807d46cdfb18
--- /dev/null
+++ b/browser/components/torpreferences/content/requestBridgeDialog.jsm
@@ -0,0 +1,204 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["RequestBridgeDialog"];
+
+const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm");
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+class RequestBridgeDialog {
+ constructor() {
+ this._dialog = null;
+ this._submitButton = null;
+ this._dialogDescription = null;
+ this._captchaImage = null;
+ this._captchaEntryTextbox = null;
+ this._captchaRefreshButton = null;
+ this._incorrectCaptchaHbox = null;
+ this._incorrectCaptchaLabel = null;
+ this._bridges = [];
+ this._proxyURI = null;
+ }
+
+ static get selectors() {
+ return {
+ submitButton:
+ "accept" /* not really a selector but a key for dialog's getButton */,
+ dialogDescription: "description#torPreferences-requestBridge-description",
+ captchaImage: "image#torPreferences-requestBridge-captchaImage",
+ captchaEntryTextbox: "input#torPreferences-requestBridge-captchaTextbox",
+ refreshCaptchaButton:
+ "button#torPreferences-requestBridge-refreshCaptchaButton",
+ incorrectCaptchaHbox:
+ "hbox#torPreferences-requestBridge-incorrectCaptchaHbox",
+ incorrectCaptchaLabel:
+ "label#torPreferences-requestBridge-incorrectCaptchaError",
+ };
+ }
+
+ _populateXUL(dialog) {
+ const selectors = RequestBridgeDialog.selectors;
+
+ this._dialog = dialog;
+ const dialogWin = dialog.parentElement;
+ dialogWin.setAttribute(
+ "title",
+ TorStrings.settings.requestBridgeDialogTitle
+ );
+ // user may have opened a Request Bridge dialog in another tab, so update the
+ // CAPTCHA image or close out the dialog if we have a bridge list
+ this._dialog.addEventListener("focusin", () => {
+ const uri = BridgeDB.currentCaptchaImage;
+ const bridges = BridgeDB.currentBridges;
+
+ // new captcha image
+ if (uri) {
+ this._setcaptchaImage(uri);
+ } else if (bridges) {
+ this._bridges = bridges;
+ this._submitButton.disabled = false;
+ this._dialog.cancelDialog();
+ }
+ });
+
+ this._submitButton = this._dialog.getButton(selectors.submitButton);
+ this._submitButton.setAttribute("label", TorStrings.settings.submitCaptcha);
+ this._submitButton.disabled = true;
+ this._dialog.addEventListener("dialogaccept", e => {
+ e.preventDefault();
+ this.onSubmitCaptcha();
+ });
+
+ this._dialogDescription = this._dialog.querySelector(
+ selectors.dialogDescription
+ );
+ this._dialogDescription.textContent =
+ TorStrings.settings.contactingBridgeDB;
+
+ this._captchaImage = this._dialog.querySelector(selectors.captchaImage);
+
+ // request captcha from bridge db
+ BridgeDB.requestNewCaptchaImage(this._proxyURI).then(uri => {
+ this._setcaptchaImage(uri);
+ });
+
+ this._captchaEntryTextbox = this._dialog.querySelector(
+ selectors.captchaEntryTextbox
+ );
+ this._captchaEntryTextbox.setAttribute(
+ "placeholder",
+ TorStrings.settings.captchaTextboxPlaceholder
+ );
+ this._captchaEntryTextbox.disabled = true;
+ // disable submit if entry textbox is empty
+ this._captchaEntryTextbox.oninput = () => {
+ this._submitButton.disabled = this._captchaEntryTextbox.value == "";
+ };
+
+ this._captchaRefreshButton = this._dialog.querySelector(
+ selectors.refreshCaptchaButton
+ );
+ this._captchaRefreshButton.disabled = true;
+
+ this._incorrectCaptchaHbox = this._dialog.querySelector(
+ selectors.incorrectCaptchaHbox
+ );
+ this._incorrectCaptchaLabel = this._dialog.querySelector(
+ selectors.incorrectCaptchaLabel
+ );
+ this._incorrectCaptchaLabel.setAttribute(
+ "value",
+ TorStrings.settings.incorrectCaptcha
+ );
+
+ return true;
+ }
+
+ _setcaptchaImage(uri) {
+ if (uri != this._captchaImage.src) {
+ this._captchaImage.src = uri;
+ this._dialogDescription.textContent = TorStrings.settings.solveTheCaptcha;
+ this._setUIDisabled(false);
+ this._captchaEntryTextbox.focus();
+ this._captchaEntryTextbox.select();
+ }
+ }
+
+ _setUIDisabled(disabled) {
+ this._submitButton.disabled = this._captchaGuessIsEmpty() || disabled;
+ this._captchaEntryTextbox.disabled = disabled;
+ this._captchaRefreshButton.disabled = disabled;
+ }
+
+ _captchaGuessIsEmpty() {
+ return this._captchaEntryTextbox.value == "";
+ }
+
+ init(window, dialog) {
+ // defer to later until firefox has populated the dialog with all our elements
+ window.setTimeout(() => {
+ this._populateXUL(dialog);
+ }, 0);
+ }
+
+ close() {
+ BridgeDB.close();
+ }
+
+ /*
+ Event Handlers
+ */
+ onSubmitCaptcha() {
+ let captchaText = this._captchaEntryTextbox.value.trim();
+ // noop if the field is empty
+ if (captchaText == "") {
+ return;
+ }
+
+ // freeze ui while we make request
+ this._setUIDisabled(true);
+ this._incorrectCaptchaHbox.style.visibility = "hidden";
+
+ BridgeDB.submitCaptchaGuess(captchaText)
+ .then(aBridges => {
+ this._bridges = aBridges;
+
+ this._submitButton.disabled = false;
+ // This was successful, but use cancelDialog() to close, since
+ // we intercept the `dialogaccept` event.
+ this._dialog.cancelDialog();
+ })
+ .catch(aError => {
+ this._bridges = [];
+ this._setUIDisabled(false);
+ this._incorrectCaptchaHbox.style.visibility = "visible";
+ });
+ }
+
+ onRefreshCaptcha() {
+ this._setUIDisabled(true);
+ this._captchaImage.src = "";
+ this._dialogDescription.textContent =
+ TorStrings.settings.contactingBridgeDB;
+ this._captchaEntryTextbox.value = "";
+ this._incorrectCaptchaHbox.style.visibility = "hidden";
+
+ BridgeDB.requestNewCaptchaImage(this._proxyURI).then(uri => {
+ this._setcaptchaImage(uri);
+ });
+ }
+
+ openDialog(gSubDialog, aProxyURI, aCloseCallback) {
+ this._proxyURI = aProxyURI;
+ gSubDialog.open(
+ "chrome://browser/content/torpreferences/requestBridgeDialog.xhtml",
+ {
+ features: "resizable=yes",
+ closingCallback: () => {
+ this.close();
+ aCloseCallback(this._bridges);
+ }
+ },
+ this,
+ );
+ }
+}
diff --git a/browser/components/torpreferences/content/requestBridgeDialog.xhtml b/browser/components/torpreferences/content/requestBridgeDialog.xhtml
new file mode 100644
index 000000000000..64c4507807fb
--- /dev/null
+++ b/browser/components/torpreferences/content/requestBridgeDialog.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
+<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?>
+
+<window type="child"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml">
+<dialog id="torPreferences-requestBridge-dialog"
+ buttons="accept,cancel">
+ <!-- ok, so ​ is a zero-width space. We need to have *something* in the innerText so that XUL knows how tall the
+ description node is so that it can determine how large to make the dialog element's inner draw area. If we have
+ nothing in the innerText, then it collapse to 0 height, and the contents of the dialog ends up partially hidden >:( -->
+ <description id="torPreferences-requestBridge-description">​</description>
+ <!-- init to transparent 400x125 png -->
+ <image id="torPreferences-requestBridge-captchaImage" flex="1"/>
+ <hbox id="torPreferences-requestBridge-inputHbox">
+ <html:input id="torPreferences-requestBridge-captchaTextbox" type="text" style="-moz-box-flex: 1;"/>
+ <button id="torPreferences-requestBridge-refreshCaptchaButton"
+ image="chrome://browser/skin/reload.svg"
+ oncommand="requestBridgeDialog.onRefreshCaptcha();"/>
+ </hbox>
+ <hbox id="torPreferences-requestBridge-incorrectCaptchaHbox" align="center">
+ <image id="torPreferences-requestBridge-errorIcon" />
+ <label id="torPreferences-requestBridge-incorrectCaptchaError" flex="1"/>
+ </hbox>
+ <script type="application/javascript"><![CDATA[
+ "use strict";
+
+ let requestBridgeDialog = window.arguments[0];
+ let dialog = document.getElementById("torPreferences-requestBridge-dialog");
+ requestBridgeDialog.init(window, dialog);
+ ]]></script>
+</dialog>
+</window>
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torBridgeSettings.jsm b/browser/components/torpreferences/content/torBridgeSettings.jsm
new file mode 100644
index 000000000000..ceb61d3ec972
--- /dev/null
+++ b/browser/components/torpreferences/content/torBridgeSettings.jsm
@@ -0,0 +1,325 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "TorBridgeSource",
+ "TorBridgeSettings",
+ "makeTorBridgeSettingsNone",
+ "makeTorBridgeSettingsBuiltin",
+ "makeTorBridgeSettingsBridgeDB",
+ "makeTorBridgeSettingsUserProvided",
+];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+const TorBridgeSource = {
+ NONE: "NONE",
+ BUILTIN: "BUILTIN",
+ BRIDGEDB: "BRIDGEDB",
+ USERPROVIDED: "USERPROVIDED",
+};
+
+class TorBridgeSettings {
+ constructor() {
+ this._bridgeSource = TorBridgeSource.NONE;
+ this._selectedDefaultBridgeType = null;
+ this._bridgeStrings = [];
+ }
+
+ get selectedDefaultBridgeType() {
+ if (this._bridgeSource == TorBridgeSource.BUILTIN) {
+ return this._selectedDefaultBridgeType;
+ }
+ return undefined;
+ }
+
+ get bridgeSource() {
+ return this._bridgeSource;
+ }
+
+ // for display
+ get bridgeStrings() {
+ return this._bridgeStrings.join("\n");
+ }
+
+ // raw
+ get bridgeStringsArray() {
+ return this._bridgeStrings;
+ }
+
+ static get defaultBridgeTypes() {
+ if (TorBridgeSettings._defaultBridgeTypes) {
+ return TorBridgeSettings._defaultBridgeTypes;
+ }
+
+ let bridgeListBranch = Services.prefs.getBranch(
+ TorStrings.preferenceBranches.defaultBridge
+ );
+ let bridgePrefs = bridgeListBranch.getChildList("", {});
+
+ // an unordered set for shoving bridge types into
+ let bridgeTypes = new Set();
+ // look for keys ending in ".N" and treat string before that as the bridge type
+ const pattern = /\.[0-9]+$/;
+ for (const key of bridgePrefs) {
+ const offset = key.search(pattern);
+ if (offset != -1) {
+ const bt = key.substring(0, offset);
+ bridgeTypes.add(bt);
+ }
+ }
+
+ // recommended bridge type goes first in the list
+ let recommendedBridgeType = Services.prefs.getCharPref(
+ TorStrings.preferenceKeys.recommendedBridgeType,
+ null
+ );
+
+ let retval = [];
+ if (recommendedBridgeType && bridgeTypes.has(recommendedBridgeType)) {
+ retval.push(recommendedBridgeType);
+ }
+
+ for (const bridgeType of bridgeTypes.values()) {
+ if (bridgeType != recommendedBridgeType) {
+ retval.push(bridgeType);
+ }
+ }
+
+ // cache off
+ TorBridgeSettings._defaultBridgeTypes = retval;
+ return retval;
+ }
+
+ _readDefaultBridges(aBridgeType) {
+ let bridgeBranch = Services.prefs.getBranch(
+ TorStrings.preferenceBranches.defaultBridge
+ );
+ let bridgeBranchPrefs = bridgeBranch.getChildList("", {});
+
+ let retval = [];
+
+ // regex matches against strings ending in ".N" where N is a positive integer
+ let pattern = /\.[0-9]+$/;
+ for (const key of bridgeBranchPrefs) {
+ // verify the location of the match is the correct offset required for aBridgeType
+ // to fit, and that the string begins with aBridgeType
+ if (
+ key.search(pattern) == aBridgeType.length &&
+ key.startsWith(aBridgeType)
+ ) {
+ let bridgeStr = bridgeBranch.getCharPref(key);
+ retval.push(bridgeStr);
+ }
+ }
+
+ // fisher-yates shuffle
+ // shuffle so that Tor Browser users don't all try the built-in bridges in the same order
+ for (let i = retval.length - 1; i > 0; --i) {
+ // number n such that 0.0 <= n < 1.0
+ const n = Math.random();
+ // integer j such that 0 <= j <= i
+ const j = Math.floor(n * (i + 1));
+
+ // swap values at indices i and j
+ const tmp = retval[i];
+ retval[i] = retval[j];
+ retval[j] = tmp;
+ }
+
+ return retval;
+ }
+
+ _readBridgeDBBridges() {
+ let bridgeBranch = Services.prefs.getBranch(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}`
+ );
+ let bridgeBranchPrefs = bridgeBranch.getChildList("", {});
+ // the child prefs do not come in any particular order so sort the keys
+ // so the values can be compared to what we get out off torrc
+ bridgeBranchPrefs.sort();
+
+ // just assume all of the prefs under the parent point to valid bridge string
+ let retval = bridgeBranchPrefs.map(key =>
+ bridgeBranch.getCharPref(key).trim()
+ );
+
+ return retval;
+ }
+
+ _readTorrcBridges() {
+ let bridgeList = TorProtocolService.readStringArraySetting(
+ TorStrings.configKeys.bridgeList
+ );
+
+ let retval = [];
+ for (const line of bridgeList) {
+ let trimmedLine = line.trim();
+ if (trimmedLine) {
+ retval.push(trimmedLine);
+ }
+ }
+
+ return retval;
+ }
+
+ // analagous to initBridgeSettings()
+ readSettings() {
+ // restore to defaults
+ this._bridgeSource = TorBridgeSource.NONE;
+ this._selectedDefaultBridgeType = null;
+ this._bridgeStrings = [];
+
+ // So the way tor-launcher determines the origin of the configured bridges is a bit
+ // weird and depends on inferring our scenario based on some firefox prefs and the
+ // relationship between the saved list of bridges in about:config vs the list saved in torrc
+
+ // first off, if "extensions.torlauncher.default_bridge_type" is set to one of our
+ // builtin default types (obfs4, meek-azure, snowflake, etc) then we provide the
+ // bridges in "extensions.torlauncher.default_bridge.*" (filtered by our default_bridge_type)
+
+ // next, we compare the list of bridges saved in torrc to the bridges stored in the
+ // "extensions.torlauncher.bridgedb_bridge."" branch. If they match *exactly* then we assume
+ // the bridges were retrieved from BridgeDB and use those. If the torrc list is empty then we know
+ // we have no bridge settings
+
+ // finally, if none of the previous conditions are not met, it is assumed the bridges stored in
+ // torrc are user-provided
+
+ // what we should(?) do once we excise tor-launcher entirely is explicitly store an int/enum in
+ // about:config that tells us which scenario we are in so we don't have to guess
+
+ let defaultBridgeType = Services.prefs.getCharPref(
+ TorStrings.preferenceKeys.defaultBridgeType,
+ null
+ );
+
+ // check if source is BUILTIN
+ if (defaultBridgeType) {
+ this._bridgeStrings = this._readDefaultBridges(defaultBridgeType);
+ this._bridgeSource = TorBridgeSource.BUILTIN;
+ this._selectedDefaultBridgeType = defaultBridgeType;
+ return;
+ }
+
+ let torrcBridges = this._readTorrcBridges();
+
+ // no stored bridges means no bridge is in use
+ if (torrcBridges.length == 0) {
+ this._bridgeStrings = [];
+ this._bridgeSource = TorBridgeSource.NONE;
+ return;
+ }
+
+ let bridgedbBridges = this._readBridgeDBBridges();
+
+ // if these two lists are equal then we got our bridges from bridgedb
+ // ie: same element in identical order
+ let arraysEqual = (left, right) => {
+ if (left.length != right.length) {
+ return false;
+ }
+ const length = left.length;
+ for (let i = 0; i < length; ++i) {
+ if (left[i] != right[i]) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ // agreement between prefs and torrc means bridgedb bridges
+ if (arraysEqual(torrcBridges, bridgedbBridges)) {
+ this._bridgeStrings = torrcBridges;
+ this._bridgeSource = TorBridgeSource.BRIDGEDB;
+ return;
+ }
+
+ // otherwise they must be user provided
+ this._bridgeStrings = torrcBridges;
+ this._bridgeSource = TorBridgeSource.USERPROVIDED;
+ }
+
+ writeSettings() {
+ let settingsObject = new Map();
+
+ // init tor bridge settings to null
+ settingsObject.set(TorStrings.configKeys.useBridges, null);
+ settingsObject.set(TorStrings.configKeys.bridgeList, null);
+
+ // clear bridge related firefox prefs
+ Services.prefs.setCharPref(TorStrings.preferenceKeys.defaultBridgeType, "");
+ let bridgeBranch = Services.prefs.getBranch(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}`
+ );
+ let bridgeBranchPrefs = bridgeBranch.getChildList("", {});
+ for (const pref of bridgeBranchPrefs) {
+ Services.prefs.clearUserPref(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}${pref}`
+ );
+ }
+
+ switch (this._bridgeSource) {
+ case TorBridgeSource.BUILTIN:
+ // set builtin bridge type to use in prefs
+ Services.prefs.setCharPref(
+ TorStrings.preferenceKeys.defaultBridgeType,
+ this._selectedDefaultBridgeType
+ );
+ break;
+ case TorBridgeSource.BRIDGEDB:
+ // save bridges off to prefs
+ for (let i = 0; i < this.bridgeStringsArray.length; ++i) {
+ Services.prefs.setCharPref(
+ `${TorStrings.preferenceBranches.bridgeDBBridges}${i}`,
+ this.bridgeStringsArray[i]
+ );
+ }
+ break;
+ }
+
+ // write over our bridge list if bridges are enabled
+ if (this._bridgeSource != TorBridgeSource.NONE) {
+ settingsObject.set(TorStrings.configKeys.useBridges, true);
+ settingsObject.set(
+ TorStrings.configKeys.bridgeList,
+ this.bridgeStringsArray
+ );
+ }
+ TorProtocolService.writeSettings(settingsObject);
+ }
+}
+
+function makeTorBridgeSettingsNone() {
+ return new TorBridgeSettings();
+}
+
+function makeTorBridgeSettingsBuiltin(aBridgeType) {
+ let retval = new TorBridgeSettings();
+ retval._bridgeSource = TorBridgeSource.BUILTIN;
+ retval._selectedDefaultBridgeType = aBridgeType;
+ retval._bridgeStrings = retval._readDefaultBridges(aBridgeType);
+
+ return retval;
+}
+
+function makeTorBridgeSettingsBridgeDB(aBridges) {
+ let retval = new TorBridgeSettings();
+ retval._bridgeSource = TorBridgeSource.BRIDGEDB;
+ retval._selectedDefaultBridgeType = null;
+ retval._bridgeStrings = aBridges;
+
+ return retval;
+}
+
+function makeTorBridgeSettingsUserProvided(aBridges) {
+ let retval = new TorBridgeSettings();
+ retval._bridgeSource = TorBridgeSource.USERPROVIDED;
+ retval._selectedDefaultBridgeType = null;
+ retval._bridgeStrings = aBridges;
+
+ return retval;
+}
diff --git a/browser/components/torpreferences/content/torCategory.inc.xhtml b/browser/components/torpreferences/content/torCategory.inc.xhtml
new file mode 100644
index 000000000000..abe56200f571
--- /dev/null
+++ b/browser/components/torpreferences/content/torCategory.inc.xhtml
@@ -0,0 +1,9 @@
+<richlistitem id="category-tor"
+ class="category"
+ value="paneTor"
+ helpTopic="prefs-tor"
+ align="center"
+ hidden="true">
+ <image class="category-icon"/>
+ <label id="torPreferences-labelCategory" class="category-name" flex="1" value="Tor"/>
+</richlistitem>
diff --git a/browser/components/torpreferences/content/torFirewallSettings.jsm b/browser/components/torpreferences/content/torFirewallSettings.jsm
new file mode 100644
index 000000000000..e77f18ef2fae
--- /dev/null
+++ b/browser/components/torpreferences/content/torFirewallSettings.jsm
@@ -0,0 +1,72 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "TorFirewallSettings",
+ "makeTorFirewallSettingsNone",
+ "makeTorFirewallSettingsCustom",
+];
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+const { parseAddrPortList } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/parseFunctions.jsm"
+);
+
+class TorFirewallSettings {
+ constructor() {
+ this._allowedPorts = [];
+ }
+
+ get portsConfigurationString() {
+ let portStrings = this._allowedPorts.map(port => `*:${port}`);
+ return portStrings.join(",");
+ }
+
+ get commaSeparatedListString() {
+ return this._allowedPorts.join(",");
+ }
+
+ get hasPorts() {
+ return this._allowedPorts.length > 0;
+ }
+
+ readSettings() {
+ let addressPortList = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.reachableAddresses
+ );
+
+ let allowedPorts = [];
+ if (addressPortList) {
+ allowedPorts = parseAddrPortList(addressPortList);
+ }
+ this._allowedPorts = allowedPorts;
+ }
+
+ writeSettings() {
+ let settingsObject = new Map();
+
+ // init to null so Tor daemon resets if no ports
+ settingsObject.set(TorStrings.configKeys.reachableAddresses, null);
+
+ if (this._allowedPorts.length > 0) {
+ settingsObject.set(
+ TorStrings.configKeys.reachableAddresses,
+ this.portsConfigurationString
+ );
+ }
+
+ TorProtocolService.writeSettings(settingsObject);
+ }
+}
+
+function makeTorFirewallSettingsNone() {
+ return new TorFirewallSettings();
+}
+
+function makeTorFirewallSettingsCustom(aPortsList) {
+ let retval = new TorFirewallSettings();
+ retval._allowedPorts = aPortsList;
+ return retval;
+}
diff --git a/browser/components/torpreferences/content/torLogDialog.jsm b/browser/components/torpreferences/content/torLogDialog.jsm
new file mode 100644
index 000000000000..ecc684d878c2
--- /dev/null
+++ b/browser/components/torpreferences/content/torLogDialog.jsm
@@ -0,0 +1,66 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorLogDialog"];
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+class TorLogDialog {
+ constructor() {
+ this._dialog = null;
+ this._logTextarea = null;
+ this._copyLogButton = null;
+ }
+
+ static get selectors() {
+ return {
+ copyLogButton: "extra1",
+ logTextarea: "textarea#torPreferences-torDialog-textarea",
+ };
+ }
+
+ _populateXUL(aDialog) {
+ this._dialog = aDialog;
+ const dialogWin = this._dialog.parentElement;
+ dialogWin.setAttribute("title", TorStrings.settings.torLogDialogTitle);
+
+ this._logTextarea = this._dialog.querySelector(
+ TorLogDialog.selectors.logTextarea
+ );
+
+ this._copyLogButton = this._dialog.getButton(
+ TorLogDialog.selectors.copyLogButton
+ );
+ this._copyLogButton.setAttribute("label", TorStrings.settings.copyLog);
+ this._copyLogButton.addEventListener("command", () => {
+ this.copyTorLog();
+ });
+
+ this._logTextarea.value = TorProtocolService.getLog();
+ }
+
+ init(window, aDialog) {
+ // defer to later until firefox has populated the dialog with all our elements
+ window.setTimeout(() => {
+ this._populateXUL(aDialog);
+ }, 0);
+ }
+
+ copyTorLog() {
+ // Copy tor log messages to the system clipboard.
+ let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
+ Ci.nsIClipboardHelper
+ );
+ clipboard.copyString(this._logTextarea.value);
+ }
+
+ openDialog(gSubDialog) {
+ gSubDialog.open(
+ "chrome://browser/content/torpreferences/torLogDialog.xhtml",
+ { features: "resizable=yes" },
+ this
+ );
+ }
+}
diff --git a/browser/components/torpreferences/content/torLogDialog.xhtml b/browser/components/torpreferences/content/torLogDialog.xhtml
new file mode 100644
index 000000000000..9c17f8132978
--- /dev/null
+++ b/browser/components/torpreferences/content/torLogDialog.xhtml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
+<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?>
+
+<window type="child"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml">
+<dialog id="torPreferences-torLog-dialog"
+ buttons="accept,extra1">
+ <html:textarea
+ id="torPreferences-torDialog-textarea"
+ multiline="true"
+ readonly="true"/>
+ <script type="application/javascript"><![CDATA[
+ "use strict";
+
+ let torLogDialog = window.arguments[0];
+ let dialog = document.getElementById("torPreferences-torLog-dialog");
+ torLogDialog.init(window, dialog);
+ ]]></script>
+</dialog>
+</window>
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torPane.js b/browser/components/torpreferences/content/torPane.js
new file mode 100644
index 000000000000..49054b5dac6a
--- /dev/null
+++ b/browser/components/torpreferences/content/torPane.js
@@ -0,0 +1,857 @@
+"use strict";
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+
+const {
+ TorBridgeSource,
+ TorBridgeSettings,
+ makeTorBridgeSettingsNone,
+ makeTorBridgeSettingsBuiltin,
+ makeTorBridgeSettingsBridgeDB,
+ makeTorBridgeSettingsUserProvided,
+} = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/torBridgeSettings.jsm"
+);
+
+const {
+ TorProxyType,
+ TorProxySettings,
+ makeTorProxySettingsNone,
+ makeTorProxySettingsSocks4,
+ makeTorProxySettingsSocks5,
+ makeTorProxySettingsHTTPS,
+} = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/torProxySettings.jsm"
+);
+const {
+ TorFirewallSettings,
+ makeTorFirewallSettingsNone,
+ makeTorFirewallSettingsCustom,
+} = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/torFirewallSettings.jsm"
+);
+
+const { TorLogDialog } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/torLogDialog.jsm"
+);
+
+const { RequestBridgeDialog } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/requestBridgeDialog.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TorStrings",
+ "resource:///modules/TorStrings.jsm"
+);
+
+const { parsePort, parseBridgeStrings, parsePortList } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/parseFunctions.jsm"
+);
+
+/*
+ Tor Pane
+
+ Code for populating the XUL in about:preferences#tor, handling input events, interfacing with tor-launcher
+*/
+const gTorPane = (function() {
+ /* CSS selectors for all of the Tor Network DOM elements we need to access */
+ const selectors = {
+ category: {
+ title: "label#torPreferences-labelCategory",
+ },
+ torPreferences: {
+ header: "h1#torPreferences-header",
+ description: "span#torPreferences-description",
+ learnMore: "label#torPreferences-learnMore",
+ },
+ bridges: {
+ header: "h2#torPreferences-bridges-header",
+ description: "span#torPreferences-bridges-description",
+ learnMore: "label#torPreferences-bridges-learnMore",
+ useBridgeCheckbox: "checkbox#torPreferences-bridges-toggle",
+ bridgeSelectionRadiogroup:
+ "radiogroup#torPreferences-bridges-bridgeSelection",
+ builtinBridgeOption: "radio#torPreferences-bridges-radioBuiltin",
+ builtinBridgeList: "menulist#torPreferences-bridges-builtinList",
+ requestBridgeOption: "radio#torPreferences-bridges-radioRequestBridge",
+ requestBridgeButton: "button#torPreferences-bridges-buttonRequestBridge",
+ requestBridgeTextarea:
+ "textarea#torPreferences-bridges-textareaRequestBridge",
+ provideBridgeOption: "radio#torPreferences-bridges-radioProvideBridge",
+ provideBridgeDescription:
+ "description#torPreferences-bridges-descriptionProvideBridge",
+ provideBridgeTextarea:
+ "textarea#torPreferences-bridges-textareaProvideBridge",
+ },
+ advanced: {
+ header: "h2#torPreferences-advanced-header",
+ description: "span#torPreferences-advanced-description",
+ learnMore: "label#torPreferences-advanced-learnMore",
+ useProxyCheckbox: "checkbox#torPreferences-advanced-toggleProxy",
+ proxyTypeLabel: "label#torPreferences-localProxy-type",
+ proxyTypeList: "menulist#torPreferences-localProxy-builtinList",
+ proxyAddressLabel: "label#torPreferences-localProxy-address",
+ proxyAddressTextbox: "input#torPreferences-localProxy-textboxAddress",
+ proxyPortLabel: "label#torPreferences-localProxy-port",
+ proxyPortTextbox: "input#torPreferences-localProxy-textboxPort",
+ proxyUsernameLabel: "label#torPreferences-localProxy-username",
+ proxyUsernameTextbox: "input#torPreferences-localProxy-textboxUsername",
+ proxyPasswordLabel: "label#torPreferences-localProxy-password",
+ proxyPasswordTextbox: "input#torPreferences-localProxy-textboxPassword",
+ useFirewallCheckbox: "checkbox#torPreferences-advanced-toggleFirewall",
+ firewallAllowedPortsLabel: "label#torPreferences-advanced-allowedPorts",
+ firewallAllowedPortsTextbox:
+ "input#torPreferences-advanced-textboxAllowedPorts",
+ torLogsLabel: "label#torPreferences-torLogs",
+ torLogsButton: "button#torPreferences-buttonTorLogs",
+ },
+ }; /* selectors */
+
+ let retval = {
+ // cached frequently accessed DOM elements
+ _useBridgeCheckbox: null,
+ _bridgeSelectionRadiogroup: null,
+ _builtinBridgeOption: null,
+ _builtinBridgeMenulist: null,
+ _requestBridgeOption: null,
+ _requestBridgeButton: null,
+ _requestBridgeTextarea: null,
+ _provideBridgeOption: null,
+ _provideBridgeTextarea: null,
+ _useProxyCheckbox: null,
+ _proxyTypeLabel: null,
+ _proxyTypeMenulist: null,
+ _proxyAddressLabel: null,
+ _proxyAddressTextbox: null,
+ _proxyPortLabel: null,
+ _proxyPortTextbox: null,
+ _proxyUsernameLabel: null,
+ _proxyUsernameTextbox: null,
+ _proxyPasswordLabel: null,
+ _proxyPasswordTextbox: null,
+ _useFirewallCheckbox: null,
+ _allowedPortsLabel: null,
+ _allowedPortsTextbox: null,
+
+ // tor network settings
+ _bridgeSettings: null,
+ _proxySettings: null,
+ _firewallSettings: null,
+
+ // disables the provided list of elements
+ _setElementsDisabled(elements, disabled) {
+ for (let currentElement of elements) {
+ currentElement.disabled = disabled;
+ }
+ },
+
+ // populate xul with strings and cache the relevant elements
+ _populateXUL() {
+ // saves tor settings to disk when navigate away from about:preferences
+ window.addEventListener("blur", val => {
+ TorProtocolService.flushSettings();
+ });
+
+ document
+ .querySelector(selectors.category.title)
+ .setAttribute("value", TorStrings.settings.categoryTitle);
+
+ let prefpane = document.getElementById("mainPrefPane");
+
+ // Heading
+ prefpane.querySelector(selectors.torPreferences.header).innerText =
+ TorStrings.settings.torPreferencesHeading;
+ prefpane.querySelector(selectors.torPreferences.description).textContent =
+ TorStrings.settings.torPreferencesDescription;
+ {
+ let learnMore = prefpane.querySelector(
+ selectors.torPreferences.learnMore
+ );
+ learnMore.setAttribute("value", TorStrings.settings.learnMore);
+ learnMore.setAttribute(
+ "href",
+ TorStrings.settings.learnMoreTorBrowserURL
+ );
+ }
+
+ // Bridge setup
+ prefpane.querySelector(selectors.bridges.header).innerText =
+ TorStrings.settings.bridgesHeading;
+ prefpane.querySelector(selectors.bridges.description).textContent =
+ TorStrings.settings.bridgesDescription;
+ {
+ let learnMore = prefpane.querySelector(selectors.bridges.learnMore);
+ learnMore.setAttribute("value", TorStrings.settings.learnMore);
+ learnMore.setAttribute("href", TorStrings.settings.learnMoreBridgesURL);
+ }
+
+ this._useBridgeCheckbox = prefpane.querySelector(
+ selectors.bridges.useBridgeCheckbox
+ );
+ this._useBridgeCheckbox.setAttribute(
+ "label",
+ TorStrings.settings.useBridge
+ );
+ this._useBridgeCheckbox.addEventListener("command", e => {
+ const checked = this._useBridgeCheckbox.checked;
+ gTorPane.onToggleBridge(checked).onUpdateBridgeSettings();
+ });
+ this._bridgeSelectionRadiogroup = prefpane.querySelector(
+ selectors.bridges.bridgeSelectionRadiogroup
+ );
+ this._bridgeSelectionRadiogroup.value = TorBridgeSource.BUILTIN;
+ this._bridgeSelectionRadiogroup.addEventListener("command", e => {
+ const value = this._bridgeSelectionRadiogroup.value;
+ gTorPane.onSelectBridgeOption(value).onUpdateBridgeSettings();
+ });
+
+ // Builtin bridges
+ this._builtinBridgeOption = prefpane.querySelector(
+ selectors.bridges.builtinBridgeOption
+ );
+ this._builtinBridgeOption.setAttribute(
+ "label",
+ TorStrings.settings.selectBridge
+ );
+ this._builtinBridgeOption.setAttribute("value", TorBridgeSource.BUILTIN);
+ this._builtinBridgeMenulist = prefpane.querySelector(
+ selectors.bridges.builtinBridgeList
+ );
+ this._builtinBridgeMenulist.addEventListener("command", e => {
+ gTorPane.onUpdateBridgeSettings();
+ });
+
+ // Request bridge
+ this._requestBridgeOption = prefpane.querySelector(
+ selectors.bridges.requestBridgeOption
+ );
+ this._requestBridgeOption.setAttribute(
+ "label",
+ TorStrings.settings.requestBridgeFromTorProject
+ );
+ this._requestBridgeOption.setAttribute("value", TorBridgeSource.BRIDGEDB);
+ this._requestBridgeButton = prefpane.querySelector(
+ selectors.bridges.requestBridgeButton
+ );
+ this._requestBridgeButton.setAttribute(
+ "label",
+ TorStrings.settings.requestNewBridge
+ );
+ this._requestBridgeButton.addEventListener("command", () =>
+ gTorPane.onRequestBridge()
+ );
+ this._requestBridgeTextarea = prefpane.querySelector(
+ selectors.bridges.requestBridgeTextarea
+ );
+
+ // Provide a bridge
+ this._provideBridgeOption = prefpane.querySelector(
+ selectors.bridges.provideBridgeOption
+ );
+ this._provideBridgeOption.setAttribute(
+ "label",
+ TorStrings.settings.provideBridge
+ );
+ this._provideBridgeOption.setAttribute(
+ "value",
+ TorBridgeSource.USERPROVIDED
+ );
+ prefpane.querySelector(
+ selectors.bridges.provideBridgeDescription
+ ).textContent = TorStrings.settings.provideBridgeDirections;
+ this._provideBridgeTextarea = prefpane.querySelector(
+ selectors.bridges.provideBridgeTextarea
+ );
+ this._provideBridgeTextarea.setAttribute(
+ "placeholder",
+ TorStrings.settings.provideBridgePlaceholder
+ );
+ this._provideBridgeTextarea.addEventListener("blur", () => {
+ gTorPane.onUpdateBridgeSettings();
+ });
+
+ // Advanced setup
+ prefpane.querySelector(selectors.advanced.header).innerText =
+ TorStrings.settings.advancedHeading;
+ prefpane.querySelector(selectors.advanced.description).textContent =
+ TorStrings.settings.advancedDescription;
+ {
+ let learnMore = prefpane.querySelector(selectors.advanced.learnMore);
+ learnMore.setAttribute("value", TorStrings.settings.learnMore);
+ learnMore.setAttribute(
+ "href",
+ TorStrings.settings.learnMoreNetworkSettingsURL
+ );
+ }
+
+ // Local Proxy
+ this._useProxyCheckbox = prefpane.querySelector(
+ selectors.advanced.useProxyCheckbox
+ );
+ this._useProxyCheckbox.setAttribute(
+ "label",
+ TorStrings.settings.useLocalProxy
+ );
+ this._useProxyCheckbox.addEventListener("command", e => {
+ const checked = this._useProxyCheckbox.checked;
+ gTorPane.onToggleProxy(checked).onUpdateProxySettings();
+ });
+ this._proxyTypeLabel = prefpane.querySelector(
+ selectors.advanced.proxyTypeLabel
+ );
+ this._proxyTypeLabel.setAttribute("value", TorStrings.settings.proxyType);
+
+ let mockProxies = [
+ {
+ value: TorProxyType.SOCKS4,
+ label: TorStrings.settings.proxyTypeSOCKS4,
+ },
+ {
+ value: TorProxyType.SOCKS5,
+ label: TorStrings.settings.proxyTypeSOCKS5,
+ },
+ { value: TorProxyType.HTTPS, label: TorStrings.settings.proxyTypeHTTP },
+ ];
+ this._proxyTypeMenulist = prefpane.querySelector(
+ selectors.advanced.proxyTypeList
+ );
+ this._proxyTypeMenulist.addEventListener("command", e => {
+ const value = this._proxyTypeMenulist.value;
+ gTorPane.onSelectProxyType(value).onUpdateProxySettings();
+ });
+ for (let currentProxy of mockProxies) {
+ let menuEntry = document.createXULElement("menuitem");
+ menuEntry.setAttribute("value", currentProxy.value);
+ menuEntry.setAttribute("label", currentProxy.label);
+ this._proxyTypeMenulist
+ .querySelector("menupopup")
+ .appendChild(menuEntry);
+ }
+
+ this._proxyAddressLabel = prefpane.querySelector(
+ selectors.advanced.proxyAddressLabel
+ );
+ this._proxyAddressLabel.setAttribute(
+ "value",
+ TorStrings.settings.proxyAddress
+ );
+ this._proxyAddressTextbox = prefpane.querySelector(
+ selectors.advanced.proxyAddressTextbox
+ );
+ this._proxyAddressTextbox.setAttribute(
+ "placeholder",
+ TorStrings.settings.proxyAddressPlaceholder
+ );
+ this._proxyAddressTextbox.addEventListener("blur", () => {
+ gTorPane.onUpdateProxySettings();
+ });
+ this._proxyPortLabel = prefpane.querySelector(
+ selectors.advanced.proxyPortLabel
+ );
+ this._proxyPortLabel.setAttribute("value", TorStrings.settings.proxyPort);
+ this._proxyPortTextbox = prefpane.querySelector(
+ selectors.advanced.proxyPortTextbox
+ );
+ this._proxyPortTextbox.addEventListener("blur", () => {
+ gTorPane.onUpdateProxySettings();
+ });
+ this._proxyUsernameLabel = prefpane.querySelector(
+ selectors.advanced.proxyUsernameLabel
+ );
+ this._proxyUsernameLabel.setAttribute(
+ "value",
+ TorStrings.settings.proxyUsername
+ );
+ this._proxyUsernameTextbox = prefpane.querySelector(
+ selectors.advanced.proxyUsernameTextbox
+ );
+ this._proxyUsernameTextbox.setAttribute(
+ "placeholder",
+ TorStrings.settings.proxyUsernamePasswordPlaceholder
+ );
+ this._proxyUsernameTextbox.addEventListener("blur", () => {
+ gTorPane.onUpdateProxySettings();
+ });
+ this._proxyPasswordLabel = prefpane.querySelector(
+ selectors.advanced.proxyPasswordLabel
+ );
+ this._proxyPasswordLabel.setAttribute(
+ "value",
+ TorStrings.settings.proxyPassword
+ );
+ this._proxyPasswordTextbox = prefpane.querySelector(
+ selectors.advanced.proxyPasswordTextbox
+ );
+ this._proxyPasswordTextbox.setAttribute(
+ "placeholder",
+ TorStrings.settings.proxyUsernamePasswordPlaceholder
+ );
+ this._proxyPasswordTextbox.addEventListener("blur", () => {
+ gTorPane.onUpdateProxySettings();
+ });
+
+ // Local firewall
+ this._useFirewallCheckbox = prefpane.querySelector(
+ selectors.advanced.useFirewallCheckbox
+ );
+ this._useFirewallCheckbox.setAttribute(
+ "label",
+ TorStrings.settings.useFirewall
+ );
+ this._useFirewallCheckbox.addEventListener("command", e => {
+ const checked = this._useFirewallCheckbox.checked;
+ gTorPane.onToggleFirewall(checked).onUpdateFirewallSettings();
+ });
+ this._allowedPortsLabel = prefpane.querySelector(
+ selectors.advanced.firewallAllowedPortsLabel
+ );
+ this._allowedPortsLabel.setAttribute(
+ "value",
+ TorStrings.settings.allowedPorts
+ );
+ this._allowedPortsTextbox = prefpane.querySelector(
+ selectors.advanced.firewallAllowedPortsTextbox
+ );
+ this._allowedPortsTextbox.setAttribute(
+ "placeholder",
+ TorStrings.settings.allowedPortsPlaceholder
+ );
+ this._allowedPortsTextbox.addEventListener("blur", () => {
+ gTorPane.onUpdateFirewallSettings();
+ });
+
+ // Tor logs
+ prefpane
+ .querySelector(selectors.advanced.torLogsLabel)
+ .setAttribute("value", TorStrings.settings.showTorDaemonLogs);
+ let torLogsButton = prefpane.querySelector(
+ selectors.advanced.torLogsButton
+ );
+ torLogsButton.setAttribute("label", TorStrings.settings.showLogs);
+ torLogsButton.addEventListener("command", () => {
+ gTorPane.onViewTorLogs();
+ });
+
+ // Disable all relevant elements by default
+ this._setElementsDisabled(
+ [
+ this._builtinBridgeOption,
+ this._builtinBridgeMenulist,
+ this._requestBridgeOption,
+ this._requestBridgeButton,
+ this._requestBridgeTextarea,
+ this._provideBridgeOption,
+ this._provideBridgeTextarea,
+ this._proxyTypeLabel,
+ this._proxyTypeMenulist,
+ this._proxyAddressLabel,
+ this._proxyAddressTextbox,
+ this._proxyPortLabel,
+ this._proxyPortTextbox,
+ this._proxyUsernameLabel,
+ this._proxyUsernameTextbox,
+ this._proxyPasswordLabel,
+ this._proxyPasswordTextbox,
+ this._allowedPortsLabel,
+ this._allowedPortsTextbox,
+ ],
+ true
+ );
+
+ // load bridge settings
+ let torBridgeSettings = new TorBridgeSettings();
+ torBridgeSettings.readSettings();
+
+ // populate the bridge list
+ for (let currentBridge of TorBridgeSettings.defaultBridgeTypes) {
+ let menuEntry = document.createXULElement("menuitem");
+ menuEntry.setAttribute("value", currentBridge);
+ menuEntry.setAttribute("label", currentBridge);
+ this._builtinBridgeMenulist
+ .querySelector("menupopup")
+ .appendChild(menuEntry);
+ }
+
+ this.onSelectBridgeOption(torBridgeSettings.bridgeSource);
+ this.onToggleBridge(
+ torBridgeSettings.bridgeSource != TorBridgeSource.NONE
+ );
+ switch (torBridgeSettings.bridgeSource) {
+ case TorBridgeSource.NONE:
+ break;
+ case TorBridgeSource.BUILTIN:
+ this._builtinBridgeMenulist.value =
+ torBridgeSettings.selectedDefaultBridgeType;
+ break;
+ case TorBridgeSource.BRIDGEDB:
+ this._requestBridgeTextarea.value = torBridgeSettings.bridgeStrings;
+ break;
+ case TorBridgeSource.USERPROVIDED:
+ this._provideBridgeTextarea.value = torBridgeSettings.bridgeStrings;
+ break;
+ }
+
+ this._bridgeSettings = torBridgeSettings;
+
+ // load proxy settings
+ let torProxySettings = new TorProxySettings();
+ torProxySettings.readSettings();
+
+ if (torProxySettings.type != TorProxyType.NONE) {
+ this.onToggleProxy(true);
+ this.onSelectProxyType(torProxySettings.type);
+ this._proxyAddressTextbox.value = torProxySettings.address;
+ this._proxyPortTextbox.value = torProxySettings.port;
+ this._proxyUsernameTextbox.value = torProxySettings.username;
+ this._proxyPasswordTextbox.value = torProxySettings.password;
+ }
+
+ this._proxySettings = torProxySettings;
+
+ // load firewall settings
+ let torFirewallSettings = new TorFirewallSettings();
+ torFirewallSettings.readSettings();
+
+ if (torFirewallSettings.hasPorts) {
+ this.onToggleFirewall(true);
+ this._allowedPortsTextbox.value =
+ torFirewallSettings.commaSeparatedListString;
+ }
+
+ this._firewallSettings = torFirewallSettings;
+ },
+
+ init() {
+ this._populateXUL();
+ },
+
+ // whether the page should be present in about:preferences
+ get enabled() {
+ return TorProtocolService.ownsTorDaemon;
+ },
+
+ //
+ // Callbacks
+ //
+
+ // callback when using bridges toggled
+ onToggleBridge(enabled) {
+ this._useBridgeCheckbox.checked = enabled;
+ let disabled = !enabled;
+
+ // first disable all the bridge related elements
+ this._setElementsDisabled(
+ [
+ this._builtinBridgeOption,
+ this._builtinBridgeMenulist,
+ this._requestBridgeOption,
+ this._requestBridgeButton,
+ this._requestBridgeTextarea,
+ this._provideBridgeOption,
+ this._provideBridgeTextarea,
+ ],
+ disabled
+ );
+
+ // and selectively re-enable based on the radiogroup's current value
+ if (enabled) {
+ this.onSelectBridgeOption(this._bridgeSelectionRadiogroup.value);
+ } else {
+ this.onSelectBridgeOption(TorBridgeSource.NONE);
+ }
+ return this;
+ },
+
+ // callback when a bridge option is selected
+ onSelectBridgeOption(source) {
+ // disable all of the bridge elements under radio buttons
+ this._setElementsDisabled(
+ [
+ this._builtinBridgeMenulist,
+ this._requestBridgeButton,
+ this._requestBridgeTextarea,
+ this._provideBridgeTextarea,
+ ],
+ true
+ );
+
+ if (source != TorBridgeSource.NONE) {
+ this._bridgeSelectionRadiogroup.value = source;
+ }
+
+ switch (source) {
+ case TorBridgeSource.BUILTIN: {
+ this._setElementsDisabled([this._builtinBridgeMenulist], false);
+ break;
+ }
+ case TorBridgeSource.BRIDGEDB: {
+ this._setElementsDisabled(
+ [this._requestBridgeButton, this._requestBridgeTextarea],
+ false
+ );
+ break;
+ }
+ case TorBridgeSource.USERPROVIDED: {
+ this._setElementsDisabled([this._provideBridgeTextarea], false);
+ break;
+ }
+ }
+ return this;
+ },
+
+ // called when the request bridge button is activated
+ onRequestBridge() {
+ let requestBridgeDialog = new RequestBridgeDialog();
+ requestBridgeDialog.openDialog(
+ gSubDialog,
+ this._proxySettings.proxyURI,
+ aBridges => {
+ if (aBridges.length > 0) {
+ let bridgeSettings = makeTorBridgeSettingsBridgeDB(aBridges);
+ bridgeSettings.writeSettings();
+ this._bridgeSettings = bridgeSettings;
+
+ this._requestBridgeTextarea.value = bridgeSettings.bridgeStrings;
+ }
+ }
+ );
+ return this;
+ },
+
+ // pushes bridge settings from UI to tor
+ onUpdateBridgeSettings() {
+ let bridgeSettings = null;
+
+ let source = this._useBridgeCheckbox.checked
+ ? this._bridgeSelectionRadiogroup.value
+ : TorBridgeSource.NONE;
+ switch (source) {
+ case TorBridgeSource.NONE: {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ break;
+ }
+ case TorBridgeSource.BUILTIN: {
+ // if there is a built-in bridge already selected, use that
+ let bridgeType = this._builtinBridgeMenulist.value;
+ if (bridgeType) {
+ bridgeSettings = makeTorBridgeSettingsBuiltin(bridgeType);
+ } else {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ }
+ break;
+ }
+ case TorBridgeSource.BRIDGEDB: {
+ // if there are bridgedb bridges saved in the text area, use them
+ let bridgeStrings = this._requestBridgeTextarea.value;
+ if (bridgeStrings) {
+ let bridgeStringList = parseBridgeStrings(bridgeStrings);
+ bridgeSettings = makeTorBridgeSettingsBridgeDB(bridgeStringList);
+ } else {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ }
+ break;
+ }
+ case TorBridgeSource.USERPROVIDED: {
+ // if bridges already exist in the text area, use them
+ let bridgeStrings = this._provideBridgeTextarea.value;
+ if (bridgeStrings) {
+ let bridgeStringList = parseBridgeStrings(bridgeStrings);
+ bridgeSettings = makeTorBridgeSettingsUserProvided(
+ bridgeStringList
+ );
+ } else {
+ bridgeSettings = makeTorBridgeSettingsNone();
+ }
+ break;
+ }
+ }
+ bridgeSettings.writeSettings();
+ this._bridgeSettings = bridgeSettings;
+ return this;
+ },
+
+ // callback when proxy is toggled
+ onToggleProxy(enabled) {
+ this._useProxyCheckbox.checked = enabled;
+ let disabled = !enabled;
+
+ this._setElementsDisabled(
+ [
+ this._proxyTypeLabel,
+ this._proxyTypeMenulist,
+ this._proxyAddressLabel,
+ this._proxyAddressTextbox,
+ this._proxyPortLabel,
+ this._proxyPortTextbox,
+ this._proxyUsernameLabel,
+ this._proxyUsernameTextbox,
+ this._proxyPasswordLabel,
+ this._proxyPasswordTextbox,
+ ],
+ disabled
+ );
+ this.onSelectProxyType(this._proxyTypeMenulist.value);
+ return this;
+ },
+
+ // callback when proxy type is changed
+ onSelectProxyType(value) {
+ if (value == "") {
+ value = TorProxyType.NONE;
+ }
+ this._proxyTypeMenulist.value = value;
+ switch (value) {
+ case TorProxyType.NONE: {
+ this._setElementsDisabled(
+ [
+ this._proxyAddressLabel,
+ this._proxyAddressTextbox,
+ this._proxyPortLabel,
+ this._proxyPortTextbox,
+ this._proxyUsernameLabel,
+ this._proxyUsernameTextbox,
+ this._proxyPasswordLabel,
+ this._proxyPasswordTextbox,
+ ],
+ true
+ ); // DISABLE
+
+ this._proxyAddressTextbox.value = "";
+ this._proxyPortTextbox.value = "";
+ this._proxyUsernameTextbox.value = "";
+ this._proxyPasswordTextbox.value = "";
+ break;
+ }
+ case TorProxyType.SOCKS4: {
+ this._setElementsDisabled(
+ [
+ this._proxyAddressLabel,
+ this._proxyAddressTextbox,
+ this._proxyPortLabel,
+ this._proxyPortTextbox,
+ ],
+ false
+ ); // ENABLE
+ this._setElementsDisabled(
+ [
+ this._proxyUsernameLabel,
+ this._proxyUsernameTextbox,
+ this._proxyPasswordLabel,
+ this._proxyPasswordTextbox,
+ ],
+ true
+ ); // DISABLE
+
+ this._proxyUsernameTextbox.value = "";
+ this._proxyPasswordTextbox.value = "";
+ break;
+ }
+ case TorProxyType.SOCKS5:
+ case TorProxyType.HTTPS: {
+ this._setElementsDisabled(
+ [
+ this._proxyAddressLabel,
+ this._proxyAddressTextbox,
+ this._proxyPortLabel,
+ this._proxyPortTextbox,
+ this._proxyUsernameLabel,
+ this._proxyUsernameTextbox,
+ this._proxyPasswordLabel,
+ this._proxyPasswordTextbox,
+ ],
+ false
+ ); // ENABLE
+ break;
+ }
+ }
+ return this;
+ },
+
+ // pushes proxy settings from UI to tor
+ onUpdateProxySettings() {
+ const proxyType = this._useProxyCheckbox.checked
+ ? this._proxyTypeMenulist.value
+ : TorProxyType.NONE;
+ const addressString = this._proxyAddressTextbox.value;
+ const portString = this._proxyPortTextbox.value;
+ const usernameString = this._proxyUsernameTextbox.value;
+ const passwordString = this._proxyPasswordTextbox.value;
+
+ let proxySettings = null;
+
+ switch (proxyType) {
+ case TorProxyType.NONE:
+ proxySettings = makeTorProxySettingsNone();
+ break;
+ case TorProxyType.SOCKS4:
+ proxySettings = makeTorProxySettingsSocks4(
+ addressString,
+ parsePort(portString)
+ );
+ break;
+ case TorProxyType.SOCKS5:
+ proxySettings = makeTorProxySettingsSocks5(
+ addressString,
+ parsePort(portString),
+ usernameString,
+ passwordString
+ );
+ break;
+ case TorProxyType.HTTPS:
+ proxySettings = makeTorProxySettingsHTTPS(
+ addressString,
+ parsePort(portString),
+ usernameString,
+ passwordString
+ );
+ break;
+ }
+
+ proxySettings.writeSettings();
+ this._proxySettings = proxySettings;
+ return this;
+ },
+
+ // callback when firewall proxy is toggled
+ onToggleFirewall(enabled) {
+ this._useFirewallCheckbox.checked = enabled;
+ let disabled = !enabled;
+
+ this._setElementsDisabled(
+ [this._allowedPortsLabel, this._allowedPortsTextbox],
+ disabled
+ );
+
+ return this;
+ },
+
+ // pushes firewall settings from UI to tor
+ onUpdateFirewallSettings() {
+ let portListString = this._useFirewallCheckbox.checked
+ ? this._allowedPortsTextbox.value
+ : "";
+ let firewallSettings = null;
+
+ if (portListString) {
+ firewallSettings = makeTorFirewallSettingsCustom(
+ parsePortList(portListString)
+ );
+ } else {
+ firewallSettings = makeTorFirewallSettingsNone();
+ }
+
+ firewallSettings.writeSettings();
+ this._firewallSettings = firewallSettings;
+ return this;
+ },
+
+ onViewTorLogs() {
+ let torLogDialog = new TorLogDialog();
+ torLogDialog.openDialog(gSubDialog);
+ },
+ };
+ return retval;
+})(); /* gTorPane */
diff --git a/browser/components/torpreferences/content/torPane.xhtml b/browser/components/torpreferences/content/torPane.xhtml
new file mode 100644
index 000000000000..3c966b2b3726
--- /dev/null
+++ b/browser/components/torpreferences/content/torPane.xhtml
@@ -0,0 +1,123 @@
+<!-- Tor panel -->
+
+<script type="application/javascript"
+ src="chrome://browser/content/torpreferences/torPane.js"/>
+<html:template id="template-paneTor">
+<hbox id="torPreferencesCategory"
+ class="subcategory"
+ data-category="paneTor"
+ hidden="true">
+ <html:h1 id="torPreferences-header"/>
+</hbox>
+
+<groupbox data-category="paneTor"
+ hidden="true">
+ <description flex="1">
+ <html:span id="torPreferences-description" class="tail-with-learn-more"/>
+ <label id="torPreferences-learnMore" class="learnMore text-link" is="text-link"/>
+ </description>
+</groupbox>
+
+<!-- Bridges -->
+<groupbox id="torPreferences-bridges-group"
+ data-category="paneTor"
+ hidden="true">
+ <html:h2 id="torPreferences-bridges-header"/>
+ <description flex="1">
+ <html:span id="torPreferences-bridges-description" class="tail-with-learn-more"/>
+ <label id="torPreferences-bridges-learnMore" class="learnMore text-link" is="text-link"/>
+ </description>
+ <checkbox id="torPreferences-bridges-toggle"/>
+ <radiogroup id="torPreferences-bridges-bridgeSelection">
+ <hbox class="indent">
+ <radio id="torPreferences-bridges-radioBuiltin"/>
+ <spacer flex="1"/>
+ <menulist id="torPreferences-bridges-builtinList" class="torMarginFix">
+ <menupopup/>
+ </menulist>
+ </hbox>
+ <vbox class="indent">
+ <hbox>
+ <radio id="torPreferences-bridges-radioRequestBridge"/>
+ <space flex="1"/>
+ <button id="torPreferences-bridges-buttonRequestBridge" class="torMarginFix"/>
+ </hbox>
+ <html:textarea
+ id="torPreferences-bridges-textareaRequestBridge"
+ class="indent torMarginFix"
+ multiline="true"
+ rows="3"
+ readonly="true"/>
+ </vbox>
+ <hbox class="indent" flex="1">
+ <vbox flex="1">
+ <radio id="torPreferences-bridges-radioProvideBridge"/>
+ <description id="torPreferences-bridges-descriptionProvideBridge" class="indent"/>
+ <html:textarea
+ id="torPreferences-bridges-textareaProvideBridge"
+ class="indent torMarginFix"
+ multiline="true"
+ rows="3"/>
+ </vbox>
+ </hbox>
+ </radiogroup>
+</groupbox>
+
+<!-- Advanced -->
+<groupbox id="torPreferences-advanced-group"
+ data-category="paneTor"
+ hidden="true">
+ <html:h2 id="torPreferences-advanced-header"/>
+ <description flex="1">
+ <html:span id="torPreferences-advanced-description" class="tail-with-learn-more"/>
+ <label id="torPreferences-advanced-learnMore" class="learnMore text-link" is="text-link" style="display:none"/>
+ </description>
+ <box id="torPreferences-advanced-grid">
+ <!-- Local Proxy -->
+ <hbox class="torPreferences-advanced-checkbox-container">
+ <checkbox id="torPreferences-advanced-toggleProxy"/>
+ </hbox>
+ <hbox class="indent" align="center">
+ <label id="torPreferences-localProxy-type"/>
+ </hbox>
+ <hbox align="center">
+ <spacer flex="1"/>
+ <menulist id="torPreferences-localProxy-builtinList" class="torMarginFix">
+ <menupopup/>
+ </menulist>
+ </hbox>
+ <hbox class="indent" align="center">
+ <label id="torPreferences-localProxy-address"/>
+ </hbox>
+ <hbox align="center">
+ <html:input id="torPreferences-localProxy-textboxAddress" type="text" class="torMarginFix"/>
+ <label id="torPreferences-localProxy-port"/>
+ <!-- proxy-port-input class style pulled from preferences.css and used in the vanilla proxy setup menu -->
+ <html:input id="torPreferences-localProxy-textboxPort" class="proxy-port-input torMarginFix" hidespinbuttons="true" type="number" min="0" max="65535" maxlength="5"/>
+ </hbox>
+ <hbox class="indent" align="center">
+ <label id="torPreferences-localProxy-username"/>
+ </hbox>
+ <hbox align="center">
+ <html:input id="torPreferences-localProxy-textboxUsername" type="text" class="torMarginFix"/>
+ <label id="torPreferences-localProxy-password"/>
+ <html:input id="torPreferences-localProxy-textboxPassword" class="torMarginFix" type="password"/>
+ </hbox>
+ <!-- Firewall -->
+ <hbox class="torPreferences-advanced-checkbox-container">
+ <checkbox id="torPreferences-advanced-toggleFirewall"/>
+ </hbox>
+ <hbox class="indent" align="center">
+ <label id="torPreferences-advanced-allowedPorts"/>
+ </hbox>
+ <hbox align="center">
+ <html:input id="torPreferences-advanced-textboxAllowedPorts" type="text" class="torMarginFix" value="80,443"/>
+ </hbox>
+ </box>
+ <hbox id="torPreferences-torDaemon-hbox" align="center">
+ <label id="torPreferences-torLogs"/>
+ <spacer flex="1"/>
+ <button id="torPreferences-buttonTorLogs" class="torMarginFix"/>
+ </hbox>
+</groupbox>
+</html:template>
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torPreferences.css b/browser/components/torpreferences/content/torPreferences.css
new file mode 100644
index 000000000000..4dac2c457823
--- /dev/null
+++ b/browser/components/torpreferences/content/torPreferences.css
@@ -0,0 +1,77 @@
+#category-tor > .category-icon {
+ list-style-image: url("chrome://browser/content/torpreferences/torPreferencesIcon.svg");
+}
+
+#torPreferences-advanced-grid {
+ display: grid;
+ grid-template-columns: auto 1fr;
+}
+
+.torPreferences-advanced-checkbox-container {
+ grid-column: 1 / 3;
+}
+
+#torPreferences-localProxy-textboxAddress,
+#torPreferences-localProxy-textboxUsername,
+#torPreferences-localProxy-textboxPassword,
+#torPreferences-advanced-textboxAllowedPorts {
+ -moz-box-flex: 1;
+}
+
+hbox#torPreferences-torDaemon-hbox {
+ margin-top: 20px;
+}
+
+description#torPreferences-requestBridge-description {
+ /*margin-bottom: 1em;*/
+ min-height: 2em;
+}
+
+image#torPreferences-requestBridge-captchaImage {
+ margin: 1em;
+ min-height: 125px;
+}
+
+button#torPreferences-requestBridge-refreshCaptchaButton {
+ min-width: initial;
+}
+
+dialog#torPreferences-requestBridge-dialog > hbox {
+ margin-bottom: 1em;
+}
+
+/*
+ Various elements that really should be lining up don't because they have inconsistent margins
+*/
+.torMarginFix {
+ margin-left : 4px;
+ margin-right : 4px;
+}
+
+/*
+ This hbox is hidden by css here by default so that the
+ xul dialog allocates enough screen space for the error message
+ element, otherwise it gets cut off since dialog's overflow is hidden
+*/
+hbox#torPreferences-requestBridge-incorrectCaptchaHbox {
+ visibility: hidden;
+}
+
+image#torPreferences-requestBridge-errorIcon {
+ list-style-image: url("chrome://browser/skin/warning.svg");
+}
+
+groupbox#torPreferences-bridges-group textarea {
+ white-space: pre;
+ overflow: auto;
+}
+
+textarea#torPreferences-torDialog-textarea {
+ -moz-box-flex: 1;
+ font-family: monospace;
+ font-size: 0.8em;
+ white-space: pre;
+ overflow: auto;
+ /* 10 lines */
+ min-height: 20em;
+}
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torPreferencesIcon.svg b/browser/components/torpreferences/content/torPreferencesIcon.svg
new file mode 100644
index 000000000000..d7895f1107c5
--- /dev/null
+++ b/browser/components/torpreferences/content/torPreferencesIcon.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+ <g fill="context-fill" fill-opacity="context-fill-opacity" fill-rule="nonzero">
+ <path d="M12.0246161,21.8174863 L12.0246161,20.3628098 C16.6324777,20.3495038 20.3634751,16.6108555 20.3634751,11.9996673 C20.3634751,7.38881189 16.6324777,3.65016355 12.0246161,3.63685757 L12.0246161,2.18218107 C17.4358264,2.1958197 21.8178189,6.58546322 21.8178189,11.9996673 C21.8178189,17.4142042 17.4358264,21.8041803 12.0246161,21.8174863 L12.0246161,21.8174863 Z M12.0246161,16.7259522 C14.623607,16.7123136 16.7272828,14.6023175 16.7272828,11.9996673 C16.7272828,9.39734991 14.623607,7.28735377 12.0246161,7.27371516 L12.0246161,5.81937131 C15.4272884,5.8326773 18.1819593,8.59400123 18.1819593,11.9996673 C18.1819593,15.4056661 15.4272884,18.1669901 12.0246161,18.1802961 L12.0246161,16.7259522 Z M12.0246161,9.45556355 C13.4187503,9.46886953 14.5454344,10.6022066 14.5454344,11.9996673 C14.5454344,13.3974608 13.4187503,14.5307978 12.0246161,14.5441038 L12.0246161,9.45556355 Z M0,11.9996673 C0,18.6273771 5.37229031,24 12,24 C18.6273771,24 24,18.6273771 24,11.9996673 C24,5.37229031
18.6273771,0 12,0 C5.37229031,0 0,5.37229031 0,11.9996673 Z"/>
+ </g>
+</svg>
\ No newline at end of file
diff --git a/browser/components/torpreferences/content/torProxySettings.jsm b/browser/components/torpreferences/content/torProxySettings.jsm
new file mode 100644
index 000000000000..98bb5e8d5cbf
--- /dev/null
+++ b/browser/components/torpreferences/content/torProxySettings.jsm
@@ -0,0 +1,245 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "TorProxyType",
+ "TorProxySettings",
+ "makeTorProxySettingsNone",
+ "makeTorProxySettingsSocks4",
+ "makeTorProxySettingsSocks5",
+ "makeTorProxySettingsHTTPS",
+];
+
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+const { parseAddrPort, parseUsernamePassword } = ChromeUtils.import(
+ "chrome://browser/content/torpreferences/parseFunctions.jsm"
+);
+
+const TorProxyType = {
+ NONE: "NONE",
+ SOCKS4: "SOCKS4",
+ SOCKS5: "SOCKS5",
+ HTTPS: "HTTPS",
+};
+
+class TorProxySettings {
+ constructor() {
+ this._proxyType = TorProxyType.NONE;
+ this._proxyAddress = undefined;
+ this._proxyPort = undefined;
+ this._proxyUsername = undefined;
+ this._proxyPassword = undefined;
+ }
+
+ get type() {
+ return this._proxyType;
+ }
+ get address() {
+ return this._proxyAddress;
+ }
+ get port() {
+ return this._proxyPort;
+ }
+ get username() {
+ return this._proxyUsername;
+ }
+ get password() {
+ return this._proxyPassword;
+ }
+ get proxyURI() {
+ switch (this._proxyType) {
+ case TorProxyType.SOCKS4:
+ return `socks4a://${this._proxyAddress}:${this._proxyPort}`;
+ case TorProxyType.SOCKS5:
+ if (this._proxyUsername) {
+ return `socks5://${this._proxyUsername}:${this._proxyPassword}@${
+ this._proxyAddress
+ }:${this._proxyPort}`;
+ }
+ return `socks5://${this._proxyAddress}:${this._proxyPort}`;
+ case TorProxyType.HTTPS:
+ if (this._proxyUsername) {
+ return `http://${this._proxyUsername}:${this._proxyPassword}@${
+ this._proxyAddress
+ }:${this._proxyPort}`;
+ }
+ return `http://${this._proxyAddress}:${this._proxyPort}`;
+ }
+ return undefined;
+ }
+
+ // attempts to read proxy settings from Tor daemon
+ readSettings() {
+ // SOCKS4
+ {
+ let addressPort = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks4Proxy
+ );
+ if (addressPort) {
+ // address+port
+ let [proxyAddress, proxyPort] = parseAddrPort(addressPort);
+
+ this._proxyType = TorProxyType.SOCKS4;
+ this._proxyAddress = proxyAddress;
+ this._proxyPort = proxyPort;
+ this._proxyUsername = "";
+ this._proxyPassword = "";
+
+ return;
+ }
+ }
+
+ // SOCKS5
+ {
+ let addressPort = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks5Proxy
+ );
+
+ if (addressPort) {
+ // address+port
+ let [proxyAddress, proxyPort] = parseAddrPort(addressPort);
+ // username
+ let proxyUsername = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks5ProxyUsername
+ );
+ // password
+ let proxyPassword = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.socks5ProxyPassword
+ );
+
+ this._proxyType = TorProxyType.SOCKS5;
+ this._proxyAddress = proxyAddress;
+ this._proxyPort = proxyPort;
+ this._proxyUsername = proxyUsername;
+ this._proxyPassword = proxyPassword;
+
+ return;
+ }
+ }
+
+ // HTTP
+ {
+ let addressPort = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.httpsProxy
+ );
+
+ if (addressPort) {
+ // address+port
+ let [proxyAddress, proxyPort] = parseAddrPort(addressPort);
+
+ // username:password
+ let proxyAuthenticator = TorProtocolService.readStringSetting(
+ TorStrings.configKeys.httpsProxyAuthenticator
+ );
+
+ let [proxyUsername, proxyPassword] = ["", ""];
+ if (proxyAuthenticator) {
+ [proxyUsername, proxyPassword] = parseUsernamePassword(
+ proxyAuthenticator
+ );
+ }
+
+ this._proxyType = TorProxyType.HTTPS;
+ this._proxyAddress = proxyAddress;
+ this._proxyPort = proxyPort;
+ this._proxyUsername = proxyUsername;
+ this._proxyPassword = proxyPassword;
+ }
+ }
+ // no proxy settings
+ } /* TorProxySettings::ReadFromTor() */
+
+ // attempts to write proxy settings to Tor daemon
+ // throws on error
+ writeSettings() {
+ let settingsObject = new Map();
+
+ // init proxy related settings to null so Tor daemon resets them
+ settingsObject.set(TorStrings.configKeys.socks4Proxy, null);
+ settingsObject.set(TorStrings.configKeys.socks5Proxy, null);
+ settingsObject.set(TorStrings.configKeys.socks5ProxyUsername, null);
+ settingsObject.set(TorStrings.configKeys.socks5ProxyPassword, null);
+ settingsObject.set(TorStrings.configKeys.httpsProxy, null);
+ settingsObject.set(TorStrings.configKeys.httpsProxyAuthenticator, null);
+
+ switch (this._proxyType) {
+ case TorProxyType.SOCKS4:
+ settingsObject.set(
+ TorStrings.configKeys.socks4Proxy,
+ `${this._proxyAddress}:${this._proxyPort}`
+ );
+ break;
+ case TorProxyType.SOCKS5:
+ settingsObject.set(
+ TorStrings.configKeys.socks5Proxy,
+ `${this._proxyAddress}:${this._proxyPort}`
+ );
+ settingsObject.set(
+ TorStrings.configKeys.socks5ProxyUsername,
+ this._proxyUsername
+ );
+ settingsObject.set(
+ TorStrings.configKeys.socks5ProxyPassword,
+ this._proxyPassword
+ );
+ break;
+ case TorProxyType.HTTPS:
+ settingsObject.set(
+ TorStrings.configKeys.httpsProxy,
+ `${this._proxyAddress}:${this._proxyPort}`
+ );
+ settingsObject.set(
+ TorStrings.configKeys.httpsProxyAuthenticator,
+ `${this._proxyUsername}:${this._proxyPassword}`
+ );
+ break;
+ }
+
+ TorProtocolService.writeSettings(settingsObject);
+ } /* TorProxySettings::WriteToTor() */
+}
+
+// factory methods for our various supported proxies
+function makeTorProxySettingsNone() {
+ return new TorProxySettings();
+}
+
+function makeTorProxySettingsSocks4(aProxyAddress, aProxyPort) {
+ let retval = new TorProxySettings();
+ retval._proxyType = TorProxyType.SOCKS4;
+ retval._proxyAddress = aProxyAddress;
+ retval._proxyPort = aProxyPort;
+ return retval;
+}
+
+function makeTorProxySettingsSocks5(
+ aProxyAddress,
+ aProxyPort,
+ aProxyUsername,
+ aProxyPassword
+) {
+ let retval = new TorProxySettings();
+ retval._proxyType = TorProxyType.SOCKS5;
+ retval._proxyAddress = aProxyAddress;
+ retval._proxyPort = aProxyPort;
+ retval._proxyUsername = aProxyUsername;
+ retval._proxyPassword = aProxyPassword;
+ return retval;
+}
+
+function makeTorProxySettingsHTTPS(
+ aProxyAddress,
+ aProxyPort,
+ aProxyUsername,
+ aProxyPassword
+) {
+ let retval = new TorProxySettings();
+ retval._proxyType = TorProxyType.HTTPS;
+ retval._proxyAddress = aProxyAddress;
+ retval._proxyPort = aProxyPort;
+ retval._proxyUsername = aProxyUsername;
+ retval._proxyPassword = aProxyPassword;
+ return retval;
+}
diff --git a/browser/components/torpreferences/jar.mn b/browser/components/torpreferences/jar.mn
new file mode 100644
index 000000000000..857bc9ee3eac
--- /dev/null
+++ b/browser/components/torpreferences/jar.mn
@@ -0,0 +1,14 @@
+browser.jar:
+ content/browser/torpreferences/parseFunctions.jsm (content/parseFunctions.jsm)
+ content/browser/torpreferences/requestBridgeDialog.xhtml (content/requestBridgeDialog.xhtml)
+ content/browser/torpreferences/requestBridgeDialog.jsm (content/requestBridgeDialog.jsm)
+ content/browser/torpreferences/torBridgeSettings.jsm (content/torBridgeSettings.jsm)
+ content/browser/torpreferences/torCategory.inc.xhtml (content/torCategory.inc.xhtml)
+ content/browser/torpreferences/torFirewallSettings.jsm (content/torFirewallSettings.jsm)
+ content/browser/torpreferences/torLogDialog.jsm (content/torLogDialog.jsm)
+ content/browser/torpreferences/torLogDialog.xhtml (content/torLogDialog.xhtml)
+ content/browser/torpreferences/torPane.js (content/torPane.js)
+ content/browser/torpreferences/torPane.xhtml (content/torPane.xhtml)
+ content/browser/torpreferences/torPreferences.css (content/torPreferences.css)
+ content/browser/torpreferences/torPreferencesIcon.svg (content/torPreferencesIcon.svg)
+ content/browser/torpreferences/torProxySettings.jsm (content/torProxySettings.jsm)
diff --git a/browser/components/torpreferences/moz.build b/browser/components/torpreferences/moz.build
new file mode 100644
index 000000000000..2661ad7cb9f3
--- /dev/null
+++ b/browser/components/torpreferences/moz.build
@@ -0,0 +1 @@
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/browser/modules/BridgeDB.jsm b/browser/modules/BridgeDB.jsm
new file mode 100644
index 000000000000..2caa26b4e2e0
--- /dev/null
+++ b/browser/modules/BridgeDB.jsm
@@ -0,0 +1,110 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["BridgeDB"];
+
+const { TorLauncherBridgeDB } = ChromeUtils.import(
+ "resource://torlauncher/modules/tl-bridgedb.jsm"
+);
+const { TorProtocolService } = ChromeUtils.import(
+ "resource:///modules/TorProtocolService.jsm"
+);
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+var BridgeDB = {
+ _moatRequestor: null,
+ _currentCaptchaInfo: null,
+ _bridges: null,
+
+ get currentCaptchaImage() {
+ if (this._currentCaptchaInfo) {
+ return this._currentCaptchaInfo.captchaImage;
+ }
+ return null;
+ },
+
+ get currentBridges() {
+ return this._bridges;
+ },
+
+ submitCaptchaGuess(aCaptchaSolution) {
+ if (this._moatRequestor && this._currentCaptchaInfo) {
+ return this._moatRequestor
+ .finishFetch(
+ this._currentCaptchaInfo.transport,
+ this._currentCaptchaInfo.challenge,
+ aCaptchaSolution
+ )
+ .then(aBridgeInfo => {
+ this._moatRequestor.close();
+ this._moatRequestor = null;
+ this._currentCaptchaInfo = null;
+ this._bridges = aBridgeInfo.bridges;
+ // array of bridge strings
+ return this._bridges;
+ });
+ }
+
+ return new Promise((aResponse, aReject) => {
+ aReject(new Error("Invalid _moatRequestor or _currentCaptchaInfo"));
+ });
+ },
+
+ requestNewCaptchaImage(aProxyURI) {
+ // close and clear out existing state on captcha request
+ this.close();
+
+ let transportPlugins = TorProtocolService.readStringArraySetting(
+ TorStrings.configKeys.clientTransportPlugin
+ );
+
+ let meekClientPath;
+ let meekTransport; // We support both "meek" and "meek_lite".
+ let meekClientArgs;
+ // TODO: shouldn't this early out once meek settings are found?
+ for (const line of transportPlugins) {
+ // Parse each ClientTransportPlugin line and look for the meek or
+ // meek_lite transport. This code works a lot like the Tor daemon's
+ // parse_transport_line() function.
+ let tokens = line.split(" ");
+ if (tokens.length > 2 && tokens[1] == "exec") {
+ let transportArray = tokens[0].split(",").map(aStr => aStr.trim());
+ let transport = transportArray.find(
+ aTransport => aTransport === "meek"
+ );
+ if (!transport) {
+ transport = transportArray.find(
+ aTransport => aTransport === "meek_lite"
+ );
+ }
+ if (transport) {
+ meekTransport = transport;
+ meekClientPath = tokens[2];
+ meekClientArgs = tokens.slice(3);
+ }
+ }
+ }
+
+ this._moatRequestor = TorLauncherBridgeDB.createMoatRequestor();
+
+ return this._moatRequestor
+ .init(aProxyURI, meekTransport, meekClientPath, meekClientArgs)
+ .then(() => {
+ // TODO: get this from TorLauncherUtil
+ let bridgeType = "obfs4";
+ return this._moatRequestor.fetchBridges([bridgeType]);
+ })
+ .then(aCaptchaInfo => {
+ // cache off the current captcha info as the challenge is needed for response
+ this._currentCaptchaInfo = aCaptchaInfo;
+ return aCaptchaInfo.captchaImage;
+ });
+ },
+
+ close() {
+ if (this._moatRequestor) {
+ this._moatRequestor.close();
+ this._moatRequestor = null;
+ }
+ this._currentCaptchaInfo = null;
+ },
+};
diff --git a/browser/modules/TorProtocolService.jsm b/browser/modules/TorProtocolService.jsm
new file mode 100644
index 000000000000..b4e6ed9a3253
--- /dev/null
+++ b/browser/modules/TorProtocolService.jsm
@@ -0,0 +1,212 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorProtocolService"];
+
+const { TorLauncherUtil } = ChromeUtils.import(
+ "resource://torlauncher/modules/tl-util.jsm"
+);
+
+var TorProtocolService = {
+ _tlps: Cc["@torproject.org/torlauncher-protocol-service;1"].getService(
+ Ci.nsISupports
+ ).wrappedJSObject,
+
+ // maintain a map of tor settings set by Tor Browser so that we don't
+ // repeatedly set the same key/values over and over
+ // this map contains string keys to primitive or array values
+ _settingsCache: new Map(),
+
+ _typeof(aValue) {
+ switch (typeof aValue) {
+ case "boolean":
+ return "boolean";
+ case "string":
+ return "string";
+ case "object":
+ if (aValue == null) {
+ return "null";
+ } else if (Array.isArray(aValue)) {
+ return "array";
+ }
+ return "object";
+ }
+ return "unknown";
+ },
+
+ _assertValidSettingKey(aSetting) {
+ // ensure the 'key' is a string
+ if (typeof aSetting != "string") {
+ throw new Error(
+ `Expected setting of type string but received ${typeof aSetting}`
+ );
+ }
+ },
+
+ _assertValidSetting(aSetting, aValue) {
+ this._assertValidSettingKey(aSetting);
+
+ const valueType = this._typeof(aValue);
+ switch (valueType) {
+ case "boolean":
+ case "string":
+ case "null":
+ return;
+ case "array":
+ for (const element of aValue) {
+ if (typeof element != "string") {
+ throw new Error(
+ `Setting '${aSetting}' array contains value of invalid type '${typeof element}'`
+ );
+ }
+ }
+ return;
+ default:
+ throw new Error(
+ `Invalid object type received for setting '${aSetting}'`
+ );
+ }
+ },
+
+ // takes a Map containing tor settings
+ // throws on error
+ writeSettings(aSettingsObj) {
+ // only write settings that have changed
+ let newSettings = new Map();
+ for (const [setting, value] of aSettingsObj) {
+ let saveSetting = false;
+
+ // make sure we have valid data here
+ this._assertValidSetting(setting, value);
+
+ if (!this._settingsCache.has(setting)) {
+ // no cached setting, so write
+ saveSetting = true;
+ } else {
+ const cachedValue = this._settingsCache.get(setting);
+ if (value != cachedValue) {
+ // compare arrays member-wise
+ if (Array.isArray(value) && Array.isArray(cachedValue)) {
+ if (value.length != cachedValue.length) {
+ saveSetting = true;
+ } else {
+ const arrayLength = value.length;
+ for (let i = 0; i < arrayLength; ++i) {
+ if (value[i] != cachedValue[i]) {
+ saveSetting = true;
+ break;
+ }
+ }
+ }
+ } else {
+ // some other different values
+ saveSetting = true;
+ }
+ }
+ }
+
+ if (saveSetting) {
+ newSettings.set(setting, value);
+ }
+ }
+
+ // only write if new setting to save
+ if (newSettings.size > 0) {
+ // convert settingsObject map to js object for torlauncher-protocol-service
+ let settingsObject = {};
+ for (const [setting, value] of newSettings) {
+ settingsObject[setting] = value;
+ }
+
+ let errorObject = {};
+ if (!this._tlps.TorSetConfWithReply(settingsObject, errorObject)) {
+ throw new Error(errorObject.details);
+ }
+
+ // save settings to cache after successfully writing to Tor
+ for (const [setting, value] of newSettings) {
+ this._settingsCache.set(setting, value);
+ }
+ }
+ },
+
+ _readSetting(aSetting) {
+ this._assertValidSettingKey(aSetting);
+ let reply = this._tlps.TorGetConf(aSetting);
+ if (this._tlps.TorCommandSucceeded(reply)) {
+ return reply.lineArray;
+ }
+ throw new Error(reply.lineArray.join("\n"));
+ },
+
+ _readBoolSetting(aSetting) {
+ let lineArray = this._readSetting(aSetting);
+ if (lineArray.length != 1) {
+ throw new Error(
+ `Expected an array with length 1 but received array of length ${
+ lineArray.length
+ }`
+ );
+ }
+
+ let retval = lineArray[0];
+ switch (retval) {
+ case "0":
+ return false;
+ case "1":
+ return true;
+ default:
+ throw new Error(`Expected boolean (1 or 0) but received '${retval}'`);
+ }
+ },
+
+ _readStringSetting(aSetting) {
+ let lineArray = this._readSetting(aSetting);
+ if (lineArray.length != 1) {
+ throw new Error(
+ `Expected an array with length 1 but received array of length ${
+ lineArray.length
+ }`
+ );
+ }
+ return lineArray[0];
+ },
+
+ _readStringArraySetting(aSetting) {
+ let lineArray = this._readSetting(aSetting);
+ return lineArray;
+ },
+
+ readBoolSetting(aSetting) {
+ let value = this._readBoolSetting(aSetting);
+ this._settingsCache.set(aSetting, value);
+ return value;
+ },
+
+ readStringSetting(aSetting) {
+ let value = this._readStringSetting(aSetting);
+ this._settingsCache.set(aSetting, value);
+ return value;
+ },
+
+ readStringArraySetting(aSetting) {
+ let value = this._readStringArraySetting(aSetting);
+ this._settingsCache.set(aSetting, value);
+ return value;
+ },
+
+ // writes current tor settings to disk
+ flushSettings() {
+ this._tlps.TorSendCommand("SAVECONF");
+ },
+
+ getLog() {
+ let countObj = { value: 0 };
+ let torLog = this._tlps.TorGetLog(countObj);
+ return torLog;
+ },
+
+ // true if we launched and control tor, false if using system tor
+ get ownsTorDaemon() {
+ return TorLauncherUtil.shouldStartAndOwnTor;
+ },
+};
diff --git a/browser/modules/moz.build b/browser/modules/moz.build
index 6071d9cb5c8a..ffaaba2d0de2 100644
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -122,6 +122,7 @@ EXTRA_JS_MODULES += [
"AboutNewTab.jsm",
"AppUpdater.jsm",
"AsyncTabSwitcher.jsm",
+ "BridgeDB.jsm",
"BrowserUIUtils.jsm",
"BrowserUsageTelemetry.jsm",
"BrowserWindowTracker.jsm",
@@ -145,6 +146,7 @@ EXTRA_JS_MODULES += [
"SitePermissions.jsm",
"TabsList.jsm",
"TabUnloader.jsm",
+ "TorProtocolService.jsm",
"TorStrings.jsm",
"TransientPrefs.jsm",
"webrtcUI.jsm",
1
0
[tor-browser/geckoview-96.0-11.5-1] Bug 27604: Fix addon issues when moving TB directory
by sysrqb@torproject.org 17 Feb '22
by sysrqb@torproject.org 17 Feb '22
17 Feb '22
commit 409d72598deed01175f269e0e64d141063e679ec
Author: Alex Catarineu <acat(a)torproject.org>
Date: Wed Oct 30 10:44:48 2019 +0100
Bug 27604: Fix addon issues when moving TB directory
---
toolkit/mozapps/extensions/internal/XPIProvider.jsm | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
index 1722939f69aa..553a974aa858 100644
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -475,7 +475,7 @@ class XPIState {
// Builds prior to be 1512436 did not include the rootURI property.
// If we're updating from such a build, add that property now.
- if (!("rootURI" in this) && this.file) {
+ if (this.file) {
this.rootURI = getURIForResourceInFile(this.file, "").spec;
}
@@ -488,7 +488,10 @@ class XPIState {
saved.currentModifiedTime != this.lastModifiedTime
) {
this.lastModifiedTime = saved.currentModifiedTime;
- } else if (saved.currentModifiedTime === null) {
+ } else if (
+ saved.currentModifiedTime === null &&
+ (!this.file || !this.file.exists())
+ ) {
this.missing = true;
}
}
@@ -1449,6 +1452,7 @@ var XPIStates = {
if (shouldRestoreLocationData && oldState[loc.name]) {
loc.restore(oldState[loc.name]);
+ changed = changed || loc.path != oldState[loc.name].path;
}
changed = changed || loc.changed;
1
0
[tor-browser/tor-browser-91.6.0esr-11.5-1] Bug 40807: Added QRCode.js to toolkit/modules
by richard@torproject.org 17 Feb '22
by richard@torproject.org 17 Feb '22
17 Feb '22
commit 05456834494c8f75ce28f95e0591330d168f2e86
Author: Pier Angelo Vendrame <pierov(a)torproject.org>
Date: Thu Feb 17 12:17:25 2022 +0100
Bug 40807: Added QRCode.js to toolkit/modules
---
toolkit/content/license.html | 33 +++
toolkit/modules/QRCode.jsm | 619 +++++++++++++++++++++++++++++++++++++++++++
toolkit/modules/moz.build | 1 +
3 files changed, 653 insertions(+)
diff --git a/toolkit/content/license.html b/toolkit/content/license.html
index 782e874edf2a..8860ee3cec8a 100644
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -155,6 +155,7 @@
<li><a href="about:license#prop-types">prop-types License</a></li>
<li><a href="about:license#qcms">qcms License</a></li>
<li><a href="about:license#qrcode-generator">QR Code Generator License</a></li>
+ <li><a href="about:license#qrcode-js">QRCode.js</a></li>
<li><a href="about:license#raven-js">Raven.js License</a></li>
<li><a href="about:license#react">React License</a></li>
<li><a href="about:license#react-mit">React MIT License</a></li>
@@ -5136,6 +5137,38 @@ furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</pre>
+
+ <hr>
+
+ <h1><a id="qrcode-generator"></a>QRCode.js</h1>
+
+ <p>This license applies to the file
+ <code>toolkit/modules/QRCode.jsm</code>.</p>
+<pre>
+The MIT License (MIT)
+---------------------
+Copyright (c) 2009 Kazuhiko Arase
+Copyright (c) 2012 davidshimjs
+Copyright (c) 2018 ivan386
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
diff --git a/toolkit/modules/QRCode.jsm b/toolkit/modules/QRCode.jsm
new file mode 100644
index 000000000000..b8101301ceed
--- /dev/null
+++ b/toolkit/modules/QRCode.jsm
@@ -0,0 +1,619 @@
+/**
+ * @fileoverview
+ * - Using the 'QRCode for Javascript library'
+ * - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
+ * - this library has no dependencies.
+ *
+ * @author davidshimjs
+ * @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
+ * @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
+ */
+var QRCode;
+
+(function () {
+ //---------------------------------------------------------------------
+ // QRCode for JavaScript
+ //
+ // Copyright (c) 2009 Kazuhiko Arase
+ //
+ // URL: http://www.d-project.com/
+ //
+ // Licensed under the MIT license:
+ // http://www.opensource.org/licenses/mit-license.php
+ //
+ // The word "QR Code" is registered trademark of
+ // DENSO WAVE INCORPORATED
+ // http://www.denso-wave.com/qrcode/faqpatent-e.html
+ //
+ //---------------------------------------------------------------------
+ function QR8bitByte(data) {
+ this.mode = QRMode.MODE_8BIT_BYTE;
+ this.data = data;
+ this.parsedData = [];
+
+ // Added to support UTF-8 Characters
+ for (var i = 0, l = this.data.length; i < l; i++) {
+ var byteArray = [];
+ var code = this.data.charCodeAt(i);
+
+ if (code > 0x10000) {
+ byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
+ byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
+ byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
+ byteArray[3] = 0x80 | (code & 0x3F);
+ } else if (code > 0x800) {
+ byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
+ byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
+ byteArray[2] = 0x80 | (code & 0x3F);
+ } else if (code > 0x80) {
+ byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
+ byteArray[1] = 0x80 | (code & 0x3F);
+ } else {
+ byteArray[0] = code;
+ }
+
+ this.parsedData.push(byteArray);
+ }
+
+ this.parsedData = Array.prototype.concat.apply([], this.parsedData);
+
+ if (this.parsedData.length != this.data.length) {
+ this.parsedData.unshift(191);
+ this.parsedData.unshift(187);
+ this.parsedData.unshift(239);
+ }
+ }
+
+ QR8bitByte.prototype = {
+ getLength: function (buffer) {
+ return this.parsedData.length;
+ },
+ write: function (buffer) {
+ for (var i = 0, l = this.parsedData.length; i < l; i++) {
+ buffer.put(this.parsedData[i], 8);
+ }
+ }
+ };
+
+ function QRCodeModel(typeNumber, errorCorrectLevel) {
+ this.typeNumber = typeNumber;
+ this.errorCorrectLevel = errorCorrectLevel;
+ this.modules = null;
+ this.moduleCount = 0;
+ this.dataCache = null;
+ this.dataList = [];
+ }
+
+ QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);}
+ return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row<this.moduleCount;row++){this.modules[row]=new Array(this.moduleCount);for(var col=0;col<this.moduleCount;col++){this.modules[row][col]=null;}}
+ this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(test,maskPattern);if(this.typeNumber>=7){this.setupTypeNumber(test);}
+ if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);}
+ this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}}
+ return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row<this.modules.length;row++){var y=row*cs;for(var col=0;col<this.modules[row].length;col++){var x=col*cs;var dark=this.modules[row][col];if(dark){qr_mc.beginFill(0,100);qr_mc.moveTo(x,y);qr_mc.lineTo(x+cs,y);qr_mc.lineTo(x+cs,y+cs);qr_mc.lineTo(x,y+cs);qr_mc.endFill();}}}
+ return qr_mc;},setupTimingPattern:function(){for(var r=8;r<this.moduleCount-8;r++){if(this.modules[r][6]!=null){continue;}
+ this.modules[r][6]=(r%2==0);}
+ for(var c=8;c<this.moduleCount-8;c++){if(this.modules[6][c]!=null){continue;}
+ this.modules[6][c]=(c%2==0);}},setupPositionAdjustPattern:function(){var pos=QRUtil.getPatternPosition(this.typeNumber);for(var i=0;i<pos.length;i++){for(var j=0;j<pos.length;j++){var row=pos[i];var col=pos[j];if(this.modules[row][col]!=null){continue;}
+ for(var r=-2;r<=2;r++){for(var c=-2;c<=2;c++){if(r==-2||r==2||c==-2||c==2||(r==0&&c==0)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}}}},setupTypeNumber:function(test){var bits=QRUtil.getBCHTypeNumber(this.typeNumber);for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;}
+ for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}}
+ for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}}
+ this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex<data.length){dark=(((data[byteIndex]>>>bitIndex)&1)==1);}
+ var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;}
+ this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}}
+ row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;i<dataList.length;i++){var data=dataList[i];buffer.put(data.mode,4);buffer.put(data.getLength(),QRUtil.getLengthInBits(data.mode,typeNumber));data.write(buffer);}
+ var totalDataCount=0;for(var i=0;i<rsBlocks.length;i++){totalDataCount+=rsBlocks[i].dataCount;}
+ if(buffer.getLengthInBits()>totalDataCount*8){throw new Error("code length overflow. ("
+ +buffer.getLengthInBits()
+ +">"
+ +totalDataCount*8
+ +")");}
+ if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);}
+ while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);}
+ while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;}
+ buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;}
+ buffer.put(QRCodeModel.PAD1,8);}
+ return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r<rsBlocks.length;r++){var dcCount=rsBlocks[r].dataCount;var ecCount=rsBlocks[r].totalCount-dcCount;maxDcCount=Math.max(maxDcCount,dcCount);maxEcCount=Math.max(maxEcCount,ecCount);dcdata[r]=new Array(dcCount);for(var i=0;i<dcdata[r].length;i++){dcdata[r][i]=0xff&buffer.buffer[i+offset];}
+ offset+=dcCount;var rsPoly=QRUtil.getErrorCorrectPolynomial(ecCount);var rawPoly=new QRPolynomial(dcdata[r],rsPoly.getLength()-1);var modPoly=rawPoly.mod(rsPoly);ecdata[r]=new Array(rsPoly.getLength()-1);for(var i=0;i<ecdata[r].length;i++){var modIndex=i+modPoly.getLength()-ecdata[r].length;ecdata[r][i]=(modIndex>=0)?modPoly.get(modIndex):0;}}
+ var totalCodeCount=0;for(var i=0;i<rsBlocks.length;i++){totalCodeCount+=rsBlocks[i].totalCount;}
+ var data=new Array(totalCodeCount);var index=0;for(var i=0;i<maxDcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<dcdata[r].length){data[index++]=dcdata[r][i];}}}
+ for(var i=0;i<maxEcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<ecdata[r].length){data[index++]=ecdata[r][i];}}}
+ return data;};var QRMode={MODE_NUMBER:1<<0,MODE_ALPHA_NUM:1<<1,MODE_8BIT_BYTE:1<<2,MODE_KANJI:1<<3};var QRErrorCorrectLevel={L:1,M:0,Q:3,H:2};var QRMaskPattern={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var QRUtil={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:(1<<10)|(1<<8)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0),G18:(1<<12)|(1<<11)|(1<<10)
|(1<<9)|(1<<8)|(1<<5)|(1<<2)|(1<<0),G15_MASK:(1<<14)|(1<<12)|(1<<10)|(1<<4)|(1<<1),getBCHTypeInfo:function(data){var d=data<<10;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)>=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));}
+ return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));}
+ return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;}
+ return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i<errorCorrectLength;i++){a=a.multiply(new QRPolynomial([1,QRMath.gexp(i)],0));}
+ return a;},getLengthInBits:function(mode,type){if(1<=type&&type<10){switch(mode){case QRMode.MODE_NUMBER:return 10;case QRMode.MODE_ALPHA_NUM:return 9;case QRMode.MODE_8BIT_BYTE:return 8;case QRMode.MODE_KANJI:return 8;default:throw new Error("mode:"+mode);}}else if(type<27){switch(mode){case QRMode.MODE_NUMBER:return 12;case QRMode.MODE_ALPHA_NUM:return 11;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 10;default:throw new Error("mode:"+mode);}}else if(type<41){switch(mode){case QRMode.MODE_NUMBER:return 14;case QRMode.MODE_ALPHA_NUM:return 13;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 12;default:throw new Error("mode:"+mode);}}else{throw new Error("type:"+type);}},getLostPoint:function(qrCode){var moduleCount=qrCode.getModuleCount();var lostPoint=0;for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount;col++){var sameCount=0;var dark=qrCode.isDark(row,col);for(var r=-1;r<=1;r++){if(row+r<0||moduleCount<=row+r){continue;}
+ for(var c=-1;c<=1;c++){if(col+c<0||moduleCount<=col+c){continue;}
+ if(r==0&&c==0){continue;}
+ if(dark==qrCode.isDark(row+r,col+c)){sameCount++;}}}
+ if(sameCount>5){lostPoint+=(3+sameCount-5);}}}
+ for(var row=0;row<moduleCount-1;row++){for(var col=0;col<moduleCount-1;col++){var count=0;if(qrCode.isDark(row,col))count++;if(qrCode.isDark(row+1,col))count++;if(qrCode.isDark(row,col+1))count++;if(qrCode.isDark(row+1,col+1))count++;if(count==0||count==4){lostPoint+=3;}}}
+ for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount-6;col++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row,col+1)&&qrCode.isDark(row,col+2)&&qrCode.isDark(row,col+3)&&qrCode.isDark(row,col+4)&&!qrCode.isDark(row,col+5)&&qrCode.isDark(row,col+6)){lostPoint+=40;}}}
+ for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount-6;row++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row+1,col)&&qrCode.isDark(row+2,col)&&qrCode.isDark(row+3,col)&&qrCode.isDark(row+4,col)&&!qrCode.isDark(row+5,col)&&qrCode.isDark(row+6,col)){lostPoint+=40;}}}
+ var darkCount=0;for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount;row++){if(qrCode.isDark(row,col)){darkCount++;}}}
+ var ratio=Math.abs(100*darkCount/moduleCount/moduleCount-50)/5;lostPoint+=ratio*10;return lostPoint;}};var QRMath={glog:function(n){if(n<1){throw new Error("glog("+n+")");}
+ return QRMath.LOG_TABLE[n];},gexp:function(n){while(n<0){n+=255;}
+ while(n>=256){n-=255;}
+ return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<<i;}
+ for(var i=8;i<256;i++){QRMath.EXP_TABLE[i]=QRMath.EXP_TABLE[i-4]^QRMath.EXP_TABLE[i-5]^QRMath.EXP_TABLE[i-6]^QRMath.EXP_TABLE[i-8];}
+ for(var i=0;i<255;i++){QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]]=i;}
+ function QRPolynomial(num,shift){if(num.length==undefined){throw new Error(num.length+"/"+shift);}
+ var offset=0;while(offset<num.length&&num[offset]==0){offset++;}
+ this.num=new Array(num.length-offset+shift);for(var i=0;i<num.length-offset;i++){this.num[i]=num[i+offset];}}
+ QRPolynomial.prototype={get:function(index){return this.num[index];},getLength:function(){return this.num.length;},multiply:function(e){var num=new Array(this.getLength()+e.getLength()-1);for(var i=0;i<this.getLength();i++){for(var j=0;j<e.getLength();j++){num[i+j]^=QRMath.gexp(QRMath.glog(this.get(i))+QRMath.glog(e.get(j)));}}
+ return new QRPolynomial(num,0);},mod:function(e){if(this.getLength()-e.getLength()<0){return this;}
+ var ratio=QRMath.glog(this.get(0))-QRMath.glog(e.get(0));var num=new Array(this.getLength());for(var i=0;i<this.getLength();i++){num[i]=this.get(i);}
+ for(var i=0;i<e.getLength();i++){num[i]^=QRMath.gexp(QRMath.glog(e.get(i))+ratio);}
+ return new QRPolynomial(num,0).mod(e);}};function QRRSBlock(totalCount,dataCount){this.totalCount=totalCount;this.dataCount=dataCount;}
+ QRRSBlock.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,7
4,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,1
0,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];QRRSBlock.getRSBlocks=function(typeNumber,errorCorrectLevel){var rsBlock=QRRSBlock.getRsBlockTable(typeNumber,er
rorCorrectLevel);if(rsBlock==undefined){throw new Error("bad rs block @ typeNumber:"+typeNumber+"/errorCorrectLevel:"+errorCorrectLevel);}
+ var length=rsBlock.length/3;var list=[];for(var i=0;i<length;i++){var count=rsBlock[i*3+0];var totalCount=rsBlock[i*3+1];var dataCount=rsBlock[i*3+2];for(var j=0;j<count;j++){list.push(new QRRSBlock(totalCount,dataCount));}}
+ return list;};QRRSBlock.getRsBlockTable=function(typeNumber,errorCorrectLevel){switch(errorCorrectLevel){case QRErrorCorrectLevel.L:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+0];case QRErrorCorrectLevel.M:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+1];case QRErrorCorrectLevel.Q:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+2];case QRErrorCorrectLevel.H:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+3];default:return undefined;}};function QRBitBuffer(){this.buffer=[];this.length=0;}
+ QRBitBuffer.prototype={get:function(index){var bufIndex=Math.floor(index/8);return((this.buffer[bufIndex]>>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i<length;i++){this.putBit(((num>>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);}
+ if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));}
+ this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]];
+
+ function _isSupportCanvas() {
+ return typeof CanvasRenderingContext2D != "undefined";
+ }
+
+ // android 2.x doesn't support Data-URI spec
+ function _getAndroid() {
+ var android = false;
+ var sAgent = navigator.userAgent;
+
+ if (/android/i.test(sAgent)) { // android
+ android = true;
+ var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i);
+
+ if (aMat && aMat[1]) {
+ android = parseFloat(aMat[1]);
+ }
+ }
+
+ return android;
+ }
+
+ var svgDrawer = (function() {
+
+ var Drawing = function (el, htOption) {
+ this._el = el;
+ this._htOption = htOption;
+ };
+
+ Drawing.prototype.draw = function (oQRCode) {
+ var _htOption = this._htOption;
+ var _el = this._el;
+ var nCount = oQRCode.getModuleCount();
+ var nWidth = Math.floor(_htOption.width / nCount);
+ var nHeight = Math.floor(_htOption.height / nCount);
+
+ this.clear();
+
+ function makeSVG(tag, attrs) {
+ var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
+ for (var k in attrs)
+ if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]);
+ return el;
+ }
+
+ var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight});
+ svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", "http://www.w3.org/2000/svg");
+ _el.appendChild(svg);
+
+ svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"}));
+
+ var path = []
+ for (var row = 0; row < nCount; row++) {
+ for (var col = 0; col < nCount; col++) {
+ var width = 0;
+ while(col+width < nCount && oQRCode.isDark(row, col+width))
+ width++;
+
+ if (width>0) {
+ path.push("M"+col+" "+row+"v1h"+width+"v-1z");
+ col+=width;
+ }
+ }
+ }
+ var child = makeSVG("path", {"d": path.join(""), "fill": _htOption.colorDark});
+ svg.appendChild(child);
+ };
+ Drawing.prototype.clear = function () {
+ while (this._el.hasChildNodes())
+ this._el.removeChild(this._el.lastChild);
+ };
+ return Drawing;
+ })();
+
+ var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
+
+ // Drawing in DOM by using Table tag
+ var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () {
+ var Drawing = function (el, htOption) {
+ this._el = el;
+ this._htOption = htOption;
+ };
+
+ /**
+ * Draw the QRCode
+ *
+ * @param {QRCode} oQRCode
+ */
+ Drawing.prototype.draw = function (oQRCode) {
+ var _htOption = this._htOption;
+ var _el = this._el;
+ var nCount = oQRCode.getModuleCount();
+ var nWidth = Math.floor(_htOption.width / nCount);
+ var nHeight = Math.floor(_htOption.height / nCount);
+ var aHTML = ['<table style="border:0;border-collapse:collapse;">'];
+
+ for (var row = 0; row < nCount; row++) {
+ aHTML.push('<tr>');
+
+ for (var col = 0; col < nCount; col++) {
+ aHTML.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';"></td>');
+ }
+
+ aHTML.push('</tr>');
+ }
+
+ aHTML.push('</table>');
+ _el.innerHTML = aHTML.join('');
+
+ // Fix the margin values as real size.
+ var elTable = _el.childNodes[0];
+ var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;
+ var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
+
+ if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
+ elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px";
+ }
+ };
+
+ /**
+ * Clear the QRCode
+ */
+ Drawing.prototype.clear = function () {
+ this._el.innerHTML = '';
+ };
+
+ return Drawing;
+ })() : (function () { // Drawing in Canvas
+ function _onMakeImage() {
+ this._elImage.src = this._elCanvas.toDataURL("image/png");
+ this._elImage.style.display = "block";
+ this._elCanvas.style.display = "none";
+ }
+
+ // Android 2.1 bug workaround
+ // http://code.google.com/p/android/issues/detail?id=5141
+ if (this._android && this._android <= 2.1) {
+ var factor = 1 / window.devicePixelRatio;
+ var drawImage = CanvasRenderingContext2D.prototype.drawImage;
+ CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
+ if (("nodeName" in image) && /img/i.test(image.nodeName)) {
+ for (var i = arguments.length - 1; i >= 1; i--) {
+ arguments[i] = arguments[i] * factor;
+ }
+ } else if (typeof dw == "undefined") {
+ arguments[1] *= factor;
+ arguments[2] *= factor;
+ arguments[3] *= factor;
+ arguments[4] *= factor;
+ }
+
+ drawImage.apply(this, arguments);
+ };
+ }
+
+ /**
+ * Check whether the user's browser supports Data URI or not
+ *
+ * @private
+ * @param {Function} fSuccess Occurs if it supports Data URI
+ * @param {Function} fFail Occurs if it doesn't support Data URI
+ */
+ function _safeSetDataURI(fSuccess, fFail) {
+ var self = this;
+ self._fFail = fFail;
+ self._fSuccess = fSuccess;
+
+ // Check it just once
+ if (self._bSupportDataURI === null) {
+ var el = document.createElement("img");
+ var fOnError = function() {
+ self._bSupportDataURI = false;
+
+ if (self._fFail) {
+ self._fFail.call(self);
+ }
+ };
+ var fOnSuccess = function() {
+ self._bSupportDataURI = true;
+
+ if (self._fSuccess) {
+ self._fSuccess.call(self);
+ }
+ };
+
+ el.onabort = fOnError;
+ el.onerror = fOnError;
+ el.onload = fOnSuccess;
+ el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data.
+ return;
+ } else if (self._bSupportDataURI === true && self._fSuccess) {
+ self._fSuccess.call(self);
+ } else if (self._bSupportDataURI === false && self._fFail) {
+ self._fFail.call(self);
+ }
+ };
+
+ /**
+ * Drawing QRCode by using canvas
+ *
+ * @constructor
+ * @param {HTMLElement} el
+ * @param {Object} htOption QRCode Options
+ */
+ var Drawing = function (el, htOption) {
+ this._bIsPainted = false;
+ this._android = _getAndroid();
+
+ this._htOption = htOption;
+ this._elCanvas = document.createElement("canvas");
+ this._elCanvas.width = htOption.width;
+ this._elCanvas.height = htOption.height;
+ el.appendChild(this._elCanvas);
+ this._el = el;
+ this._oContext = this._elCanvas.getContext("2d");
+ this._bIsPainted = false;
+ this._elImage = document.createElement("img");
+ this._elImage.alt = "Scan me!";
+ this._elImage.style.display = "none";
+ this._el.appendChild(this._elImage);
+ this._bSupportDataURI = null;
+ };
+
+ /**
+ * Draw the QRCode
+ *
+ * @param {QRCode} oQRCode
+ */
+ Drawing.prototype.draw = function (oQRCode) {
+ var _elImage = this._elImage;
+ var _oContext = this._oContext;
+ var _htOption = this._htOption;
+
+ var nCount = oQRCode.getModuleCount();
+ var nWidth = _htOption.width / nCount;
+ var nHeight = _htOption.height / nCount;
+ var nRoundedWidth = Math.round(nWidth);
+ var nRoundedHeight = Math.round(nHeight);
+
+ _elImage.style.display = "none";
+ this.clear();
+
+ for (var row = 0; row < nCount; row++) {
+ for (var col = 0; col < nCount; col++) {
+ var bIsDark = oQRCode.isDark(row, col);
+ var nLeft = col * nWidth;
+ var nTop = row * nHeight;
+ _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
+ _oContext.lineWidth = 1;
+ _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
+ _oContext.fillRect(nLeft, nTop, nWidth, nHeight);
+
+ // 안티 앨리어싱 방지 처리
+ _oContext.strokeRect(
+ Math.floor(nLeft) + 0.5,
+ Math.floor(nTop) + 0.5,
+ nRoundedWidth,
+ nRoundedHeight
+ );
+
+ _oContext.strokeRect(
+ Math.ceil(nLeft) - 0.5,
+ Math.ceil(nTop) - 0.5,
+ nRoundedWidth,
+ nRoundedHeight
+ );
+ }
+ }
+
+ this._bIsPainted = true;
+ };
+
+ /**
+ * Make the image from Canvas if the browser supports Data URI.
+ */
+ Drawing.prototype.makeImage = function () {
+ if (this._bIsPainted) {
+ _safeSetDataURI.call(this, _onMakeImage);
+ }
+ };
+
+ /**
+ * Return whether the QRCode is painted or not
+ *
+ * @return {Boolean}
+ */
+ Drawing.prototype.isPainted = function () {
+ return this._bIsPainted;
+ };
+
+ /**
+ * Clear the QRCode
+ */
+ Drawing.prototype.clear = function () {
+ this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);
+ this._bIsPainted = false;
+ };
+
+ /**
+ * @private
+ * @param {Number} nNumber
+ */
+ Drawing.prototype.round = function (nNumber) {
+ if (!nNumber) {
+ return nNumber;
+ }
+
+ return Math.floor(nNumber * 1000) / 1000;
+ };
+
+ return Drawing;
+ })();
+
+ /**
+ * Get the type by string length
+ *
+ * @private
+ * @param {String} sText
+ * @param {Number} nCorrectLevel
+ * @return {Number} type
+ */
+ function _getTypeNumber(sText, nCorrectLevel) {
+ var nType = 1;
+ var length = _getUTF8Length(sText);
+
+ for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
+ var nLimit = 0;
+
+ switch (nCorrectLevel) {
+ case QRErrorCorrectLevel.L :
+ nLimit = QRCodeLimitLength[i][0];
+ break;
+ case QRErrorCorrectLevel.M :
+ nLimit = QRCodeLimitLength[i][1];
+ break;
+ case QRErrorCorrectLevel.Q :
+ nLimit = QRCodeLimitLength[i][2];
+ break;
+ case QRErrorCorrectLevel.H :
+ nLimit = QRCodeLimitLength[i][3];
+ break;
+ }
+
+ if (length <= nLimit) {
+ break;
+ } else {
+ nType++;
+ }
+ }
+
+ if (nType > QRCodeLimitLength.length) {
+ throw new Error("Too long data");
+ }
+
+ return nType;
+ }
+
+ function _getUTF8Length(sText) {
+ var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
+ return replacedText.length + (replacedText.length != sText ? 3 : 0);
+ }
+
+ /**
+ * @class QRCode
+ * @constructor
+ * @example
+ * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
+ *
+ * @example
+ * var oQRCode = new QRCode("test", {
+ * text : "http://naver.com",
+ * width : 128,
+ * height : 128
+ * });
+ *
+ * oQRCode.clear(); // Clear the QRCode.
+ * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
+ *
+ * @param {HTMLElement|String} el target element or 'id' attribute of element.
+ * @param {Object|String} vOption
+ * @param {String} vOption.text QRCode link data
+ * @param {Number} [vOption.width=256]
+ * @param {Number} [vOption.height=256]
+ * @param {String} [vOption.colorDark="#000000"]
+ * @param {String} [vOption.colorLight="#ffffff"]
+ * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
+ */
+ QRCode = function (el, vOption) {
+ this._htOption = {
+ width : 256,
+ height : 256,
+ typeNumber : 4,
+ colorDark : "#000000",
+ colorLight : "#ffffff",
+ correctLevel : QRErrorCorrectLevel.H
+ };
+
+ if (typeof vOption === 'string') {
+ vOption = {
+ text : vOption
+ };
+ }
+
+ // Overwrites options
+ if (vOption) {
+ for (var i in vOption) {
+ this._htOption[i] = vOption[i];
+ }
+ }
+
+ if (typeof el == "string") {
+ el = document.getElementById(el);
+ }
+
+ if (this._htOption.useSVG) {
+ Drawing = svgDrawer;
+ }
+
+ this._android = _getAndroid();
+ this._el = el;
+ this._oQRCode = null;
+ this._oDrawing = new Drawing(this._el, this._htOption);
+
+ if (this._htOption.text) {
+ this.makeCode(this._htOption.text);
+ }
+ };
+
+ /**
+ * Make the QRCode
+ *
+ * @param {String} sText link data
+ */
+ QRCode.prototype.makeCode = function (sText) {
+ this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);
+ this._oQRCode.addData(sText);
+ this._oQRCode.make();
+ this._el.title = sText;
+ this._oDrawing.draw(this._oQRCode);
+ this.makeImage();
+ };
+
+ /**
+ * Make the Image from Canvas element
+ * - It occurs automatically
+ * - Android below 3 doesn't support Data-URI spec.
+ *
+ * @private
+ */
+ QRCode.prototype.makeImage = function () {
+ if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) {
+ this._oDrawing.makeImage();
+ }
+ };
+
+ /**
+ * Clear the QRCode
+ */
+ QRCode.prototype.clear = function () {
+ this._oDrawing.clear();
+ };
+
+ /**
+ * @name QRCode.CorrectLevel
+ */
+ QRCode.CorrectLevel = QRErrorCorrectLevel;
+})();
diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build
index a3bfdf83ffbd..650893ca99b1 100644
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -202,6 +202,7 @@ EXTRA_JS_MODULES += [
"Promise-backend.js",
"Promise.jsm",
"PromiseUtils.jsm",
+ "QRCode.jsm",
"Region.jsm",
"RemotePageAccessManager.jsm",
"ResetProfile.jsm",
1
0
[tor-browser/tor-browser-91.6.0esr-11.5-1] fixup! Bug 40807: Added QRCode.js to toolkit/modules
by richard@torproject.org 17 Feb '22
by richard@torproject.org 17 Feb '22
17 Feb '22
commit 09182e228f5a3c0cd963bdc0df3db68a2753bc97
Author: Pier Angelo Vendrame <pierov(a)torproject.org>
Date: Fri Feb 11 16:45:10 2022 +0100
fixup! Bug 40807: Added QRCode.js to toolkit/modules
---
toolkit/modules/QRCode.jsm | 1836 +++++++++++++++++++++++++++++---------------
1 file changed, 1229 insertions(+), 607 deletions(-)
diff --git a/toolkit/modules/QRCode.jsm b/toolkit/modules/QRCode.jsm
index b8101301ceed..a5970ed2b7b3 100644
--- a/toolkit/modules/QRCode.jsm
+++ b/toolkit/modules/QRCode.jsm
@@ -3,617 +3,1239 @@
* - Using the 'QRCode for Javascript library'
* - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
* - this library has no dependencies.
- *
- * @author davidshimjs
+ *
+ * Modified to be used as a module by the Tor Project
+ *
+ * @author Kazuhiko Arase, davidshimjs, ivan386
* @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
* @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
*/
+
+var EXPORTED_SYMBOLS = ["QRCode"];
+
var QRCode;
-(function () {
- //---------------------------------------------------------------------
- // QRCode for JavaScript
- //
- // Copyright (c) 2009 Kazuhiko Arase
- //
- // URL: http://www.d-project.com/
- //
- // Licensed under the MIT license:
- // http://www.opensource.org/licenses/mit-license.php
- //
- // The word "QR Code" is registered trademark of
- // DENSO WAVE INCORPORATED
- // http://www.denso-wave.com/qrcode/faqpatent-e.html
- //
- //---------------------------------------------------------------------
- function QR8bitByte(data) {
- this.mode = QRMode.MODE_8BIT_BYTE;
- this.data = data;
- this.parsedData = [];
-
- // Added to support UTF-8 Characters
- for (var i = 0, l = this.data.length; i < l; i++) {
- var byteArray = [];
- var code = this.data.charCodeAt(i);
-
- if (code > 0x10000) {
- byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
- byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
- byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
- byteArray[3] = 0x80 | (code & 0x3F);
- } else if (code > 0x800) {
- byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
- byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
- byteArray[2] = 0x80 | (code & 0x3F);
- } else if (code > 0x80) {
- byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
- byteArray[1] = 0x80 | (code & 0x3F);
- } else {
- byteArray[0] = code;
- }
-
- this.parsedData.push(byteArray);
- }
-
- this.parsedData = Array.prototype.concat.apply([], this.parsedData);
-
- if (this.parsedData.length != this.data.length) {
- this.parsedData.unshift(191);
- this.parsedData.unshift(187);
- this.parsedData.unshift(239);
- }
- }
-
- QR8bitByte.prototype = {
- getLength: function (buffer) {
- return this.parsedData.length;
- },
- write: function (buffer) {
- for (var i = 0, l = this.parsedData.length; i < l; i++) {
- buffer.put(this.parsedData[i], 8);
- }
- }
- };
-
- function QRCodeModel(typeNumber, errorCorrectLevel) {
- this.typeNumber = typeNumber;
- this.errorCorrectLevel = errorCorrectLevel;
- this.modules = null;
- this.moduleCount = 0;
- this.dataCache = null;
- this.dataList = [];
- }
-
- QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);}
- return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row<this.moduleCount;row++){this.modules[row]=new Array(this.moduleCount);for(var col=0;col<this.moduleCount;col++){this.modules[row][col]=null;}}
- this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(test,maskPattern);if(this.typeNumber>=7){this.setupTypeNumber(test);}
- if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);}
- this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}}
- return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row<this.modules.length;row++){var y=row*cs;for(var col=0;col<this.modules[row].length;col++){var x=col*cs;var dark=this.modules[row][col];if(dark){qr_mc.beginFill(0,100);qr_mc.moveTo(x,y);qr_mc.lineTo(x+cs,y);qr_mc.lineTo(x+cs,y+cs);qr_mc.lineTo(x,y+cs);qr_mc.endFill();}}}
- return qr_mc;},setupTimingPattern:function(){for(var r=8;r<this.moduleCount-8;r++){if(this.modules[r][6]!=null){continue;}
- this.modules[r][6]=(r%2==0);}
- for(var c=8;c<this.moduleCount-8;c++){if(this.modules[6][c]!=null){continue;}
- this.modules[6][c]=(c%2==0);}},setupPositionAdjustPattern:function(){var pos=QRUtil.getPatternPosition(this.typeNumber);for(var i=0;i<pos.length;i++){for(var j=0;j<pos.length;j++){var row=pos[i];var col=pos[j];if(this.modules[row][col]!=null){continue;}
- for(var r=-2;r<=2;r++){for(var c=-2;c<=2;c++){if(r==-2||r==2||c==-2||c==2||(r==0&&c==0)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}}}},setupTypeNumber:function(test){var bits=QRUtil.getBCHTypeNumber(this.typeNumber);for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;}
- for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}}
- for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}}
- this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex<data.length){dark=(((data[byteIndex]>>>bitIndex)&1)==1);}
- var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;}
- this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}}
- row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;i<dataList.length;i++){var data=dataList[i];buffer.put(data.mode,4);buffer.put(data.getLength(),QRUtil.getLengthInBits(data.mode,typeNumber));data.write(buffer);}
- var totalDataCount=0;for(var i=0;i<rsBlocks.length;i++){totalDataCount+=rsBlocks[i].dataCount;}
- if(buffer.getLengthInBits()>totalDataCount*8){throw new Error("code length overflow. ("
- +buffer.getLengthInBits()
- +">"
- +totalDataCount*8
- +")");}
- if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);}
- while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);}
- while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;}
- buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;}
- buffer.put(QRCodeModel.PAD1,8);}
- return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r<rsBlocks.length;r++){var dcCount=rsBlocks[r].dataCount;var ecCount=rsBlocks[r].totalCount-dcCount;maxDcCount=Math.max(maxDcCount,dcCount);maxEcCount=Math.max(maxEcCount,ecCount);dcdata[r]=new Array(dcCount);for(var i=0;i<dcdata[r].length;i++){dcdata[r][i]=0xff&buffer.buffer[i+offset];}
- offset+=dcCount;var rsPoly=QRUtil.getErrorCorrectPolynomial(ecCount);var rawPoly=new QRPolynomial(dcdata[r],rsPoly.getLength()-1);var modPoly=rawPoly.mod(rsPoly);ecdata[r]=new Array(rsPoly.getLength()-1);for(var i=0;i<ecdata[r].length;i++){var modIndex=i+modPoly.getLength()-ecdata[r].length;ecdata[r][i]=(modIndex>=0)?modPoly.get(modIndex):0;}}
- var totalCodeCount=0;for(var i=0;i<rsBlocks.length;i++){totalCodeCount+=rsBlocks[i].totalCount;}
- var data=new Array(totalCodeCount);var index=0;for(var i=0;i<maxDcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<dcdata[r].length){data[index++]=dcdata[r][i];}}}
- for(var i=0;i<maxEcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<ecdata[r].length){data[index++]=ecdata[r][i];}}}
- return data;};var QRMode={MODE_NUMBER:1<<0,MODE_ALPHA_NUM:1<<1,MODE_8BIT_BYTE:1<<2,MODE_KANJI:1<<3};var QRErrorCorrectLevel={L:1,M:0,Q:3,H:2};var QRMaskPattern={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var QRUtil={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:(1<<10)|(1<<8)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0),G18:(1<<12)|(1<<11)|(1<<10)
|(1<<9)|(1<<8)|(1<<5)|(1<<2)|(1<<0),G15_MASK:(1<<14)|(1<<12)|(1<<10)|(1<<4)|(1<<1),getBCHTypeInfo:function(data){var d=data<<10;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)>=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));}
- return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));}
- return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;}
- return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i<errorCorrectLength;i++){a=a.multiply(new QRPolynomial([1,QRMath.gexp(i)],0));}
- return a;},getLengthInBits:function(mode,type){if(1<=type&&type<10){switch(mode){case QRMode.MODE_NUMBER:return 10;case QRMode.MODE_ALPHA_NUM:return 9;case QRMode.MODE_8BIT_BYTE:return 8;case QRMode.MODE_KANJI:return 8;default:throw new Error("mode:"+mode);}}else if(type<27){switch(mode){case QRMode.MODE_NUMBER:return 12;case QRMode.MODE_ALPHA_NUM:return 11;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 10;default:throw new Error("mode:"+mode);}}else if(type<41){switch(mode){case QRMode.MODE_NUMBER:return 14;case QRMode.MODE_ALPHA_NUM:return 13;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 12;default:throw new Error("mode:"+mode);}}else{throw new Error("type:"+type);}},getLostPoint:function(qrCode){var moduleCount=qrCode.getModuleCount();var lostPoint=0;for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount;col++){var sameCount=0;var dark=qrCode.isDark(row,col);for(var r=-1;r<=1;r++){if(row+r<0||moduleCount<=row+r){continue;}
- for(var c=-1;c<=1;c++){if(col+c<0||moduleCount<=col+c){continue;}
- if(r==0&&c==0){continue;}
- if(dark==qrCode.isDark(row+r,col+c)){sameCount++;}}}
- if(sameCount>5){lostPoint+=(3+sameCount-5);}}}
- for(var row=0;row<moduleCount-1;row++){for(var col=0;col<moduleCount-1;col++){var count=0;if(qrCode.isDark(row,col))count++;if(qrCode.isDark(row+1,col))count++;if(qrCode.isDark(row,col+1))count++;if(qrCode.isDark(row+1,col+1))count++;if(count==0||count==4){lostPoint+=3;}}}
- for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount-6;col++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row,col+1)&&qrCode.isDark(row,col+2)&&qrCode.isDark(row,col+3)&&qrCode.isDark(row,col+4)&&!qrCode.isDark(row,col+5)&&qrCode.isDark(row,col+6)){lostPoint+=40;}}}
- for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount-6;row++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row+1,col)&&qrCode.isDark(row+2,col)&&qrCode.isDark(row+3,col)&&qrCode.isDark(row+4,col)&&!qrCode.isDark(row+5,col)&&qrCode.isDark(row+6,col)){lostPoint+=40;}}}
- var darkCount=0;for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount;row++){if(qrCode.isDark(row,col)){darkCount++;}}}
- var ratio=Math.abs(100*darkCount/moduleCount/moduleCount-50)/5;lostPoint+=ratio*10;return lostPoint;}};var QRMath={glog:function(n){if(n<1){throw new Error("glog("+n+")");}
- return QRMath.LOG_TABLE[n];},gexp:function(n){while(n<0){n+=255;}
- while(n>=256){n-=255;}
- return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<<i;}
- for(var i=8;i<256;i++){QRMath.EXP_TABLE[i]=QRMath.EXP_TABLE[i-4]^QRMath.EXP_TABLE[i-5]^QRMath.EXP_TABLE[i-6]^QRMath.EXP_TABLE[i-8];}
- for(var i=0;i<255;i++){QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]]=i;}
- function QRPolynomial(num,shift){if(num.length==undefined){throw new Error(num.length+"/"+shift);}
- var offset=0;while(offset<num.length&&num[offset]==0){offset++;}
- this.num=new Array(num.length-offset+shift);for(var i=0;i<num.length-offset;i++){this.num[i]=num[i+offset];}}
- QRPolynomial.prototype={get:function(index){return this.num[index];},getLength:function(){return this.num.length;},multiply:function(e){var num=new Array(this.getLength()+e.getLength()-1);for(var i=0;i<this.getLength();i++){for(var j=0;j<e.getLength();j++){num[i+j]^=QRMath.gexp(QRMath.glog(this.get(i))+QRMath.glog(e.get(j)));}}
- return new QRPolynomial(num,0);},mod:function(e){if(this.getLength()-e.getLength()<0){return this;}
- var ratio=QRMath.glog(this.get(0))-QRMath.glog(e.get(0));var num=new Array(this.getLength());for(var i=0;i<this.getLength();i++){num[i]=this.get(i);}
- for(var i=0;i<e.getLength();i++){num[i]^=QRMath.gexp(QRMath.glog(e.get(i))+ratio);}
- return new QRPolynomial(num,0).mod(e);}};function QRRSBlock(totalCount,dataCount){this.totalCount=totalCount;this.dataCount=dataCount;}
- QRRSBlock.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,7
4,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,1
0,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];QRRSBlock.getRSBlocks=function(typeNumber,errorCorrectLevel){var rsBlock=QRRSBlock.getRsBlockTable(typeNumber,er
rorCorrectLevel);if(rsBlock==undefined){throw new Error("bad rs block @ typeNumber:"+typeNumber+"/errorCorrectLevel:"+errorCorrectLevel);}
- var length=rsBlock.length/3;var list=[];for(var i=0;i<length;i++){var count=rsBlock[i*3+0];var totalCount=rsBlock[i*3+1];var dataCount=rsBlock[i*3+2];for(var j=0;j<count;j++){list.push(new QRRSBlock(totalCount,dataCount));}}
- return list;};QRRSBlock.getRsBlockTable=function(typeNumber,errorCorrectLevel){switch(errorCorrectLevel){case QRErrorCorrectLevel.L:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+0];case QRErrorCorrectLevel.M:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+1];case QRErrorCorrectLevel.Q:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+2];case QRErrorCorrectLevel.H:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+3];default:return undefined;}};function QRBitBuffer(){this.buffer=[];this.length=0;}
- QRBitBuffer.prototype={get:function(index){var bufIndex=Math.floor(index/8);return((this.buffer[bufIndex]>>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i<length;i++){this.putBit(((num>>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);}
- if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));}
- this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]];
-
- function _isSupportCanvas() {
- return typeof CanvasRenderingContext2D != "undefined";
- }
-
- // android 2.x doesn't support Data-URI spec
- function _getAndroid() {
- var android = false;
- var sAgent = navigator.userAgent;
-
- if (/android/i.test(sAgent)) { // android
- android = true;
- var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i);
-
- if (aMat && aMat[1]) {
- android = parseFloat(aMat[1]);
- }
- }
-
- return android;
- }
-
- var svgDrawer = (function() {
-
- var Drawing = function (el, htOption) {
- this._el = el;
- this._htOption = htOption;
- };
-
- Drawing.prototype.draw = function (oQRCode) {
- var _htOption = this._htOption;
- var _el = this._el;
- var nCount = oQRCode.getModuleCount();
- var nWidth = Math.floor(_htOption.width / nCount);
- var nHeight = Math.floor(_htOption.height / nCount);
-
- this.clear();
-
- function makeSVG(tag, attrs) {
- var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
- for (var k in attrs)
- if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]);
- return el;
- }
-
- var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight});
- svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", "http://www.w3.org/2000/svg");
- _el.appendChild(svg);
-
- svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"}));
-
- var path = []
- for (var row = 0; row < nCount; row++) {
- for (var col = 0; col < nCount; col++) {
- var width = 0;
- while(col+width < nCount && oQRCode.isDark(row, col+width))
- width++;
-
- if (width>0) {
- path.push("M"+col+" "+row+"v1h"+width+"v-1z");
- col+=width;
- }
- }
- }
- var child = makeSVG("path", {"d": path.join(""), "fill": _htOption.colorDark});
- svg.appendChild(child);
- };
- Drawing.prototype.clear = function () {
- while (this._el.hasChildNodes())
- this._el.removeChild(this._el.lastChild);
- };
- return Drawing;
- })();
-
- var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
-
- // Drawing in DOM by using Table tag
- var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () {
- var Drawing = function (el, htOption) {
- this._el = el;
- this._htOption = htOption;
- };
-
- /**
- * Draw the QRCode
- *
- * @param {QRCode} oQRCode
- */
- Drawing.prototype.draw = function (oQRCode) {
- var _htOption = this._htOption;
- var _el = this._el;
- var nCount = oQRCode.getModuleCount();
- var nWidth = Math.floor(_htOption.width / nCount);
- var nHeight = Math.floor(_htOption.height / nCount);
- var aHTML = ['<table style="border:0;border-collapse:collapse;">'];
-
- for (var row = 0; row < nCount; row++) {
- aHTML.push('<tr>');
-
- for (var col = 0; col < nCount; col++) {
- aHTML.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';"></td>');
- }
-
- aHTML.push('</tr>');
- }
-
- aHTML.push('</table>');
- _el.innerHTML = aHTML.join('');
-
- // Fix the margin values as real size.
- var elTable = _el.childNodes[0];
- var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;
- var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
-
- if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
- elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px";
- }
- };
-
- /**
- * Clear the QRCode
- */
- Drawing.prototype.clear = function () {
- this._el.innerHTML = '';
- };
-
- return Drawing;
- })() : (function () { // Drawing in Canvas
- function _onMakeImage() {
- this._elImage.src = this._elCanvas.toDataURL("image/png");
- this._elImage.style.display = "block";
- this._elCanvas.style.display = "none";
- }
-
- // Android 2.1 bug workaround
- // http://code.google.com/p/android/issues/detail?id=5141
- if (this._android && this._android <= 2.1) {
- var factor = 1 / window.devicePixelRatio;
- var drawImage = CanvasRenderingContext2D.prototype.drawImage;
- CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
- if (("nodeName" in image) && /img/i.test(image.nodeName)) {
- for (var i = arguments.length - 1; i >= 1; i--) {
- arguments[i] = arguments[i] * factor;
- }
- } else if (typeof dw == "undefined") {
- arguments[1] *= factor;
- arguments[2] *= factor;
- arguments[3] *= factor;
- arguments[4] *= factor;
- }
-
- drawImage.apply(this, arguments);
- };
- }
-
- /**
- * Check whether the user's browser supports Data URI or not
- *
- * @private
- * @param {Function} fSuccess Occurs if it supports Data URI
- * @param {Function} fFail Occurs if it doesn't support Data URI
- */
- function _safeSetDataURI(fSuccess, fFail) {
- var self = this;
- self._fFail = fFail;
- self._fSuccess = fSuccess;
-
- // Check it just once
- if (self._bSupportDataURI === null) {
- var el = document.createElement("img");
- var fOnError = function() {
- self._bSupportDataURI = false;
-
- if (self._fFail) {
- self._fFail.call(self);
- }
- };
- var fOnSuccess = function() {
- self._bSupportDataURI = true;
-
- if (self._fSuccess) {
- self._fSuccess.call(self);
- }
- };
-
- el.onabort = fOnError;
- el.onerror = fOnError;
- el.onload = fOnSuccess;
- el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data.
- return;
- } else if (self._bSupportDataURI === true && self._fSuccess) {
- self._fSuccess.call(self);
- } else if (self._bSupportDataURI === false && self._fFail) {
- self._fFail.call(self);
+(function() {
+ //---------------------------------------------------------------------
+ // QRCode for JavaScript
+ //
+ // Copyright (c) 2009 Kazuhiko Arase
+ //
+ // URL: http://www.d-project.com/
+ //
+ // Licensed under the MIT license:
+ // http://www.opensource.org/licenses/mit-license.php
+ //
+ // The word "QR Code" is registered trademark of
+ // DENSO WAVE INCORPORATED
+ // http://www.denso-wave.com/qrcode/faqpatent-e.html
+ //
+ //---------------------------------------------------------------------
+ function QR8bitByte(data) {
+ this.mode = QRMode.MODE_8BIT_BYTE;
+ this.data = data;
+ this.parsedData = [];
+
+ // Added to support UTF-8 Characters
+ for (var i = 0, l = this.data.length; i < l; i++) {
+ var byteArray = [];
+ var code = this.data.charCodeAt(i);
+
+ if (code > 0x10000) {
+ byteArray[0] = 0xf0 | ((code & 0x1c0000) >>> 18);
+ byteArray[1] = 0x80 | ((code & 0x3f000) >>> 12);
+ byteArray[2] = 0x80 | ((code & 0xfc0) >>> 6);
+ byteArray[3] = 0x80 | (code & 0x3f);
+ } else if (code > 0x800) {
+ byteArray[0] = 0xe0 | ((code & 0xf000) >>> 12);
+ byteArray[1] = 0x80 | ((code & 0xfc0) >>> 6);
+ byteArray[2] = 0x80 | (code & 0x3f);
+ } else if (code > 0x80) {
+ byteArray[0] = 0xc0 | ((code & 0x7c0) >>> 6);
+ byteArray[1] = 0x80 | (code & 0x3f);
+ } else {
+ byteArray[0] = code;
+ }
+
+ this.parsedData.push(byteArray);
+ }
+
+ this.parsedData = Array.prototype.concat.apply([], this.parsedData);
+
+ if (this.parsedData.length != this.data.length) {
+ this.parsedData.unshift(191);
+ this.parsedData.unshift(187);
+ this.parsedData.unshift(239);
+ }
+ }
+
+ QR8bitByte.prototype = {
+ getLength(buffer) {
+ return this.parsedData.length;
+ },
+ write(buffer) {
+ for (var i = 0, l = this.parsedData.length; i < l; i++) {
+ buffer.put(this.parsedData[i], 8);
+ }
+ },
+ };
+
+ function QRCodeModel(typeNumber, errorCorrectLevel) {
+ this.typeNumber = typeNumber;
+ this.errorCorrectLevel = errorCorrectLevel;
+ this.modules = null;
+ this.moduleCount = 0;
+ this.dataCache = null;
+ this.dataList = [];
+ }
+
+ QRCodeModel.prototype = {
+ addData(data) {
+ var newData = new QR8bitByte(data);
+ this.dataList.push(newData);
+ this.dataCache = null;
+ },
+ isDark(row, col) {
+ if (
+ row < 0 ||
+ this.moduleCount <= row ||
+ col < 0 ||
+ this.moduleCount <= col
+ ) {
+ throw new Error(row + "," + col);
+ }
+ return this.modules[row][col];
+ },
+ getModuleCount() {
+ return this.moduleCount;
+ },
+ make() {
+ this.makeImpl(false, this.getBestMaskPattern());
+ },
+ makeImpl(test, maskPattern) {
+ this.moduleCount = this.typeNumber * 4 + 17;
+ this.modules = new Array(this.moduleCount);
+ for (var row = 0; row < this.moduleCount; row++) {
+ this.modules[row] = new Array(this.moduleCount);
+ for (var col = 0; col < this.moduleCount; col++) {
+ this.modules[row][col] = null;
+ }
+ }
+ this.setupPositionProbePattern(0, 0);
+ this.setupPositionProbePattern(this.moduleCount - 7, 0);
+ this.setupPositionProbePattern(0, this.moduleCount - 7);
+ this.setupPositionAdjustPattern();
+ this.setupTimingPattern();
+ this.setupTypeInfo(test, maskPattern);
+ if (this.typeNumber >= 7) {
+ this.setupTypeNumber(test);
+ }
+ if (this.dataCache == null) {
+ this.dataCache = QRCodeModel.createData(
+ this.typeNumber,
+ this.errorCorrectLevel,
+ this.dataList
+ );
+ }
+ this.mapData(this.dataCache, maskPattern);
+ },
+ setupPositionProbePattern(row, col) {
+ for (var r = -1; r <= 7; r++) {
+ if (row + r <= -1 || this.moduleCount <= row + r) {
+ continue;
+ }
+ for (var c = -1; c <= 7; c++) {
+ if (col + c <= -1 || this.moduleCount <= col + c) {
+ continue;
+ }
+ if (
+ (0 <= r && r <= 6 && (c == 0 || c == 6)) ||
+ (0 <= c && c <= 6 && (r == 0 || r == 6)) ||
+ (2 <= r && r <= 4 && 2 <= c && c <= 4)
+ ) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ },
+ getBestMaskPattern() {
+ var minLostPoint = 0;
+ var pattern = 0;
+ for (var i = 0; i < 8; i++) {
+ this.makeImpl(true, i);
+ var lostPoint = QRUtil.getLostPoint(this);
+ if (i == 0 || minLostPoint > lostPoint) {
+ minLostPoint = lostPoint;
+ pattern = i;
+ }
+ }
+ return pattern;
+ },
+ createMovieClip(target_mc, instance_name, depth) {
+ var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
+ var cs = 1;
+ this.make();
+ for (var row = 0; row < this.modules.length; row++) {
+ var y = row * cs;
+ for (var col = 0; col < this.modules[row].length; col++) {
+ var x = col * cs;
+ var dark = this.modules[row][col];
+ if (dark) {
+ qr_mc.beginFill(0, 100);
+ qr_mc.moveTo(x, y);
+ qr_mc.lineTo(x + cs, y);
+ qr_mc.lineTo(x + cs, y + cs);
+ qr_mc.lineTo(x, y + cs);
+ qr_mc.endFill();
+ }
+ }
+ }
+ return qr_mc;
+ },
+ setupTimingPattern() {
+ for (var r = 8; r < this.moduleCount - 8; r++) {
+ if (this.modules[r][6] != null) {
+ continue;
+ }
+ this.modules[r][6] = r % 2 == 0;
+ }
+ for (var c = 8; c < this.moduleCount - 8; c++) {
+ if (this.modules[6][c] != null) {
+ continue;
+ }
+ this.modules[6][c] = c % 2 == 0;
+ }
+ },
+ setupPositionAdjustPattern() {
+ var pos = QRUtil.getPatternPosition(this.typeNumber);
+ for (var i = 0; i < pos.length; i++) {
+ for (var j = 0; j < pos.length; j++) {
+ var row = pos[i];
+ var col = pos[j];
+ if (this.modules[row][col] != null) {
+ continue;
+ }
+ for (var r = -2; r <= 2; r++) {
+ for (var c = -2; c <= 2; c++) {
+ if (
+ r == -2 ||
+ r == 2 ||
+ c == -2 ||
+ c == 2 ||
+ (r == 0 && c == 0)
+ ) {
+ this.modules[row + r][col + c] = true;
+ } else {
+ this.modules[row + r][col + c] = false;
+ }
+ }
+ }
+ }
+ }
+ },
+ setupTypeNumber(test) {
+ var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+ for (var i = 0; i < 18; i++) {
+ var mod = !test && ((bits >> i) & 1) == 1;
+ this.modules[Math.floor(i / 3)][
+ (i % 3) + this.moduleCount - 8 - 3
+ ] = mod;
+ }
+ for (var i = 0; i < 18; i++) {
+ var mod = !test && ((bits >> i) & 1) == 1;
+ this.modules[(i % 3) + this.moduleCount - 8 - 3][
+ Math.floor(i / 3)
+ ] = mod;
+ }
+ },
+ setupTypeInfo(test, maskPattern) {
+ var data = (this.errorCorrectLevel << 3) | maskPattern;
+ var bits = QRUtil.getBCHTypeInfo(data);
+ for (var i = 0; i < 15; i++) {
+ var mod = !test && ((bits >> i) & 1) == 1;
+ if (i < 6) {
+ this.modules[i][8] = mod;
+ } else if (i < 8) {
+ this.modules[i + 1][8] = mod;
+ } else {
+ this.modules[this.moduleCount - 15 + i][8] = mod;
+ }
+ }
+ for (var i = 0; i < 15; i++) {
+ var mod = !test && ((bits >> i) & 1) == 1;
+ if (i < 8) {
+ this.modules[8][this.moduleCount - i - 1] = mod;
+ } else if (i < 9) {
+ this.modules[8][15 - i - 1 + 1] = mod;
+ } else {
+ this.modules[8][15 - i - 1] = mod;
+ }
+ }
+ this.modules[this.moduleCount - 8][8] = !test;
+ },
+ mapData(data, maskPattern) {
+ var inc = -1;
+ var row = this.moduleCount - 1;
+ var bitIndex = 7;
+ var byteIndex = 0;
+ for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+ if (col == 6) {
+ col--;
+ }
+ while (true) {
+ for (var c = 0; c < 2; c++) {
+ if (this.modules[row][col - c] == null) {
+ var dark = false;
+ if (byteIndex < data.length) {
+ dark = ((data[byteIndex] >>> bitIndex) & 1) == 1;
+ }
+ var mask = QRUtil.getMask(maskPattern, row, col - c);
+ if (mask) {
+ dark = !dark;
+ }
+ this.modules[row][col - c] = dark;
+ bitIndex--;
+ if (bitIndex == -1) {
+ byteIndex++;
+ bitIndex = 7;
+ }
+ }
+ }
+ row += inc;
+ if (row < 0 || this.moduleCount <= row) {
+ row -= inc;
+ inc = -inc;
+ break;
+ }
+ }
+ }
+ },
+ };
+ QRCodeModel.PAD0 = 0xec;
+ QRCodeModel.PAD1 = 0x11;
+ QRCodeModel.createData = function(typeNumber, errorCorrectLevel, dataList) {
+ var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
+ var buffer = new QRBitBuffer();
+ for (var i = 0; i < dataList.length; i++) {
+ var data = dataList[i];
+ buffer.put(data.mode, 4);
+ buffer.put(
+ data.getLength(),
+ QRUtil.getLengthInBits(data.mode, typeNumber)
+ );
+ data.write(buffer);
+ }
+ var totalDataCount = 0;
+ for (var i = 0; i < rsBlocks.length; i++) {
+ totalDataCount += rsBlocks[i].dataCount;
+ }
+ if (buffer.getLengthInBits() > totalDataCount * 8) {
+ throw new Error(
+ "code length overflow. (" +
+ buffer.getLengthInBits() +
+ ">" +
+ totalDataCount * 8 +
+ ")"
+ );
+ }
+ if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
+ buffer.put(0, 4);
+ }
+ while (buffer.getLengthInBits() % 8 != 0) {
+ buffer.putBit(false);
+ }
+ while (true) {
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCodeModel.PAD0, 8);
+ if (buffer.getLengthInBits() >= totalDataCount * 8) {
+ break;
+ }
+ buffer.put(QRCodeModel.PAD1, 8);
+ }
+ return QRCodeModel.createBytes(buffer, rsBlocks);
+ };
+ QRCodeModel.createBytes = function(buffer, rsBlocks) {
+ var offset = 0;
+ var maxDcCount = 0;
+ var maxEcCount = 0;
+ var dcdata = new Array(rsBlocks.length);
+ var ecdata = new Array(rsBlocks.length);
+ for (var r = 0; r < rsBlocks.length; r++) {
+ var dcCount = rsBlocks[r].dataCount;
+ var ecCount = rsBlocks[r].totalCount - dcCount;
+ maxDcCount = Math.max(maxDcCount, dcCount);
+ maxEcCount = Math.max(maxEcCount, ecCount);
+ dcdata[r] = new Array(dcCount);
+ for (var i = 0; i < dcdata[r].length; i++) {
+ dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+ }
+ offset += dcCount;
+ var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+ var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+ var modPoly = rawPoly.mod(rsPoly);
+ ecdata[r] = new Array(rsPoly.getLength() - 1);
+ for (var i = 0; i < ecdata[r].length; i++) {
+ var modIndex = i + modPoly.getLength() - ecdata[r].length;
+ ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
+ }
+ }
+ var totalCodeCount = 0;
+ for (var i = 0; i < rsBlocks.length; i++) {
+ totalCodeCount += rsBlocks[i].totalCount;
+ }
+ var data = new Array(totalCodeCount);
+ var index = 0;
+ for (var i = 0; i < maxDcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < dcdata[r].length) {
+ data[index++] = dcdata[r][i];
+ }
+ }
+ }
+ for (var i = 0; i < maxEcCount; i++) {
+ for (var r = 0; r < rsBlocks.length; r++) {
+ if (i < ecdata[r].length) {
+ data[index++] = ecdata[r][i];
+ }
+ }
+ }
+ return data;
+ };
+ var QRMode = {
+ MODE_NUMBER: 1 << 0,
+ MODE_ALPHA_NUM: 1 << 1,
+ MODE_8BIT_BYTE: 1 << 2,
+ MODE_KANJI: 1 << 3,
+ };
+ var QRErrorCorrectLevel = { L: 1, M: 0, Q: 3, H: 2 };
+ var QRMaskPattern = {
+ PATTERN000: 0,
+ PATTERN001: 1,
+ PATTERN010: 2,
+ PATTERN011: 3,
+ PATTERN100: 4,
+ PATTERN101: 5,
+ PATTERN110: 6,
+ PATTERN111: 7,
+ };
+ var QRUtil = {
+ PATTERN_POSITION_TABLE: [
+ [],
+ [6, 18],
+ [6, 22],
+ [6, 26],
+ [6, 30],
+ [6, 34],
+ [6, 22, 38],
+ [6, 24, 42],
+ [6, 26, 46],
+ [6, 28, 50],
+ [6, 30, 54],
+ [6, 32, 58],
+ [6, 34, 62],
+ [6, 26, 46, 66],
+ [6, 26, 48, 70],
+ [6, 26, 50, 74],
+ [6, 30, 54, 78],
+ [6, 30, 56, 82],
+ [6, 30, 58, 86],
+ [6, 34, 62, 90],
+ [6, 28, 50, 72, 94],
+ [6, 26, 50, 74, 98],
+ [6, 30, 54, 78, 102],
+ [6, 28, 54, 80, 106],
+ [6, 32, 58, 84, 110],
+ [6, 30, 58, 86, 114],
+ [6, 34, 62, 90, 118],
+ [6, 26, 50, 74, 98, 122],
+ [6, 30, 54, 78, 102, 126],
+ [6, 26, 52, 78, 104, 130],
+ [6, 30, 56, 82, 108, 134],
+ [6, 34, 60, 86, 112, 138],
+ [6, 30, 58, 86, 114, 142],
+ [6, 34, 62, 90, 118, 146],
+ [6, 30, 54, 78, 102, 126, 150],
+ [6, 24, 50, 76, 102, 128, 154],
+ [6, 28, 54, 80, 106, 132, 158],
+ [6, 32, 58, 84, 110, 136, 162],
+ [6, 26, 54, 82, 110, 138, 166],
+ [6, 30, 58, 86, 114, 142, 170],
+ ],
+ G15:
+ (1 << 10) |
+ (1 << 8) |
+ (1 << 5) |
+ (1 << 4) |
+ (1 << 2) |
+ (1 << 1) |
+ (1 << 0),
+ G18:
+ (1 << 12) |
+ (1 << 11) |
+ (1 << 10) |
+ (1 << 9) |
+ (1 << 8) |
+ (1 << 5) |
+ (1 << 2) |
+ (1 << 0),
+ G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+ getBCHTypeInfo(data) {
+ var d = data << 10;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+ d ^=
+ QRUtil.G15 <<
+ (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15));
+ }
+ return ((data << 10) | d) ^ QRUtil.G15_MASK;
+ },
+ getBCHTypeNumber(data) {
+ var d = data << 12;
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+ d ^=
+ QRUtil.G18 <<
+ (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18));
+ }
+ return (data << 12) | d;
+ },
+ getBCHDigit(data) {
+ var digit = 0;
+ while (data != 0) {
+ digit++;
+ data >>>= 1;
+ }
+ return digit;
+ },
+ getPatternPosition(typeNumber) {
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+ },
+ getMask(maskPattern, i, j) {
+ switch (maskPattern) {
+ case QRMaskPattern.PATTERN000:
+ return (i + j) % 2 == 0;
+ case QRMaskPattern.PATTERN001:
+ return i % 2 == 0;
+ case QRMaskPattern.PATTERN010:
+ return j % 3 == 0;
+ case QRMaskPattern.PATTERN011:
+ return (i + j) % 3 == 0;
+ case QRMaskPattern.PATTERN100:
+ return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
+ case QRMaskPattern.PATTERN101:
+ return ((i * j) % 2) + ((i * j) % 3) == 0;
+ case QRMaskPattern.PATTERN110:
+ return (((i * j) % 2) + ((i * j) % 3)) % 2 == 0;
+ case QRMaskPattern.PATTERN111:
+ return (((i * j) % 3) + ((i + j) % 2)) % 2 == 0;
+ default:
+ throw new Error("bad maskPattern:" + maskPattern);
+ }
+ },
+ getErrorCorrectPolynomial(errorCorrectLength) {
+ var a = new QRPolynomial([1], 0);
+ for (var i = 0; i < errorCorrectLength; i++) {
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
+ }
+ return a;
+ },
+ getLengthInBits(mode, type) {
+ if (1 <= type && type < 10) {
+ switch (mode) {
+ case QRMode.MODE_NUMBER:
+ return 10;
+ case QRMode.MODE_ALPHA_NUM:
+ return 9;
+ case QRMode.MODE_8BIT_BYTE:
+ return 8;
+ case QRMode.MODE_KANJI:
+ return 8;
+ default:
+ throw new Error("mode:" + mode);
+ }
+ } else if (type < 27) {
+ switch (mode) {
+ case QRMode.MODE_NUMBER:
+ return 12;
+ case QRMode.MODE_ALPHA_NUM:
+ return 11;
+ case QRMode.MODE_8BIT_BYTE:
+ return 16;
+ case QRMode.MODE_KANJI:
+ return 10;
+ default:
+ throw new Error("mode:" + mode);
+ }
+ } else if (type < 41) {
+ switch (mode) {
+ case QRMode.MODE_NUMBER:
+ return 14;
+ case QRMode.MODE_ALPHA_NUM:
+ return 13;
+ case QRMode.MODE_8BIT_BYTE:
+ return 16;
+ case QRMode.MODE_KANJI:
+ return 12;
+ default:
+ throw new Error("mode:" + mode);
+ }
+ } else {
+ throw new Error("type:" + type);
+ }
+ },
+ getLostPoint(qrCode) {
+ var moduleCount = qrCode.getModuleCount();
+ var lostPoint = 0;
+ for (var row = 0; row < moduleCount; row++) {
+ for (var col = 0; col < moduleCount; col++) {
+ var sameCount = 0;
+ var dark = qrCode.isDark(row, col);
+ for (var r = -1; r <= 1; r++) {
+ if (row + r < 0 || moduleCount <= row + r) {
+ continue;
}
- };
-
- /**
- * Drawing QRCode by using canvas
- *
- * @constructor
- * @param {HTMLElement} el
- * @param {Object} htOption QRCode Options
- */
- var Drawing = function (el, htOption) {
- this._bIsPainted = false;
- this._android = _getAndroid();
-
- this._htOption = htOption;
- this._elCanvas = document.createElement("canvas");
- this._elCanvas.width = htOption.width;
- this._elCanvas.height = htOption.height;
- el.appendChild(this._elCanvas);
- this._el = el;
- this._oContext = this._elCanvas.getContext("2d");
- this._bIsPainted = false;
- this._elImage = document.createElement("img");
- this._elImage.alt = "Scan me!";
- this._elImage.style.display = "none";
- this._el.appendChild(this._elImage);
- this._bSupportDataURI = null;
- };
-
- /**
- * Draw the QRCode
- *
- * @param {QRCode} oQRCode
- */
- Drawing.prototype.draw = function (oQRCode) {
- var _elImage = this._elImage;
- var _oContext = this._oContext;
- var _htOption = this._htOption;
-
- var nCount = oQRCode.getModuleCount();
- var nWidth = _htOption.width / nCount;
- var nHeight = _htOption.height / nCount;
- var nRoundedWidth = Math.round(nWidth);
- var nRoundedHeight = Math.round(nHeight);
-
- _elImage.style.display = "none";
- this.clear();
-
- for (var row = 0; row < nCount; row++) {
- for (var col = 0; col < nCount; col++) {
- var bIsDark = oQRCode.isDark(row, col);
- var nLeft = col * nWidth;
- var nTop = row * nHeight;
- _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
- _oContext.lineWidth = 1;
- _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
- _oContext.fillRect(nLeft, nTop, nWidth, nHeight);
-
- // 안티 앨리어싱 방지 처리
- _oContext.strokeRect(
- Math.floor(nLeft) + 0.5,
- Math.floor(nTop) + 0.5,
- nRoundedWidth,
- nRoundedHeight
- );
-
- _oContext.strokeRect(
- Math.ceil(nLeft) - 0.5,
- Math.ceil(nTop) - 0.5,
- nRoundedWidth,
- nRoundedHeight
- );
- }
- }
-
- this._bIsPainted = true;
- };
-
- /**
- * Make the image from Canvas if the browser supports Data URI.
- */
- Drawing.prototype.makeImage = function () {
- if (this._bIsPainted) {
- _safeSetDataURI.call(this, _onMakeImage);
- }
- };
-
- /**
- * Return whether the QRCode is painted or not
- *
- * @return {Boolean}
- */
- Drawing.prototype.isPainted = function () {
- return this._bIsPainted;
- };
-
- /**
- * Clear the QRCode
- */
- Drawing.prototype.clear = function () {
- this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);
- this._bIsPainted = false;
- };
-
- /**
- * @private
- * @param {Number} nNumber
- */
- Drawing.prototype.round = function (nNumber) {
- if (!nNumber) {
- return nNumber;
- }
-
- return Math.floor(nNumber * 1000) / 1000;
- };
-
- return Drawing;
- })();
-
- /**
- * Get the type by string length
- *
- * @private
- * @param {String} sText
- * @param {Number} nCorrectLevel
- * @return {Number} type
- */
- function _getTypeNumber(sText, nCorrectLevel) {
- var nType = 1;
- var length = _getUTF8Length(sText);
-
- for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
- var nLimit = 0;
-
- switch (nCorrectLevel) {
- case QRErrorCorrectLevel.L :
- nLimit = QRCodeLimitLength[i][0];
- break;
- case QRErrorCorrectLevel.M :
- nLimit = QRCodeLimitLength[i][1];
- break;
- case QRErrorCorrectLevel.Q :
- nLimit = QRCodeLimitLength[i][2];
- break;
- case QRErrorCorrectLevel.H :
- nLimit = QRCodeLimitLength[i][3];
- break;
- }
-
- if (length <= nLimit) {
- break;
- } else {
- nType++;
- }
- }
-
- if (nType > QRCodeLimitLength.length) {
- throw new Error("Too long data");
- }
-
- return nType;
- }
-
- function _getUTF8Length(sText) {
- var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
- return replacedText.length + (replacedText.length != sText ? 3 : 0);
- }
-
- /**
- * @class QRCode
- * @constructor
- * @example
- * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
- *
- * @example
- * var oQRCode = new QRCode("test", {
- * text : "http://naver.com",
- * width : 128,
- * height : 128
- * });
- *
- * oQRCode.clear(); // Clear the QRCode.
- * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
- *
- * @param {HTMLElement|String} el target element or 'id' attribute of element.
- * @param {Object|String} vOption
- * @param {String} vOption.text QRCode link data
- * @param {Number} [vOption.width=256]
- * @param {Number} [vOption.height=256]
- * @param {String} [vOption.colorDark="#000000"]
- * @param {String} [vOption.colorLight="#ffffff"]
- * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
- */
- QRCode = function (el, vOption) {
- this._htOption = {
- width : 256,
- height : 256,
- typeNumber : 4,
- colorDark : "#000000",
- colorLight : "#ffffff",
- correctLevel : QRErrorCorrectLevel.H
- };
-
- if (typeof vOption === 'string') {
- vOption = {
- text : vOption
- };
- }
-
- // Overwrites options
- if (vOption) {
- for (var i in vOption) {
- this._htOption[i] = vOption[i];
- }
- }
-
- if (typeof el == "string") {
- el = document.getElementById(el);
- }
-
- if (this._htOption.useSVG) {
- Drawing = svgDrawer;
- }
-
- this._android = _getAndroid();
- this._el = el;
- this._oQRCode = null;
- this._oDrawing = new Drawing(this._el, this._htOption);
-
- if (this._htOption.text) {
- this.makeCode(this._htOption.text);
- }
- };
-
- /**
- * Make the QRCode
- *
- * @param {String} sText link data
- */
- QRCode.prototype.makeCode = function (sText) {
- this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);
- this._oQRCode.addData(sText);
- this._oQRCode.make();
- this._el.title = sText;
- this._oDrawing.draw(this._oQRCode);
- this.makeImage();
- };
-
- /**
- * Make the Image from Canvas element
- * - It occurs automatically
- * - Android below 3 doesn't support Data-URI spec.
- *
- * @private
- */
- QRCode.prototype.makeImage = function () {
- if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) {
- this._oDrawing.makeImage();
- }
- };
-
- /**
- * Clear the QRCode
- */
- QRCode.prototype.clear = function () {
- this._oDrawing.clear();
- };
-
- /**
- * @name QRCode.CorrectLevel
- */
- QRCode.CorrectLevel = QRErrorCorrectLevel;
+ for (var c = -1; c <= 1; c++) {
+ if (col + c < 0 || moduleCount <= col + c) {
+ continue;
+ }
+ if (r == 0 && c == 0) {
+ continue;
+ }
+ if (dark == qrCode.isDark(row + r, col + c)) {
+ sameCount++;
+ }
+ }
+ }
+ if (sameCount > 5) {
+ lostPoint += 3 + sameCount - 5;
+ }
+ }
+ }
+ for (var row = 0; row < moduleCount - 1; row++) {
+ for (var col = 0; col < moduleCount - 1; col++) {
+ var count = 0;
+ if (qrCode.isDark(row, col)) {
+ count++;
+ }
+ if (qrCode.isDark(row + 1, col)) {
+ count++;
+ }
+ if (qrCode.isDark(row, col + 1)) {
+ count++;
+ }
+ if (qrCode.isDark(row + 1, col + 1)) {
+ count++;
+ }
+ if (count == 0 || count == 4) {
+ lostPoint += 3;
+ }
+ }
+ }
+ for (var row = 0; row < moduleCount; row++) {
+ for (var col = 0; col < moduleCount - 6; col++) {
+ if (
+ qrCode.isDark(row, col) &&
+ !qrCode.isDark(row, col + 1) &&
+ qrCode.isDark(row, col + 2) &&
+ qrCode.isDark(row, col + 3) &&
+ qrCode.isDark(row, col + 4) &&
+ !qrCode.isDark(row, col + 5) &&
+ qrCode.isDark(row, col + 6)
+ ) {
+ lostPoint += 40;
+ }
+ }
+ }
+ for (var col = 0; col < moduleCount; col++) {
+ for (var row = 0; row < moduleCount - 6; row++) {
+ if (
+ qrCode.isDark(row, col) &&
+ !qrCode.isDark(row + 1, col) &&
+ qrCode.isDark(row + 2, col) &&
+ qrCode.isDark(row + 3, col) &&
+ qrCode.isDark(row + 4, col) &&
+ !qrCode.isDark(row + 5, col) &&
+ qrCode.isDark(row + 6, col)
+ ) {
+ lostPoint += 40;
+ }
+ }
+ }
+ var darkCount = 0;
+ for (var col = 0; col < moduleCount; col++) {
+ for (var row = 0; row < moduleCount; row++) {
+ if (qrCode.isDark(row, col)) {
+ darkCount++;
+ }
+ }
+ }
+ var ratio =
+ Math.abs((100 * darkCount) / moduleCount / moduleCount - 50) / 5;
+ lostPoint += ratio * 10;
+ return lostPoint;
+ },
+ };
+ var QRMath = {
+ glog(n) {
+ if (n < 1) {
+ throw new Error("glog(" + n + ")");
+ }
+ return QRMath.LOG_TABLE[n];
+ },
+ gexp(n) {
+ while (n < 0) {
+ n += 255;
+ }
+ while (n >= 256) {
+ n -= 255;
+ }
+ return QRMath.EXP_TABLE[n];
+ },
+ EXP_TABLE: new Array(256),
+ LOG_TABLE: new Array(256),
+ };
+ for (var i = 0; i < 8; i++) {
+ QRMath.EXP_TABLE[i] = 1 << i;
+ }
+ for (var i = 8; i < 256; i++) {
+ QRMath.EXP_TABLE[i] =
+ QRMath.EXP_TABLE[i - 4] ^
+ QRMath.EXP_TABLE[i - 5] ^
+ QRMath.EXP_TABLE[i - 6] ^
+ QRMath.EXP_TABLE[i - 8];
+ }
+ for (var i = 0; i < 255; i++) {
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
+ }
+ function QRPolynomial(num, shift) {
+ if (num.length == undefined) {
+ throw new Error(num.length + "/" + shift);
+ }
+ var offset = 0;
+ while (offset < num.length && num[offset] == 0) {
+ offset++;
+ }
+ this.num = new Array(num.length - offset + shift);
+ for (var i = 0; i < num.length - offset; i++) {
+ this.num[i] = num[i + offset];
+ }
+ }
+ QRPolynomial.prototype = {
+ get(index) {
+ return this.num[index];
+ },
+ getLength() {
+ return this.num.length;
+ },
+ multiply(e) {
+ var num = new Array(this.getLength() + e.getLength() - 1);
+ for (var i = 0; i < this.getLength(); i++) {
+ for (var j = 0; j < e.getLength(); j++) {
+ num[i + j] ^= QRMath.gexp(
+ QRMath.glog(this.get(i)) + QRMath.glog(e.get(j))
+ );
+ }
+ }
+ return new QRPolynomial(num, 0);
+ },
+ mod(e) {
+ if (this.getLength() - e.getLength() < 0) {
+ return this;
+ }
+ var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
+ var num = new Array(this.getLength());
+ for (var i = 0; i < this.getLength(); i++) {
+ num[i] = this.get(i);
+ }
+ for (var i = 0; i < e.getLength(); i++) {
+ num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
+ }
+ return new QRPolynomial(num, 0).mod(e);
+ },
+ };
+ function QRRSBlock(totalCount, dataCount) {
+ this.totalCount = totalCount;
+ this.dataCount = dataCount;
+ }
+ QRRSBlock.RS_BLOCK_TABLE = [
+ [1, 26, 19],
+ [1, 26, 16],
+ [1, 26, 13],
+ [1, 26, 9],
+ [1, 44, 34],
+ [1, 44, 28],
+ [1, 44, 22],
+ [1, 44, 16],
+ [1, 70, 55],
+ [1, 70, 44],
+ [2, 35, 17],
+ [2, 35, 13],
+ [1, 100, 80],
+ [2, 50, 32],
+ [2, 50, 24],
+ [4, 25, 9],
+ [1, 134, 108],
+ [2, 67, 43],
+ [2, 33, 15, 2, 34, 16],
+ [2, 33, 11, 2, 34, 12],
+ [2, 86, 68],
+ [4, 43, 27],
+ [4, 43, 19],
+ [4, 43, 15],
+ [2, 98, 78],
+ [4, 49, 31],
+ [2, 32, 14, 4, 33, 15],
+ [4, 39, 13, 1, 40, 14],
+ [2, 121, 97],
+ [2, 60, 38, 2, 61, 39],
+ [4, 40, 18, 2, 41, 19],
+ [4, 40, 14, 2, 41, 15],
+ [2, 146, 116],
+ [3, 58, 36, 2, 59, 37],
+ [4, 36, 16, 4, 37, 17],
+ [4, 36, 12, 4, 37, 13],
+ [2, 86, 68, 2, 87, 69],
+ [4, 69, 43, 1, 70, 44],
+ [6, 43, 19, 2, 44, 20],
+ [6, 43, 15, 2, 44, 16],
+ [4, 101, 81],
+ [1, 80, 50, 4, 81, 51],
+ [4, 50, 22, 4, 51, 23],
+ [3, 36, 12, 8, 37, 13],
+ [2, 116, 92, 2, 117, 93],
+ [6, 58, 36, 2, 59, 37],
+ [4, 46, 20, 6, 47, 21],
+ [7, 42, 14, 4, 43, 15],
+ [4, 133, 107],
+ [8, 59, 37, 1, 60, 38],
+ [8, 44, 20, 4, 45, 21],
+ [12, 33, 11, 4, 34, 12],
+ [3, 145, 115, 1, 146, 116],
+ [4, 64, 40, 5, 65, 41],
+ [11, 36, 16, 5, 37, 17],
+ [11, 36, 12, 5, 37, 13],
+ [5, 109, 87, 1, 110, 88],
+ [5, 65, 41, 5, 66, 42],
+ [5, 54, 24, 7, 55, 25],
+ [11, 36, 12],
+ [5, 122, 98, 1, 123, 99],
+ [7, 73, 45, 3, 74, 46],
+ [15, 43, 19, 2, 44, 20],
+ [3, 45, 15, 13, 46, 16],
+ [1, 135, 107, 5, 136, 108],
+ [10, 74, 46, 1, 75, 47],
+ [1, 50, 22, 15, 51, 23],
+ [2, 42, 14, 17, 43, 15],
+ [5, 150, 120, 1, 151, 121],
+ [9, 69, 43, 4, 70, 44],
+ [17, 50, 22, 1, 51, 23],
+ [2, 42, 14, 19, 43, 15],
+ [3, 141, 113, 4, 142, 114],
+ [3, 70, 44, 11, 71, 45],
+ [17, 47, 21, 4, 48, 22],
+ [9, 39, 13, 16, 40, 14],
+ [3, 135, 107, 5, 136, 108],
+ [3, 67, 41, 13, 68, 42],
+ [15, 54, 24, 5, 55, 25],
+ [15, 43, 15, 10, 44, 16],
+ [4, 144, 116, 4, 145, 117],
+ [17, 68, 42],
+ [17, 50, 22, 6, 51, 23],
+ [19, 46, 16, 6, 47, 17],
+ [2, 139, 111, 7, 140, 112],
+ [17, 74, 46],
+ [7, 54, 24, 16, 55, 25],
+ [34, 37, 13],
+ [4, 151, 121, 5, 152, 122],
+ [4, 75, 47, 14, 76, 48],
+ [11, 54, 24, 14, 55, 25],
+ [16, 45, 15, 14, 46, 16],
+ [6, 147, 117, 4, 148, 118],
+ [6, 73, 45, 14, 74, 46],
+ [11, 54, 24, 16, 55, 25],
+ [30, 46, 16, 2, 47, 17],
+ [8, 132, 106, 4, 133, 107],
+ [8, 75, 47, 13, 76, 48],
+ [7, 54, 24, 22, 55, 25],
+ [22, 45, 15, 13, 46, 16],
+ [10, 142, 114, 2, 143, 115],
+ [19, 74, 46, 4, 75, 47],
+ [28, 50, 22, 6, 51, 23],
+ [33, 46, 16, 4, 47, 17],
+ [8, 152, 122, 4, 153, 123],
+ [22, 73, 45, 3, 74, 46],
+ [8, 53, 23, 26, 54, 24],
+ [12, 45, 15, 28, 46, 16],
+ [3, 147, 117, 10, 148, 118],
+ [3, 73, 45, 23, 74, 46],
+ [4, 54, 24, 31, 55, 25],
+ [11, 45, 15, 31, 46, 16],
+ [7, 146, 116, 7, 147, 117],
+ [21, 73, 45, 7, 74, 46],
+ [1, 53, 23, 37, 54, 24],
+ [19, 45, 15, 26, 46, 16],
+ [5, 145, 115, 10, 146, 116],
+ [19, 75, 47, 10, 76, 48],
+ [15, 54, 24, 25, 55, 25],
+ [23, 45, 15, 25, 46, 16],
+ [13, 145, 115, 3, 146, 116],
+ [2, 74, 46, 29, 75, 47],
+ [42, 54, 24, 1, 55, 25],
+ [23, 45, 15, 28, 46, 16],
+ [17, 145, 115],
+ [10, 74, 46, 23, 75, 47],
+ [10, 54, 24, 35, 55, 25],
+ [19, 45, 15, 35, 46, 16],
+ [17, 145, 115, 1, 146, 116],
+ [14, 74, 46, 21, 75, 47],
+ [29, 54, 24, 19, 55, 25],
+ [11, 45, 15, 46, 46, 16],
+ [13, 145, 115, 6, 146, 116],
+ [14, 74, 46, 23, 75, 47],
+ [44, 54, 24, 7, 55, 25],
+ [59, 46, 16, 1, 47, 17],
+ [12, 151, 121, 7, 152, 122],
+ [12, 75, 47, 26, 76, 48],
+ [39, 54, 24, 14, 55, 25],
+ [22, 45, 15, 41, 46, 16],
+ [6, 151, 121, 14, 152, 122],
+ [6, 75, 47, 34, 76, 48],
+ [46, 54, 24, 10, 55, 25],
+ [2, 45, 15, 64, 46, 16],
+ [17, 152, 122, 4, 153, 123],
+ [29, 74, 46, 14, 75, 47],
+ [49, 54, 24, 10, 55, 25],
+ [24, 45, 15, 46, 46, 16],
+ [4, 152, 122, 18, 153, 123],
+ [13, 74, 46, 32, 75, 47],
+ [48, 54, 24, 14, 55, 25],
+ [42, 45, 15, 32, 46, 16],
+ [20, 147, 117, 4, 148, 118],
+ [40, 75, 47, 7, 76, 48],
+ [43, 54, 24, 22, 55, 25],
+ [10, 45, 15, 67, 46, 16],
+ [19, 148, 118, 6, 149, 119],
+ [18, 75, 47, 31, 76, 48],
+ [34, 54, 24, 34, 55, 25],
+ [20, 45, 15, 61, 46, 16],
+ ];
+ QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) {
+ var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
+ if (rsBlock == undefined) {
+ throw new Error(
+ "bad rs block @ typeNumber:" +
+ typeNumber +
+ "/errorCorrectLevel:" +
+ errorCorrectLevel
+ );
+ }
+ var length = rsBlock.length / 3;
+ var list = [];
+ for (var i = 0; i < length; i++) {
+ var count = rsBlock[i * 3 + 0];
+ var totalCount = rsBlock[i * 3 + 1];
+ var dataCount = rsBlock[i * 3 + 2];
+ for (var j = 0; j < count; j++) {
+ list.push(new QRRSBlock(totalCount, dataCount));
+ }
+ }
+ return list;
+ };
+ QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) {
+ switch (errorCorrectLevel) {
+ case QRErrorCorrectLevel.L:
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
+ case QRErrorCorrectLevel.M:
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
+ case QRErrorCorrectLevel.Q:
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
+ case QRErrorCorrectLevel.H:
+ return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
+ default:
+ return undefined;
+ }
+ };
+ function QRBitBuffer() {
+ this.buffer = [];
+ this.length = 0;
+ }
+ QRBitBuffer.prototype = {
+ get(index) {
+ var bufIndex = Math.floor(index / 8);
+ return ((this.buffer[bufIndex] >>> (7 - (index % 8))) & 1) == 1;
+ },
+ put(num, length) {
+ for (var i = 0; i < length; i++) {
+ this.putBit(((num >>> (length - i - 1)) & 1) == 1);
+ }
+ },
+ getLengthInBits() {
+ return this.length;
+ },
+ putBit(bit) {
+ var bufIndex = Math.floor(this.length / 8);
+ if (this.buffer.length <= bufIndex) {
+ this.buffer.push(0);
+ }
+ if (bit) {
+ this.buffer[bufIndex] |= 0x80 >>> this.length % 8;
+ }
+ this.length++;
+ },
+ };
+ var QRCodeLimitLength = [
+ [17, 14, 11, 7],
+ [32, 26, 20, 14],
+ [53, 42, 32, 24],
+ [78, 62, 46, 34],
+ [106, 84, 60, 44],
+ [134, 106, 74, 58],
+ [154, 122, 86, 64],
+ [192, 152, 108, 84],
+ [230, 180, 130, 98],
+ [271, 213, 151, 119],
+ [321, 251, 177, 137],
+ [367, 287, 203, 155],
+ [425, 331, 241, 177],
+ [458, 362, 258, 194],
+ [520, 412, 292, 220],
+ [586, 450, 322, 250],
+ [644, 504, 364, 280],
+ [718, 560, 394, 310],
+ [792, 624, 442, 338],
+ [858, 666, 482, 382],
+ [929, 711, 509, 403],
+ [1003, 779, 565, 439],
+ [1091, 857, 611, 461],
+ [1171, 911, 661, 511],
+ [1273, 997, 715, 535],
+ [1367, 1059, 751, 593],
+ [1465, 1125, 805, 625],
+ [1528, 1190, 868, 658],
+ [1628, 1264, 908, 698],
+ [1732, 1370, 982, 742],
+ [1840, 1452, 1030, 790],
+ [1952, 1538, 1112, 842],
+ [2068, 1628, 1168, 898],
+ [2188, 1722, 1228, 958],
+ [2303, 1809, 1283, 983],
+ [2431, 1911, 1351, 1051],
+ [2563, 1989, 1423, 1093],
+ [2699, 2099, 1499, 1139],
+ [2809, 2213, 1579, 1219],
+ [2953, 2331, 1663, 1273],
+ ];
+
+ var svgDrawer = (function() {
+ var Drawing = function(el, htOption) {
+ this._el = el;
+ this._htOption = htOption;
+ };
+
+ Drawing.prototype.draw = function(oQRCode) {
+ var _htOption = this._htOption;
+ var _el = this._el;
+ var nCount = oQRCode.getModuleCount();
+
+ this.clear();
+
+ function makeSVG(tag, attrs) {
+ var el = _el.ownerDocument.createElementNS(
+ "http://www.w3.org/2000/svg",
+ tag
+ );
+ for (var k in attrs) {
+ if (attrs.hasOwnProperty(k)) {
+ el.setAttribute(k, attrs[k]);
+ }
+ }
+ return el;
+ }
+
+ var svg = makeSVG("svg", {
+ viewBox: "0 0 " + String(nCount) + " " + String(nCount),
+ width: "100%",
+ height: "100%",
+ fill: _htOption.colorLight,
+ "shape-rendering": "crispEdges",
+ });
+ svg.setAttributeNS(
+ "http://www.w3.org/2000/xmlns/",
+ "xmlns",
+ "http://www.w3.org/2000/svg"
+ );
+ _el.appendChild(svg);
+
+ svg.appendChild(
+ makeSVG("rect", {
+ fill: _htOption.colorLight,
+ width: "100%",
+ height: "100%",
+ })
+ );
+
+ var path = [];
+ for (var row = 0; row < nCount; row++) {
+ for (var col = 0; col < nCount; col++) {
+ var width = 0;
+ while (col + width < nCount && oQRCode.isDark(row, col + width)) {
+ width++;
+ }
+
+ if (width > 0) {
+ path.push("M" + col + " " + row + "v1h" + width + "v-1z");
+ col += width;
+ }
+ }
+ }
+ var child = makeSVG("path", {
+ d: path.join(""),
+ fill: _htOption.colorDark,
+ });
+ svg.appendChild(child);
+ };
+ Drawing.prototype.clear = function() {
+ while (this._el.hasChildNodes()) {
+ this._el.removeChild(this._el.lastChild);
+ }
+ };
+ return Drawing;
+ })();
+
+ /**
+ * Get the type by string length
+ *
+ * @private
+ * @param {String} sText
+ * @param {Number} nCorrectLevel
+ * @return {Number} type
+ */
+ function _getTypeNumber(sText, nCorrectLevel) {
+ var nType = 1;
+ var length = _getUTF8Length(sText);
+
+ for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
+ var nLimit = 0;
+
+ switch (nCorrectLevel) {
+ case QRErrorCorrectLevel.L:
+ nLimit = QRCodeLimitLength[i][0];
+ break;
+ case QRErrorCorrectLevel.M:
+ nLimit = QRCodeLimitLength[i][1];
+ break;
+ case QRErrorCorrectLevel.Q:
+ nLimit = QRCodeLimitLength[i][2];
+ break;
+ case QRErrorCorrectLevel.H:
+ nLimit = QRCodeLimitLength[i][3];
+ break;
+ }
+
+ if (length <= nLimit) {
+ break;
+ } else {
+ nType++;
+ }
+ }
+
+ if (nType > QRCodeLimitLength.length) {
+ throw new Error("Too long data");
+ }
+
+ return nType;
+ }
+
+ function _getUTF8Length(sText) {
+ var replacedText = encodeURI(sText)
+ .toString()
+ .replace(/\%[0-9a-fA-F]{2}/g, "a");
+ return replacedText.length + (replacedText.length != sText ? 3 : 0);
+ }
+
+ /**
+ * @class QRCode
+ * @constructor
+ * @example
+ * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
+ *
+ * @example
+ * var oQRCode = new QRCode("test", {
+ * text : "http://naver.com",
+ * width : 128,
+ * height : 128
+ * });
+ *
+ * oQRCode.clear(); // Clear the QRCode.
+ * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
+ *
+ * @param {HTMLElement} el target element
+ * @param {Object|String} vOption
+ * @param {String} vOption.text QRCode link data
+ * @param {Number} [vOption.width=256]
+ * @param {Number} [vOption.height=256]
+ * @param {String} [vOption.colorDark="#000000"]
+ * @param {String} [vOption.colorLight="#ffffff"]
+ * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
+ */
+ QRCode = function(el, vOption) {
+ this._htOption = {
+ width: 256,
+ height: 256,
+ typeNumber: 4,
+ colorDark: "#000000",
+ colorLight: "#ffffff",
+ correctLevel: QRErrorCorrectLevel.H,
+ };
+
+ if (typeof vOption === "string") {
+ vOption = {
+ text: vOption,
+ };
+ }
+
+ // Overwrites options
+ if (vOption) {
+ for (var i in vOption) {
+ this._htOption[i] = vOption[i];
+ }
+ }
+
+ this._el = el;
+ this._oQRCode = null;
+ this._oDrawing = new svgDrawer(this._el, this._htOption);
+
+ if (this._htOption.text) {
+ this.makeCode(this._htOption.text);
+ }
+ };
+
+ /**
+ * Make the QRCode
+ *
+ * @param {String} sText link data
+ */
+ QRCode.prototype.makeCode = function(sText) {
+ this._oQRCode = new QRCodeModel(
+ _getTypeNumber(sText, this._htOption.correctLevel),
+ this._htOption.correctLevel
+ );
+ this._oQRCode.addData(sText);
+ this._oQRCode.make();
+ this._oDrawing.draw(this._oQRCode);
+ };
+
+ /**
+ * Clear the QRCode
+ */
+ QRCode.prototype.clear = function() {
+ this._oDrawing.clear();
+ };
+
+ /**
+ * @name QRCode.CorrectLevel
+ */
+ QRCode.CorrectLevel = QRErrorCorrectLevel;
})();
1
0
[tor-browser-build/master] Bug 40431: added license info for edwards25519 and edwards25519-extra
by boklm@torproject.org 14 Feb '22
by boklm@torproject.org 14 Feb '22
14 Feb '22
commit f78de76b8f00cbee0f8b6fcbfe6bab0634577210
Author: Pier Angelo Vendrame <pierov(a)torproject.org>
Date: Mon Feb 14 18:19:34 2022 +0100
Bug 40431: added license info for edwards25519 and edwards25519-extra
---
.../Docs/Licenses/PluggableTransports/LICENSE | 44 ++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE b/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE
index f0e7cdb..1b3aeee 100644
--- a/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE
+++ b/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE
@@ -203,6 +203,50 @@ warranty. See LICENSE.CC0.
===============================================================================
+filippo.io/edwards25519
+
+Copyright 2009 The Go Authors. All rights reserved.
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file.
+
+For details about the Go License, please see LICENSE.GO.
+
+===============================================================================
+
+yawning/edwards25519-extra
+
+Copyright (c) 2021 Oasis Labs Inc. All rights reserved.
+Copyright (c) 2021 Yawning Angel. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+===============================================================================
+
xtaci/kcp-go
The MIT License (MIT)
1
0
14 Feb '22
commit 4d63b6e7addc2e4f095ebca712973b08c7b381cd
Author: Nicolas Vigier <boklm(a)torproject.org>
Date: Mon Feb 14 17:17:10 2022 +0100
Bug 40422: Remove projects/ed25519
---
projects/ed25519/config | 19 -------------------
.../Docs/Licenses/PluggableTransports/LICENSE | 10 ----------
2 files changed, 29 deletions(-)
diff --git a/projects/ed25519/config b/projects/ed25519/config
deleted file mode 100644
index c6790b8..0000000
--- a/projects/ed25519/config
+++ /dev/null
@@ -1,19 +0,0 @@
-# vim: filetype=yaml sw=2
-version: '[% c("abbrev") %]'
-git_url: https://github.com/agl/ed25519.git
-git_hash: c4161f4c7483313562781c61b9a20aba73daf9de
-filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.gz'
-
-build: '[% c("projects/go/var/build_go_lib") %]'
-
-var:
- container:
- use_container: 1
- go_lib: github.com/agl/ed25519
- go_lib_install:
- - github.com/agl/ed25519/extra25519
-
-input_files:
- - project: container-image
- - name: go
- project: go
diff --git a/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE b/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE
index fd0dbd6..f0e7cdb 100644
--- a/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE
+++ b/projects/tor-browser/Bundle-Data/Docs/Licenses/PluggableTransports/LICENSE
@@ -194,16 +194,6 @@ For details about the Go License, please see LICENSE.GO.
===============================================================================
-agl/ed25519
-
-Copyright 2013 The Go Authors. All rights reserved.
-Use of this source code is governed by a BSD-style
-license that can be found in the LICENSE file.
-
-For details about the Go License, please see LICENSE.GO.
-
-===============================================================================
-
dchest/siphash
To the extent possible under law, the authors have dedicated all
1
0
[tor-browser-build/maint-11.0] Bug 40400: Add tools/changelog-format-blog-post
by boklm@torproject.org 14 Feb '22
by boklm@torproject.org 14 Feb '22
14 Feb '22
commit 35bf019cae0bcf2df56e74e63308cc8b815cddc7
Author: Nicolas Vigier <boklm(a)torproject.org>
Date: Tue Dec 14 13:20:23 2021 +0100
Bug 40400: Add tools/changelog-format-blog-post
---
tools/changelog-format-blog-post | 53 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/tools/changelog-format-blog-post b/tools/changelog-format-blog-post
new file mode 100755
index 0000000..a50ae8f
--- /dev/null
+++ b/tools/changelog-format-blog-post
@@ -0,0 +1,53 @@
+#!/usr/bin/perl -w
+
+# This script reads the ChangeLog.txt file and outputs it to stdout
+# in the format for the blog post.
+
+use strict;
+use FindBin;
+
+sub version_type {
+ return $_[0] =~ 'a' ? 'alpha' : 'release';
+}
+
+my ($changelog, $current_tbversion, $last_tbversion);
+
+if (!open(CHANGELOG, '<', "$FindBin::Bin/../ChangeLog.txt")) {
+ print STDERR "Error opening changelog file\n";
+ exit 1;
+}
+
+foreach (<CHANGELOG>) {
+ if (m/^Tor Browser ([^\s]+) -/) {
+ if ($current_tbversion) {
+ $last_tbversion = $1;
+ last if version_type($current_tbversion) eq version_type($last_tbversion);
+ next;
+ }
+ $current_tbversion = $1;
+ next;
+ }
+
+ next if $last_tbversion;
+
+ # Remove one space at the begining of all lines
+ s/^\s//;
+
+ # Replace '*' by '-'
+ s/^(\s*)\*/$1-/;
+
+ s/&/&/; s/</</; s/>/>/;
+
+ # Change bug numbers to links
+ s|Bug (\d+): ([^\[]+) \[([^\]]+)\]|[Bug $3#$1](https://gitlab.torproject.org/tpo/applications/$3/-/issues/$1): $2|;
+
+ $changelog .= $_;
+}
+
+my $changelog_branch = 'master';
+if (! ( $current_tbversion =~ m/a/ ) ) {
+ my @v = split(/\./, $current_tbversion);
+ $changelog_branch = "maint-$v[0].$v[1]";
+}
+print "The full changelog since [Tor Browser $last_tbversion](https://gitweb.torproject.org/builders/tor-browser-build.git/plain/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt?h=$changelog_branch) is:\n\n";
+print $changelog;
1
0
14 Feb '22
commit baad54b37dc0f30500a4993fe26e31876def03c4
Author: aguestuser <aguestuser(a)torproject.org>
Date: Fri Feb 11 11:09:34 2022 -0500
Bug 40430: Update allowed_addons.json
context
- in aa65127f (prep for 11.5a4 release) we bumped noscript from
version 11.2.4 to 11.2.9 in `projects/tor-browser/config` but not in
`projects/tor-browser/allowed_addons.json`, causing nightly builds to break
change
- here, we update `allowed_addons.json` to match the version and hash
specified in `config` (after `wget`ing the `xpi` in question and
manually verifying that the hash is correct)
---
projects/tor-browser/allowed_addons.json | 221 ++++++++++++++++---------------
projects/tor-browser/config | 4 +-
2 files changed, 114 insertions(+), 111 deletions(-)
diff --git a/projects/tor-browser/allowed_addons.json b/projects/tor-browser/allowed_addons.json
index 852f4e4..1305287 100644
--- a/projects/tor-browser/allowed_addons.json
+++ b/projects/tor-browser/allowed_addons.json
@@ -17,7 +17,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/13/13299/13299734.png?modifi…"
}
],
- "average_daily_users": 720637,
+ "average_daily_users": 776142,
"categories": {
"android": [
"experimental",
@@ -31,7 +31,7 @@
"contributions_url": "https://opencollective.com/darkreader?utm_content=product-page-contribute&u…",
"created": "2017-09-19T07:03:00Z",
"current_version": {
- "id": 5333770,
+ "id": 5360273,
"compatibility": {
"firefox": {
"min": "54.0",
@@ -42,7 +42,7 @@
"max": "*"
}
},
- "edit_url": "https://addons.mozilla.org/en-US/developers/addon/darkreader/versions/53337…",
+ "edit_url": "https://addons.mozilla.org/en-US/developers/addon/darkreader/versions/53602…",
"is_strict_compatibility_enabled": false,
"license": {
"id": 22,
@@ -53,22 +53,22 @@
"url": "http://www.opensource.org/licenses/mit-license.php"
},
"release_notes": {
- "en-US": "- Dynamic mode bug fixes.\n- Users' fixes for websites."
+ "en-US": "- Revert reducing brightness for images (due to some performance issues).\n- Users' fixes for websites."
},
"reviewed": null,
- "version": "4.9.43",
+ "version": "4.9.45",
"files": [
{
- "id": 3878116,
- "created": "2021-12-07T13:18:32Z",
- "hash": "sha256:390bdc2609b43627b58e47bb3a3c2f42b156f16bd932b93746196615134db0b1",
+ "id": 3904618,
+ "created": "2022-02-05T17:44:16Z",
+ "hash": "sha256:7df6f5a6c07d3740ed26be3c24fdd1719b77e685506e2642f756ce3538b3cc7f",
"is_restart_required": false,
"is_webextension": true,
"is_mozilla_signed_extension": false,
"platform": "all",
- "size": 585173,
+ "size": 592244,
"status": "public",
- "url": "https://addons.mozilla.org/firefox/downloads/file/3878116/dark_reader-4.9.4…",
+ "url": "https://addons.mozilla.org/firefox/downloads/file/3904618/dark_reader-4.9.4…",
"permissions": [
"alarms",
"contextMenus",
@@ -143,7 +143,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2021-12-07T16:09:38Z",
+ "last_updated": "2022-02-07T12:07:52Z",
"name": {
"ar": "Dark Reader",
"bn": "Dark Reader",
@@ -216,10 +216,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.5576,
- "bayesian_average": 4.556315606540176,
- "count": 4141,
- "text_count": 1330
+ "average": 4.5591,
+ "bayesian_average": 4.557826525105629,
+ "count": 4216,
+ "text_count": 1355
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/reviews/",
"requires_payment": false,
@@ -310,7 +310,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/versions/",
- "weekly_downloads": 24544
+ "weekly_downloads": 25706
},
"notes": null
},
@@ -326,7 +326,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/5/5474/5474073.png?modified=…"
}
],
- "average_daily_users": 690147,
+ "average_daily_users": 730697,
"categories": {
"android": [
"security-privacy"
@@ -459,10 +459,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.6651,
- "bayesian_average": 4.66260537524599,
- "count": 2189,
- "text_count": 436
+ "average": 4.6633,
+ "bayesian_average": 4.6608034421408995,
+ "count": 2207,
+ "text_count": 440
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/https-everywhere/reviews/",
"requires_payment": false,
@@ -491,7 +491,7 @@
"type": "extension",
"url": "https://www.eff.org/https-everywhere",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/https-everywhere/versions/",
- "weekly_downloads": 11296
+ "weekly_downloads": 11823
},
"notes": null
},
@@ -507,7 +507,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/6/6937/6937656.png?modified=…"
}
],
- "average_daily_users": 224180,
+ "average_daily_users": 238294,
"categories": {
"android": [
"security-privacy"
@@ -519,7 +519,7 @@
"contributions_url": "",
"created": "2014-06-10T05:46:02Z",
"current_version": {
- "id": 5289638,
+ "id": 5357809,
"compatibility": {
"firefox": {
"min": "56.0a1",
@@ -530,7 +530,7 @@
"max": "*"
}
},
- "edit_url": "https://addons.mozilla.org/en-US/developers/addon/decentraleyes/versions/52…",
+ "edit_url": "https://addons.mozilla.org/en-US/developers/addon/decentraleyes/versions/53…",
"is_strict_compatibility_enabled": false,
"license": {
"id": 3338,
@@ -541,23 +541,22 @@
"url": "http://www.mozilla.org/MPL/2.0/"
},
"release_notes": {
- "en-US": "<i>New features:</i>\n\n- Added support for preconfigured enterprise policies.\n\n<i>New languages:</i>\n\n- Added support for the Albanian language.\n- Added partial support for the Sinhala language.\n- Added partial support for the Punjabi language.\n\n<i>Enhancements:</i>\n\n- Improved render performance of the popup panel.\n- Implemented preference prioritization logic.\n- Added additional resources to the staging environment.\n- Added dark mode-support to the welcome page.\n\n<i>Other changes:</i>\n\n- Applied a minor security update to the audit tool.\n- Applied various performance optimizations and stability improvements.\n- Improved and extended various existing localizations.",
- "nl": ""
+ "en-US": "<i>New languages:</i>\n\n- Added support for the Vietnamese language.\n\n<i>Enhancements:</i>\n\n- Improved popup panel display logic.\n\n<i>Bugfixes:</i>\n\n- Restored support for older versions of Firefox.\n\n<i>Other changes:</i>\n\n- Improved and extended various existing localizations.\n- Applied a minor design-related change to the options page.\n- Replaced remaining calls to a deprecated function."
},
"reviewed": null,
- "version": "2.0.16",
+ "version": "2.0.17",
"files": [
{
- "id": 3833987,
- "created": "2021-09-02T02:21:27Z",
- "hash": "sha256:fa87ceac1242c26a40c07e31be62d519c4e2cd6ffb5c1f83248cc924766465c9",
+ "id": 3902154,
+ "created": "2022-01-30T20:08:38Z",
+ "hash": "sha256:e7f16ddc458eb2bc5bea75832305895553fca53c2565b6f1d07d5d9620edaff1",
"is_restart_required": false,
"is_webextension": true,
"is_mozilla_signed_extension": false,
"platform": "all",
- "size": 6939989,
+ "size": 6941339,
"status": "public",
- "url": "https://addons.mozilla.org/firefox/downloads/file/3833987/decentraleyes-2.0…",
+ "url": "https://addons.mozilla.org/firefox/downloads/file/3902154/decentraleyes-2.0…",
"permissions": [
"*://*/*",
"privacy",
@@ -603,6 +602,7 @@
"sv-SE": "Webbplatser har alltmer börjat förlita sig mycket på tredjeparter för att tillhandahålla material. Avbryta förfrågningar från annonser eller trackers går vanligtvis utan problem, men att blockera sidans innehåll kan, inte ovÀntat, få sidor att sluta fungera. Syftet med detta tillÀgg Àr att ta bort mellanhanden genom att tillhandahålla blixtsnabb leverans av lokala (paketerade) filer för att förbÀttra integritet på nÀtet.\n\n     ⢠Skyddar integriteten genom att undvika stora leveransnÀtverk som hÀvdar att de erbjuder gratis tjÀnster.\n     ⢠Kompletterar vanliga blockerare som uBlock Origin (rekommenderas), Adblock Plus, med flera.\n     ⢠Fungerar direkt ur lådan; absolut ingen tidigare konfiguration krÀvs.\n\n<i>Obs: Decentraleyes Àr ingen dunderkur, men det hindrar en hel del webbplatser från att få dig att skicka dessa typer av förfrågningar. I slutÀndan kan du göra Decentraleyes blockera förfrågningar om
eventuella saknade CDN-resurser ocksÃ¥.</i>\n\n   > Enklare introduktion: <a href=\"https://outgoing.prod.mozaws.net/v1/d22d502c3b43fb2f96a0310a4f001f4745aba6c…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction</a>\n\n<b>Ãr jag för nÀrvarande skyddad?</b>\n\nFöljande <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">testverktyget</a> visar om du Àr ordentligt skyddad. Det Àr det rekommenderade och förmodligen det snabbaste sÀttet att bekrÀfta att tillÀgget Àr installerat, aktiverat och korrekt konfigurerat.\n\n   > LÀnk till testverktyg: <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">https://decentraley
es.org/test</a>\n\n<b>Vanliga frÃ¥gor</b>\n\n   > LÀnk till vanliga frÃ¥gor: <a href=\"https://outgoing.prod.mozaws.net/v1/1a6f38b165b3e86d666a5f770656e66788a71b6…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Frequently-Asked-Questions</a>\n\n<b>Teknisk information</b>\n\n- NÀtverk som stöds: Google Hosted Libraries, Microsoft Ajax CDN, CDNJS (Cloudflare), jQuery CDN (MaxCDN), jsDelivr (MaxCDN), Yandex CDN, Baidu CDN, Sina Public Resources och UpYun Libraries.\n\n- Samlade resurser: AngularJS, Backbone.js, Dojo, Ember.js, Ext Core, jQuery, jQuery UI, Modernizr, MooTools, Prototype, Scriptaculous, SWFObject, Underscore.js och Web Font Loader.\n\n<b>Personlig support</b>\n\nÃr din frÃ¥ga inte listad ovan, eller har du nÃ¥gon annan anledning att kontakta mig personligen? Du kan alltid nÃ¥ mig pÃ¥ <a href=\"/\" rel=\"nofollow\">decentraleyes(a)protonmail.c
om</a>. Felrapporter och förslag vÀlkomnas varmt och jag svarar pÃ¥ alla e-postmeddelanden.\n\nAnvÀnd gÀrna <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">den hÀr publika PGP-nyckeln</a> för krypterad kommunikation.\n\n   > LÀnk till publik PGP-nyckel: <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">https://decentraleyes.org/3f774aff6d/public-key.txt</a>\n\n<b>Uppskattar du detta tillÀgg?</b>\n\nDecentraleyes Àr och kommer alltid att vara gratis. Du kan stödja dess fortsatta utveckling genom att ge en donation, genom att hjÀlpa till pÃ¥ GitLab eller bara sprida kunskap om tillÀgget (vilket gör under). All hjÀlp uppskattas hjÀrtligt!\n\n     ⢠GitLab: <a href=\"https://outgoing.prod.mozaw
s.net/v1/26434c016cfe12000d62fedab907c037c0263b2e24b6351ccf2f6343cf5759b9/https%3A//git.synz.io/Synzvato/decentraleyes\" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes</a>\n     ⢠Donera: <a href=\"https://outgoing.prod.mozaws.net/v1/d41865bc77319d73d03fa7198c7d0a8a9effcbd…" rel=\"nofollow\">https://decentraleyes.org/donate</a>",
"tr": "Web siteleri giderek daha çok bÃŒyÃŒk ÌçÌncÃŒ taraf içerik daÄıtıcılarına itibar etmeye baÅladı. Reklamlar veya iz sÃŒrÃŒcÃŒler için istekleri engellemek genellikle sorunsuzdur, fakat gerçek içeriÄi engellemek, bekleneceÄi ÃŒzere, sayfaların iÅleyiÅini bozmaktadır. Bu eklentinin amacı çevrimiçi mahremiyeti artırmak için yerel (bÃŒtÃŒnleÅik) dosyaları ıÅık hızında sunarak aracıları aradan çıkarmaktır.\n\n     ⢠Ãcretsiz hizmet sunduÄunu iddia eden bÃŒyÃŒk daÄıtım aÄlarını aradan çıkararak mahremiyeti korur.\n     ⢠uBlock Origin (önerilir), Adblock Plus, vb. gibi olaÄan engelleyicileri tamamlar.\n     ⢠Kutudan çıktıÄı gibi çalıÅır; ön ayarlamaya ihtiyaç duymaz.\n\n<i>Not: Decentraleyes sihirli çözÃŒm deÄildir, ama birçok web sitesinin sizi bu istekleri göndermeye zorlamasını engeller. Son olarak, Decentraleyes'ın herhangi eksik bir CDN kaynaÄı için istekleri engellemesini de
saÄlayabilirsiniz.</i>\n\n   > Basit tanıtım: <a href=\"https://outgoing.prod.mozaws.net/v1/d22d502c3b43fb2f96a0310a4f001f4745aba6c…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction</a>\n\n<b>Åu Anda Korunuyor Muyum?</b>\n\nAÅaÄıdaki <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">test yardımcı programı</a> dÃŒzgÃŒn Åekilde korunmuÅ iseniz size gösterir. Bu, eklentinin yÃŒklÃŒ, etkin ve doÄru yapılandırılmıŠolup olmadıÄını görmek için önerilen ve muhtemelen en hızlı yol budur.\n\n   > Test yardımcı programı için tam baÄlantı: <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">https
://decentraleyes.org/test</a>\n\n<b>Sıkça Sorulan Sorular</b>\n\n   > SSS için tam baÄlantı: <a href=\"https://outgoing.prod.mozaws.net/v1/1a6f38b165b3e86d666a5f770656e66788a71b6…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Frequently-Asked-Questions</a>\n\n<b>Teknik bilgi</b>\n\n- Desteklenen AÄlar: Google Tarafından Barındırılan KÃŒtÃŒphaneler, Microsoft Ajax CDN, CDNJS (Cloudflare), jQuery CDN (MaxCDN), jsDelivr (MaxCDN), Yandex CDN, Baidu CDN, Sina Kamusal Kaynaklar ve UpYun KÃŒtÃŒphaneleri.\n\n- BÃŒtÃŒnleÅik Kaynaklar: AngularJS, Backbone.js, Dojo, Ember.js, Ext Core, jQuery, jQuery UI, Modernizr, MooTools, Prototype, Scriptaculous, SWFObject, Underscore.js ve Web Font YÃŒkleyici.\n\n<b>KiÅisel Destek</b>\n\nSorunuz yukarıda listelenenlerden biri deÄil mi, veya Åahsen bana ulaÅmak için baÅka bir sebebiniz var mı? Her zaman bana b
uradan ulaÅabilirsiniz <a href=\"/\" rel=\"nofollow\">decentraleyes(a)protonmail.com</a>. Hata raporlarına veya önerilere çok açıÄım ve her bir e-postayı yanıtlıyorum.\n\nKullanmaktan çekinmeyin <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">bu genel PGP anahtarı</a> Åifreli iletiÅim içindir.\n\n   > Genel PGP anahtarına tam baÄlantı: <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">https://decentraleyes.org/3f774aff6d/public-key.txt</a>\n\n<b>Bu Eklenti HoÅunuza Gitti Mi?</b>\n\nDecentraleyes ÃŒcretsizdir ve her zaman öyle kalacaktır, ama sÃŒrekli geliÅtirilmesini bir baÄıÅı yaparak, GitLab ÃŒzerinde katkı sunarak veya onu yayarak (ki harikalar yaratabilir) destekleyebilirsiniz. Herh
angi bir yardımdan bÃŒyÃŒk mutluluk duyacaÄız!\n\n     ⢠GitLab: <a href=\"https://outgoing.prod.mozaws.net/v1/26434c016cfe12000d62fedab907c037c0263b2…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes</a>\n     ⢠BaÄıŠyap: <a href=\"https://outgoing.prod.mozaws.net/v1/d41865bc77319d73d03fa7198c7d0a8a9effcbd…" rel=\"nofollow\">https://decentraleyes.org/donate</a>",
"uk": "ÐебÑайÑО ÐŽÐµÐŽÐ°Ð»Ñ ÑаÑÑÑÑе пПклаЎаÑÑÑÑÑ ÐœÐ° велОкÑ, ÑÑПÑÐŸÐœÐœÑ ÑеÑвÑÑО пПÑÑаÑÐ°ÐœÐœÑ ÐºÐŸÐœÑеМÑÑ. РблПкÑваММÑÐŒ ÑеклаЌО ÑО запОÑÑв ÑÑÐµÐ¶ÐµÐœÐœÑ Ð·Ð°Ð·Ð²ÐžÑай Ме Ð²ÐžÐœÐžÐºÐ°Ñ Ð¿ÑПблеЌ, ПЎМак ÑÑлÑÑÑÑÐ²Ð°ÐœÐœÑ ÐºÐŸÐœÑеМÑÑ ÐŽÐ»Ñ Ð²ÑЎПбÑÐ°Ð¶ÐµÐœÐœÑ ÑÑПÑÑМПк ПÑевОЎМП Ð»Ð°ÐŒÐ°Ñ Ð¿ÑаÑезЎаÑМÑÑÑÑ ÑайÑÑв. ЊÑÐ»Ð»Ñ ÑÑПгП ЎПЎаÑÐºÑ Ñ Ð¿ÑОбÑаÑО пПÑеÑеЎМОкÑв, МаÑПЌÑÑÑÑ ÐŒÐžÑÑÑвП пПÑÑавлÑÑÑО ÑеÑÑÑÑО ÐŽÐ»Ñ Ð²ÑЎПбÑÐ°Ð¶ÐµÐœÐœÑ ÑайÑÑв, ÑÐºÑ Ð·Ð±ÐµÑÑгаÑÑÑÑÑ Ð»ÐŸÐºÐ°Ð»ÑМП Ма кПЌп'ÑÑеÑÑ, ÑП в ÑÐ²ÐŸÑ ÑеÑÐ³Ñ Ð¿ÐŸÐºÑаÑÑÑ Ð²Ð°ÑÑ Ð¿ÑОваÑМÑÑÑÑ Ð² ЌеÑежÑ.\n\n     ⢠ÐаÑ
ОÑÐ°Ñ Ð¿ÑОваÑМÑÑÑÑ ÑлÑÑ
ПЌ ÑÐœÐžÐºÐ°ÐœÐœÑ Ð²ÐµÐ»ÐžÐºÐžÑ
ЌеÑеж ЎПпÑÐ°Ð²Ð»ÐµÐœÐœÑ Ñ ÑПзпÐ
ŸÐ²ÑÑÐŽÐ¶ÐµÐœÐœÑ ÐºÐŸÐœÑеМÑÑ (CDN), ÑÐºÑ Ð·Ð°ÑвлÑÑÑÑ, ÑП ÑÑ
ÐœÑ ÑеÑвÑÑО бÑÑÑÐŒÑП безкПÑÑПвМÑ.\n     ⢠ÐПпПвМÑÑ Ð·Ð²ÐžÑÐ°Ð¹ÐœÑ Ð±Ð»ÐŸÐºÑвалÑМОкО, ÑÐ°ÐºÑ Ñк: uBlock Origin (ÑекПЌеМЎПваМП), Adblok Plus ÑПÑП.\n     ⢠ÐÑаÑÑÑ ÐŸÐŽÑÐ°Ð·Ñ Ð· кПÑПбкО; Ме пПÑÑебÑÑ Ð¶ÐŸÐŽÐœÐžÑ
пПпеÑеЎМÑÑ
МалаÑÑÑваМÑ.\n\n<i>ÐÑОЌÑÑка: Decentraleyes Ме Ñ ÑÑÑÐ±ÐœÐŸÑ ÐºÑлеÑ, але вÑМ забПÑПМÑÑ Ð±Ð°Ð³Ð°ÑÑПЌ вебÑайÑаЌ ÑПбОÑО вОÑеПпОÑÐ°ÐœÑ Ð·Ð°Ð¿ÐžÑО. ÐÑеÑÑПÑ, вО ЌПжеÑе МалаÑÑÑваÑО Decentraleyes блПкÑваÑО ÑÑÑ Ð·Ð°Ð¿ÐžÑО ЎП CDN, МавÑÑÑ ÑкÑП ÑеÑÑÑÑО вÑÐŽÑÑÑÐœÑ Ð»ÐŸÐºÐ°Ð»ÑМП.</i>\n\n   > ÐÑПÑÑÑÑе ПзМайПЌлеММÑ: <a href=\"https://outgoing.prod.mozaws.net/v1/d22d502c3b43fb2f96a0310a4f001f4745aba6c…
s%3A//git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction\" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction</a>\n\n<b>ЧО заÑ
ОÑеМОй Ñ Ð·Ð°Ñаз?</b>\n\nÐаÑÑÑпМа <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">ÑеÑÑПва ÑÑОлÑÑа</a> пПкаже ваЌ ÑО вО ЎПÑÑаÑМÑП заÑ
ОÑеМÑ. Ње ÑекПЌеМЎПваМОй Ñ, ЌПжлОвП, МайÑвОЎÑОй ÑпПÑÑб пеÑевÑÑОÑО ÑО Ñей ЎПЎаÑПк вÑÑаМПвлеМОй, акÑОвМОй Ñ ÐºÐŸÑекÑМП МалаÑÑПваМОй.\n\n   > ÐПвМе пПÑÐžÐ»Ð°ÐœÐœÑ ÐœÐ° ÑеÑÑÐŸÐ²Ñ ÑÑОлÑÑÑ: <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">https://decentraleyes.org/test</a>\n
\n<b>ЧаÑÑÑ Ð·Ð°Ð¿ÐžÑаММÑ</b>\n\n   > ÐПвМе пПÑÐžÐ»Ð°ÐœÐœÑ ÐœÐ° ÑаÑÑÑ Ð¿ÐžÑаММÑ: <a href=\"https://outgoing.prod.mozaws.net/v1/1a6f38b165b3e86d666a5f770656e66788a71b6…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Frequently-Asked-Questions</a>\n\n<b>ТеÑ
МÑÑМа ÑМÑПÑЌаÑÑÑ</b>\n\n- ÐеÑежÑ, ÑП пÑÐŽÑÑОЌÑÑÑÑÑÑ: Google Hosted Libraries, Microsoft Ajax CDN, CDNJS (Cloudflare), jQuery CDN (MaxCDN), jsDelivr (MaxCDN), Yandex CDN, Baidu CDN, Sina Public Resources, Ñа UpYun Libraries.\n\n- ÐМÑегÑÐŸÐ²Ð°ÐœÑ ÑеÑÑÑÑО: AngularJS, Backbone.js, Dojo, Ember.js, Ext Core, jQuery, jQuery UI, Modernizr, MooTools, Prototype, Scriptaculous, SWFObject, Underscore.js, Ñа Web Font Loader.\n\n<b>ÐеÑÑПМалÑМа пÑÐŽÑÑОЌка</b>\n\nÐÑЎпПвÑÐŽÑ ÐœÐ° ваÑе пОÑÐ°ÐœÐœÑ ÐœÐµÐŒÐ°Ñ
в ÑекÑÑÑ Ð²ÐžÑе абП Ñ Ð²Ð°Ñ Ñ Ð±ÑÐŽÑ-ÑÐºÑ ÑМÑÑ Ð¿ÑОÑОМО зв'ÑзаÑОÑÑ Ð·Ñ ÐŒÐœÐŸÑ ÐŸÑПбОÑÑП? Ð¢ÐŸÐŽÑ Ð²Ðž ЌПжеÑе Ñе зÑПбОÑО пП ÑÑй аЎÑеÑÑ <a href=\"/\" rel=\"nofollow\">decentraleyes(a)protonmail.com</a>. ÐПвÑÐŽÐŸÐŒÐ»ÐµÐœÐœÑ Ð¿ÑП пПЌОлкО абП пÑПпПзОÑÑÑ ÑОÑП вÑÑаÑÑÑÑÑ, Ñ Ð²ÑЎпПвÑÐŽÐ°Ñ ÐœÐ° кПжеМ ПÑÑОЌаМОй лОÑÑ.\n\nÐе вагайÑеÑÑ Ð²ÐžÐºÐŸÑОÑÑПвÑваÑО <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">Ñей пÑблÑÑМОй PGP клÑÑ</a> ÐŽÐ»Ñ Ð·Ð°ÑОÑÑПваМПгП звеÑМеММÑ.\n\n   > ÐПвМе пПÑÐžÐ»Ð°ÐœÐœÑ ÐœÐ° пÑблÑÑМОй PGP клÑÑ: <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…
aleyes.org/3f774aff6d/public-key.txt\" rel=\"nofollow\">https://decentraleyes.org/3f774aff6d/public-key.txt</a>\n\n<b>ÐаЌ пПЎПбаÑÑÑÑÑ Ñей ЎПЎаÑПк?</b>\n\nDecentraleyes Ñ Ñ Ð·Ð°Ð²Ð¶ÐŽÐž бÑЎе безкПÑÑПвМОЌ, пÑПÑе вО ЌПжеÑе пÑÐŽÑÑОЌаÑО йПгП МевпОММОй пÑПÑÐµÑ ÑПзÑПбкО ÑлÑÑ
ПЌ гÑПÑПвПгП вМеÑкÑ, пÑОйМÑвÑО ÑÑаÑÑÑ Ñ ÑПзÑПбÑÑ ÐœÐ° GitLab, абП пПÑОÑÑÑÑО Ñей ЎПЎаÑПк ÑеÑеЎ ÑМÑОÑ
лÑЎей (ÑП ÑПбОÑÑ ÐŽÐžÐ²Ð°). ЩОÑа вЎÑÑМÑÑÑÑ Ð·Ð° бÑÐŽÑ-ÑÐºÑ ÐŽÐŸÐ¿ÐŸÐŒÐŸÐ³Ñ!\n\n     ⢠GitLab: <a href=\"https://outgoing.prod.mozaws.net/v1/26434c016cfe12000d62fedab907c037c0263b2…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes</a>\n     ⢠ÐÑÐŽÑÑОЌаÑО: <a href=\"https://outgoing.prod.mozaws.net/v1/d41865bc77319d73d03fa719
8c7d0a8a9effcbd09b31c9f48af1c75b3f23f8a6/https%3A//decentraleyes.org/donate\" rel=\"nofollow\">https://decentraleyes.org/donate</a>",
+ "vi": "Các trang web Äang ngà y cà ng phụ thuá»c nhiá»u hÆ¡n và o các bên thứ ba lá»n cho viá»c cung cấp ná»i dung. Viá»c há»§y yêu cầu cho quảng cáo hoặc trình theo dõi thưá»ng khÃŽng gặp vấn Äá» gì, tuy nhiên viá»c chặn hẳn ná»i dung, như Äã Äoán, là m há»ng các trang. Mục ÄÃch cá»§a tiá»n Ãch nà y là Äá» cắt bá» phÃa trung gian bằng cách cung cấp nhanh chóng các (gói) tá»p cục bá» nhằm cải thiá»n quyá»n riêng tư trá»±c tuyến.\n\n     ⢠Bảo vá» quyá»n riêng tư bằng cách né tránh các mạng lưá»i phân phá»i lá»n mà tá»± nháºn rằng há» cung cấp dá»ch vụ miá»
n phÃ.\n     ⢠Bá» sung các phần má»m chặn thÃŽng thưá»ng như uBlock Origin (nên dùng), Adblock Plus, v.v.\n     ⢠Cứ thế hoạt Äá»ng thÃŽi; hoà n toà n khÃŽng cần cà i chá»nh thêm gì.\n\n<i>Ghi chú: Decentraleyes khÃŽng phải cây gáºy như Ü, nhưng nó vẫn
ngÄn chặn rất nhiá»u trang web khá»i viá»c bắt bạn gá»i các loại yêu cầu nà y. Rá»t cuá»c thì, bạn vẫn có thá» khiến Decentraleyes chặn các yêu cầu cho bất cứ tà i nguyên CDN bá» thiếu nà o.</i>\n\n   > Giá»i thiá»u ÄÆ¡n giản: <a href=\"https://outgoing.prod.mozaws.net/v1/d22d502c3b43fb2f96a0310a4f001f4745aba6c…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction</a>\n\n<b>TÃŽi có Äang ÄÆ°á»£c bảo vá» khÃŽng?</b>\n\n Äá» xem bạn có ÄÆ°á»£c bảo vá» Äúng cách hay khÃŽng, hãy dùng <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">cÃŽng cụ thá»</a> sau Äây. Äây là cách ÄÆ°á»£c khuyến nghá», và có lẜ cÅ©ng là cách nhanh nhất Äá» xem liá»u tiá»n Ãch
bá» sung nà y Äã ÄÆ°á»£c cà i Äặt, ÄÆ°á»£c báºt vÃ ÄÆ°á»£c cấu hình Äúng hay chưa.\n\n   > ÄÆ°á»ng dẫn tá»i cÃŽng cụ thá»: <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">https://decentraleyes.org/test</a>\n\n<b>Câu há»i thưá»ng gặp</b>\n\n   > ÄÆ°á»ng dẫn tá»i các câu há»i thưá»ng gặp (FAQ): <a href=\"https://outgoing.prod.mozaws.net/v1/1a6f38b165b3e86d666a5f770656e66788a71b6…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Frequently-Asked-Questions</a>\n\n<b>ThÃŽng tin kỹ thuáºt</b>\n\n- Mạng ÄÆ°á»£c há» trợ: Google Hosted Libraries, Microsoft Ajax CDN, CDNJS (Cloudflare), jQuery CDN (MaxCDN), jsDelivr (MaxCDN), Yandex CDN, Baidu CDN, Sina Public Resources, và UpYun Libraries.\n\n-
Gói tà i nguyên: AngularJS, Backbone.js, Dojo, Ember.js, Ext Core, jQuery, jQuery UI, Modernizr, MooTools, Prototype, Scriptaculous, SWFObject, Underscore.js, và Web Font Loader.\n\n<b>Há» trợ cá nhân</b>\n\nCâu há»i cá»§a bạn khÃŽng ÄÆ°á»£c liá»t kê á» trên, hoặc bạn có lÜ do nà o khác muá»n liên há» vá»i tÃŽi? Bạn luÃŽn có thá» liên há» tại <a href=\"/\" rel=\"nofollow\">decentraleyes(a)protonmail.com</a>. Các báo cáo lá»i hoặc Äá» xuất Äá»u rất ÄÆ°á»£c hoan nghênh, và tÃŽi cÅ©ng sẜ trả lá»i từng email.\n\nBạn có thá» thoải mái sá» dụng <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">khóa PGP cÃŽng khai nà y</a> cho viá»c mã hóa giao tiếp.\n\n   > ÄÆ°á»ng dẫn tá»i khóa PGP cÃŽng khai: <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c376
0afe28f50debb8e19627d7e824b814b/https%3A//decentraleyes.org/3f774aff6d/publ…" rel=\"nofollow\">https://decentraleyes.org/3f774aff6d/public-key.txt</a>\n\n<b>Bạn có Äang táºn hưá»ng tiá»n Ãch nà y khÃŽng?</b>\n\nDecentraleyes là , và sẜ luÃŽn là miá»
n phÃ, nhưng bạn có thá» há» trợ sá»± phát triá»n liên tục cá»§a nó bằng cách quyên góp, bằng cách Äóng góp trên GitLab, hoặc bằng cách truyá»n bá rá»ng rãi tiá»n Ãch nà y (chÃnh là cái là m nên Äiá»u kỳ diá»u). Chúng tÃŽi rất biết Æ¡n bất cứ sá»± trợ giúp nà o!\n\n     ⢠GitLab: <a href=\"https://outgoing.prod.mozaws.net/v1/26434c016cfe12000d62fedab907c037c0263b2…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes</a>\n     ⢠Ịng há»: <a href=\"https://outgoing.prod.mozaws.net/v1/d41865bc77319d73d03fa7198c7d0a8a9effcbd…
aleyes.org/donate\" rel=\"nofollow\">https://decentraleyes.org/donate</a>",
"zh-CN": "è¶æ¥è¶å€ççœç«åŒå§æŽå äŸèµå€§åç¬¬äžæ¹æ¥äº€ä»å
容ãåæ¶å¯¹å¹¿åæè·èžªåšç请æ±é垞没æé®é¢ïŒäœå±èœå®é
å
容åçæåœç¶äŒæå页é¢ãæ€æ©å±çç®çæ¯æªå»äžéŽäººïŒæäŸè¶
é«éçæ¬å°ïŒèªåžŠçïŒæä»¶äº€ä»ä»¥æ¹åçœäžéç§ã\n\n     ⢠åé¿å£°ç§°ä¿æ€éç§å¹¶å
莹æå¡ç倧å亀ä»çœç»ä»¥ä¿æ€æšçéç§ã\n     ⢠èŸ
å©åžžè§è¿æ»€åšïŒäŸåŠ uBlock Origin (æšè)ïŒAdblock Plus çã\n     ⢠å³è£
å³çšïŒäœ¿çšåç»å¯¹äžéèŠä»»äœé
眮ã\n\n<i>泚æïŒDecentraleyes äžæ¯äžçµè¯ïŒäœå®çç¡®èœé²æ¢è®žå€çœç«è®©äœ åéæ€ç±»è¯·æ±ãæåïŒäœ è¿å¯ä»¥è®© Decentraleyes å±èœä»»äœçŒºå€±ç CDN èµæºã</i>\n\n   > æŽç®çç®ä»ïŒ<a href=\"https://outgoing.prod.mozaws.net/v1/d22d502c3b43fb2f96a0310a4f001f4745aba6c…" rel=\"nofollow
\">https://git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction</a>\n\n<b>æç®åæ¯åŠåå°ä¿æ€?</b>\n\nè¿äžª <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">æµè¯å·¥å
·</a> èœåè¯äœ äœ æ¯åŠåå°ä¿æ€ãæä»¬æšèæšçšè¿äžªç®åãå¿«éçæ¹åŒïŒæ£æ¥æ¬éå ç»ä»¶æ¯åŠå·²è¢«å®è£
ãå¯çšå¹¶æ£ç¡®é
眮ã\n\n   > æµè¯å·¥å
·ç宿ŽçœåïŒ<a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">https://decentraleyes.org/test</a>\n\n<b>åžžè§é®é¢åè§£ç</b>\n\n   > åžžè§é®é¢é¡µé¢ç宿ŽéŸæ¥ïŒ<a href=\"https://outgoing.prod.mozaws.net/v1/1a6f38b165b3e86d666a5f770656e66788a71b6…" rel=\"nofollow\">https:
//git.synz.io/Synzvato/decentraleyes/wikis/Frequently-Asked-Questions</a>\n\n<b>ææ¯ä¿¡æ¯</b>\n\n- æ¯æççœç»ïŒGoogle Hosted Libraries, Microsoft Ajax CDN, CDNJS (Cloudflare), jQuery CDN (MaxCDN), jsDelivr (MaxCDN), Yandex CDN, Baidu CDN, Sina Public Resources, å UpYun Librariesã\n\n- æå
çèµæºïŒAngularJS, Backbone.js, Dojo, Ember.js, Ext Core, jQuery, jQuery UI, Modernizr, MooTools, Prototype, Scriptaculous, SWFObject, Underscore.js å Web Font Loaderã\n\n<b>äžªäººæ¯æ</b>\n\näœ çé®é¢äžè¿°æ²¡æè§£çïŒæè
äœ æå
¶ä»çç±èŠåç¬èç³»æïŒæšéæ¶å¯ä»¥èç³»æ: <a href=\"/\" rel=\"nofollow\">decentraleyes(a)protonmail.com</a>ãå忬¢è¿æåºBugæ¥åæå»ºè®®ïŒæä¹äŒå倿¯äžå°çµåé®ä»¶ã\n\n欢è¿äœ¿çš <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">æ€ PGP å
¬é¥</a> äžæè¿è¡å å¯éä
¿¡ã\n\n   > PGP å
¬é¥å®æŽéŸæ¥ïŒ<a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">https://decentraleyes.org/3f774aff6d/public-key.txt</a>\n\n<b>äœ åæ¬¢è¿äžªæ©å±åïŒ</b>\n\nDecentraleyes ç®åæ¯å¹¶äžå°æ°žè¿æ¯å
莹çãäœäœ ä¹å¯ä»¥æ¯æå®çåŒåïŒæ¯åŠææ¬ŸïŒåš GitLab 莡ç®ïŒæè
äŒ æè¿äžªèœ¯ä»¶ïŒåªææ¯åç§°ïŒãä»»äœåž®å©éœäžèææ¿ïŒ\n\n     ⢠GitLabïŒ<a href=\"https://outgoing.prod.mozaws.net/v1/26434c016cfe12000d62fedab907c037c0263b2…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes</a>\nÂ Â Â Â Â â¢ ææ¬ŸïŒ<a href=\"https://outgoing.prod.mozaws.net/v1/d41865bc77319d73d03fa7198c7d0a8a9effcbd…" rel=\"nofollow\">https://decentraleyes.org/donat
e</a>",
"zh-TW": "èš±å€ç¶²ç«å·²é挞éå§äŸé ç¬¬äžæ¹å€§ç¶²ç«çæåäŸå³éå
§å®¹ãåæ¶è廣åæåæè¿œè¹€ç¶²ç«éçé£ç·éåžžäžææä»éºŒåé¡ïŒç¶èå°éç¶²é ç¶äžç寊éå
§å®¹å°±åŸæå¯èœæé æç¶²é é¯èª€ãæ€éå å
ä»¶çç®æšæ¯èŠééåšæ¬æ©æºåäžä»œåžžçšççšåŒåº«ïŒè®æšå¯ç¡é éé網路ååŸå³å¯å¿«éèŒå
¥éäºçšåŒåº«ïŒä¹æ¹åäºç·äžé±ç§ã\n\n     ⢠é¿å
é£ç·è³å®£çš±å
è²»ç倧åçå
§å®¹å³é網路ïŒä»¥ä¿è·æšçé±ç§ã\n     ⢠å¯èåžžèŠçå°éåšïŒäŸåŠ uBlock OriginïŒæšèŠäœ¿çšïŒãAdblock Plus ççäžå䜿çšïŒåèœäºè£ã\n     ⢠éšè£å³çšïŒäžéç¹å¥èšå®ã\n\n<i>èš»: Decentraleyes äžæ¯è¬èœïŒäœç確å¯é¿å
倧éç¶²ç«è®æšéåºéé¡è«æ±ãæçµïŒæšéå¯ä»¥è® Decentraleyes å°é猺å°ç CDN è³æºè«æ±ã</i>\n\n   > ç°¡å®ä»ç޹: <a href=\"https://outgoing.prod.mozaws.net/v1/d22d502c3b43fb2f96a0310a4f001f4
745aba6cf82e0ad5b78c2145db00476fd/https%3A//git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction\" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Simple-Introduction</a>\n\n<b>æç®åæ¯åŠåå°ä¿è·ïŒ</b>\n\néå <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">å°å·¥å
·</a> å¯é¡¯ç€ºæšæ¯åŠæ£ç¢ºåå°ä¿è·ãæšèŠæšéééåç°¡äŸ¿çæ¹åŒäŸç¢ºèªæ¯åŠå·²å®è£ãåçšã䞊æ£ç¢ºèšå®å®æéå¥éå å
ä»¶ã\n\n   > 枬詊工å
·ç宿Žéçµ: <a href=\"https://outgoing.prod.mozaws.net/v1/221301c720e1488012efd45e7bd7ec2124fb4e2…" rel=\"nofollow\">https://decentraleyes.org/test</a>\n\n<b>åžžèŠåé¡é</b>\n\n   > åžžèŠåé¡ç宿Žéçµ: <a href=\"https://outgoing.prod.mozaws.net/v1/1a6f38b165b3e86d666a5f770656e66788a71b6…
25/https%3A//git.synz.io/Synzvato/decentraleyes/wikis/Frequently-Asked-Questions\" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes/wikis/Frequently-Asked-Questions</a>\n\n<b>æè¡è³èš</b>\n\n- æ¯æŽçå³é網路: Google Hosted LibrariesãMicrosoft Ajax CDNãCDNJS (Cloudflare)ãjQuery CDN (MaxCDN)ãjsDelivr (MaxCDN)ãYandex CDNãBaidu CDNãSina Public Resources 以å UpYun Librariesã\n\n- å
§å»ºè³æº: AngularJSãBackbone.jsãDojoãEmber.jsãExt CoreãjQueryãjQuery UIãModernizrãMooToolsãPrototypeãScriptaculousãSWFObjectãUnderscore.js 以å Web Font Loader çã\n\n<b>åäººæ¯æŽ</b>\n\nè¥æšçåé¡äžåšäžé¢ïŒæéæå
¶ä»å顿³é£çµ¡ææ¬äººåïŒæšå¯ä»¥å¯ä¿¡å° <a href=\"/\" rel=\"nofollow\">decentraleyes(a)protonmail.com</a>ãä¹éåžžæ¡è¿æšåå ± Bug ææ¯æäŸå»ºè°ïŒææåèŠæ¯äžå°ä¿¡ã\n\næ¡è¿äœ¿çš <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…
e824b814b/https%3A//decentraleyes.org/3f774aff6d/public-key.txt\" rel=\"nofollow\">æ€ PGP å
¬é°</a> é²è¡å å¯éèšã\n\n   > PGP å
¬é°ç宿Žéçµ: <a href=\"https://outgoing.prod.mozaws.net/v1/33b2d4ef799a5e03c5dcb380ecb55c3760afe28…" rel=\"nofollow\">https://decentraleyes.org/3f774aff6d/public-key.txt</a>\n\n<b>æšåæ¡éå¥éå å
ä»¶åïŒ</b>\n\nDecentraleyes ç®åå
è²»ïŒäžå°æ°žé å
è²»ãæšå¯ä»¥ééææ¬Ÿãåš GitLab è²¢ç»çšåŒç¢Œæå¹«å©å®£å³ïŒäŸæ¯æŽéå¥éå å
ä»¶çæ°žçºçŒå±ãéåžžæè¬æšçä»»äœå¹«å©ïŒ\n\n     ⢠GitLab: <a href=\"https://outgoing.prod.mozaws.net/v1/26434c016cfe12000d62fedab907c037c0263b2…" rel=\"nofollow\">https://git.synz.io/Synzvato/decentraleyes</a>\nÂ Â Â Â Â â¢ ææ¬Ÿ: <a href=\"https://outgoing.prod.mozaws.net/v1/d41865bc77319d73d03fa7198c7d0a8a
9effcbd09b31c9f48af1c75b3f23f8a6/https%3A//decentraleyes.org/donate\" rel=\"nofollow\">https://decentraleyes.org/donate</a>"
},
@@ -645,7 +645,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2021-09-03T09:18:28Z",
+ "last_updated": "2022-01-31T20:45:40Z",
"name": {
"ar": "Decentraleyes",
"bg": "Decentraleyes",
@@ -676,6 +676,7 @@
"sv-SE": "Decentraleyes",
"tr": "Decentraleyes",
"uk": "Decentraleyes",
+ "vi": "Decentraleyes",
"zh-CN": "Decentraleyes",
"zh-TW": "Decentraleyes"
},
@@ -721,10 +722,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.8099,
- "bayesian_average": 4.805137452044962,
- "count": 1189,
- "text_count": 226
+ "average": 4.8109,
+ "bayesian_average": 4.806163057596681,
+ "count": 1206,
+ "text_count": 228
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/reviews/",
"requires_payment": false,
@@ -761,6 +762,7 @@
"sv-SE": "Skyddar dig mot att spåras av \"gratis\" centraliserad innehållsleverans. Det hindrar att förfrågningar görs till nÀtverk som Google Hosted Libraries och tillhandahåller lokala filer, så att sidor inte går sönder. Kompletterar vanliga blockerare.",
"tr": "Sizi içerik daÄıtımıyla yapılan merkezi ve \"ÃŒcretsiz\" takipten korur. Google YerleÅimli KÃŒtÃŒphaneler gibi aÄlara eriÅimi engelleyip bunlar yerine siteler için gerekli yerel dosyaları sunar. OlaÄan içerik engelleyicileri iÅlevsel olarak tamamlar.",
"uk": "ÐаÑ
ОÑÐ°Ñ Ð²Ð°Ñ Ð²ÑÐŽ ÑÑÐµÐ¶ÐµÐœÐœÑ \"безкПÑÑПвМОЌО\", ÑеМÑÑалÑзПваМОЌО ЌеÑежаЌО ЎПÑÑавкО кПМÑеМÑÑ. ÐлПкÑÑ Ð±ÐµÐ·Ð»ÑÑ Ð·Ð°Ð¿ÐžÑÑв ЎП ЌеÑеж, Ñк-ÐŸÑ Google Hosted Libraries, взаЌÑМ ÐœÐ°ÐŽÐ°Ñ Ð»ÐŸÐºÐ°Ð»ÑÐœÑ ÑайлО ÐŽÐ»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑÐµÐœÐœÑ ÑпÑавМПÑÑÑ ÑайÑÑв. ÐПпПвМÑÑ Ð·Ð²ÐžÑÐœÑ Ð±Ð»ÐŸÐºÐ°ÑПÑО кПМÑеМÑÑ.",
+ "vi": "Bảo vá» bạn khá»i bá» theo dõi thÃŽng qua viá»c phân phá»i ná»i dung \"miá»
n phÃ\", táºp trung. Nó ngÄn nhiá»u yêu cầu Äến các mạng lưá»i như Google Hosted Libraries, và giao các tá»p cục bá» Äá» giữ các trang web khá»i bá» lá»i. Bá» sung cho các trình chặn ná»i dung.",
"zh-CN": "ä¿æ€æšå
åéäžåŒçå
容亀ä»çœç»ïŒCDNïŒçè·èžªãå®å¯ä»¥æŠæªè®žå€ç§éŸå CDN ç请æ±ïŒèœ¬èæåæ¬å°æäŸçæä»¶ïŒä»è鲿¢çœç«åèœåæãèŸ
å©åžžè§çå
å®¹è¿æ»€è§åã",
"zh-TW": "ä¿è·æšäžåãå
è²»ããäžå¿åçå
§å®¹å³é網路ïŒCDNïŒçè¿œè¹€ãæ€éå å
ä»¶å¯é²æ¢æšçç芜åšéåºè«æ±å° Google Hosted Libraries éé¡çæåïŒäžŠå𿬿©æäŸçšåŒåº«ä»¥éå°çžåçææïŒèäžé æç¶²é é¯èª€ãå¯è£å
äžè¬å
§å®¹å°éåšçåèœã"
},
@@ -795,6 +797,7 @@
"sv-SE": "https://decentraleyes.org",
"tr": "https://decentraleyes.org",
"uk": "https://decentraleyes.org",
+ "vi": "https://decentraleyes.org",
"zh-CN": "https://decentraleyes.org",
"zh-TW": "https://decentraleyes.org"
},
@@ -807,7 +810,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/versions/",
- "weekly_downloads": 4750
+ "weekly_downloads": 4600
},
"notes": null
},
@@ -823,7 +826,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/5/5474/5474073.png?modified=…"
}
],
- "average_daily_users": 973188,
+ "average_daily_users": 1047445,
"categories": {
"android": [
"security-privacy"
@@ -1345,10 +1348,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.7993,
- "bayesian_average": 4.796428745055322,
- "count": 1968,
- "text_count": 387
+ "average": 4.7922,
+ "bayesian_average": 4.789383427482703,
+ "count": 2021,
+ "text_count": 400
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/reviews/",
"requires_payment": false,
@@ -1372,7 +1375,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/versions/",
- "weekly_downloads": 16349
+ "weekly_downloads": 17114
},
"notes": null
},
@@ -1388,7 +1391,7 @@
"picture_url": null
}
],
- "average_daily_users": 5191457,
+ "average_daily_users": 5616569,
"categories": {
"android": [
"security-privacy"
@@ -1400,18 +1403,18 @@
"contributions_url": "",
"created": "2015-04-25T07:26:22Z",
"current_version": {
- "id": 5341891,
+ "id": 5362670,
"compatibility": {
"firefox": {
- "min": "60.0",
+ "min": "68.0",
"max": "*"
},
"android": {
- "min": "60.0",
+ "min": "68.0",
"max": "*"
}
},
- "edit_url": "https://addons.mozilla.org/en-US/developers/addon/ublock-origin/versions/53…",
+ "edit_url": "https://addons.mozilla.org/en-US/developers/addon/ublock-origin/versions/53…",
"is_strict_compatibility_enabled": false,
"license": {
"id": 6,
@@ -1422,22 +1425,22 @@
"url": "http://www.gnu.org/licenses/gpl-3.0.html"
},
"release_notes": {
- "en-US": "See complete release notes for <a href=\"https://outgoing.prod.mozaws.net/v1/90bb2b83a5130b536588d759f90b137cbd7523e…" rel=\"nofollow\">1.40.2</a>.\n\n<b>Closed as fixed:</b>\n\n<ul><li><a href=\"https://outgoing.prod.mozaws.net/v1/ae662bdc4a6203c3e24028f680e9b6fdbb19b7b…" rel=\"nofollow\">localhost: breakage</a></li></ul>\n<a href=\"https://outgoing.prod.mozaws.net/v1/a10383476cb962cc4f0e0b9f32bba6d8ae0a175…" rel=\"nofollow\">Commits history since last version</a>."
+ "en-US": "See complete release notes for <a href=\"https://outgoing.prod.mozaws.net/v1/8f1a1c9bde802fd173764201fe3c408fe936094…" rel=\"nofollow\">1.41.2</a>.\n\n<b>Notes</b>\n\nMinimum supported browser version has been bumped up Firefox 68.\n\n<b>New</b>\n\n<b>Dark mode</b>\n\nSupport for dark mode added to the <em>Settings</em> pane, under the (new) <em>Appearance</em> section. The new setting can be either <em>Auto</em>, <em>Light</em>, or <em>Dark</em>.\n\nIn addition, there is a new setting to control the accent color used by uBO throughout its user interface. For example, changing the accent color changes the look of the popup panel.\n\n<b>Behavior at browser launch</b>\n\nA new setting in <em>\"Filter lists\"</em> pane to control whether uBO should wait for all filter lists to be loaded before unsuspending network activity. By default, at browser launch uBO waits for all filter lists
to be loaded before unsuspending network activity so as to ensure web pages are properly filtered at launch.\n\nThe new setting allows to opt out of network activity suspension at launch, i.e. allowing web pages to load without waiting for filter lists to be fully loaded, of course at the cost of potentially not filtering properly those web pages.\n\n<b>Closed as fixed:</b>\n\n<ul><li><a href=\"https://outgoing.prod.mozaws.net/v1/2eab71586632eb640a895bec1b39be79077afd6…" rel=\"nofollow\">Logger incorrectly reporting <code>header=</code> filters</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/42bbec08988dab83ae4dafa0798eae142de7ed6…" rel=\"nofollow\">Picker is broken by quotation mark in attribute</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/d4e64acbfe1c46e4f275c5996fa356e3841dd73…
a138363c5a1/https%3A//github.com/uBlockOrigin/uBlock-issues/issues/1918%23i…" rel=\"nofollow\">Use \"âŠ\" instead of \"...\"</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/bab74682cd729207a20d88c4a4062b80312acfc…" rel=\"nofollow\">Fix bad detection of unnecessary trailing <code>|</code></a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/7c1a491f8edec5894e63b0e6b04e85a22592ff1…" rel=\"nofollow\">Unexplained popup block on streamlare</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/68625ccb914fa8a8ceb5575470af83fec911b66…" rel=\"nofollow\">Scrollbars appear in click2load.html</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/365fdd7916a792aa069cddd9e1a5e5dbd0a579e…
b72f1b4c6328e3dc/https%3A//github.com/uBlockOrigin/uBlock-issues/issues/185…" rel=\"nofollow\">Element Zapper denies on a specific website</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/d7b163312b5f8028fd69980302a9e655899d367…" rel=\"nofollow\">Prevent uBO from hiding html or body when matched by a generic cosmetic filter</a> (final fix)</li><li><a href=\"https://outgoing.prod.mozaws.net/v1/cdbd62b27ed42f78ab4f4966ada9560c8d2cadc…" rel=\"nofollow\">Dark Mode support</a></li></ul>\n<b>Notable commits without an entry in the issue tracker:</b>\n\n<ul><li><a href=\"https://outgoing.prod.mozaws.net/v1/382e7cf88882727789d14c5be75b206a537be20…" rel=\"nofollow\">Add a redirectable script that sets canRunAds true</a>\n <ul> <li>
<a href=\"https://outgoing.prod.mozaws.net/v1/18e2bbf0ef4779ce63340bdce8f17718a250718…" rel=\"nofollow\">Related commit</a> (contributed by @Yuki2718)\n </li></ul></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/83a72eea2b6f4714daac8d93f8d30cbed96617c…" rel=\"nofollow\">Work toward bringing dark theme closer to a stable release</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/8ad5936e192ccea4f3f085c76d75092ae198a9f…" rel=\"nofollow\">Raise minimum versions of browsers</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/ec36e87e6528a3b741a45c833a35fe710c037b8…
1578c0dcdf662f5c5cddd5a7d81\" rel=\"nofollow\">Improve dealing with ambiguity in regex-based-looking network filters</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/6d2bb828ee7b7f312a08f5673598b051ad2e72c…" rel=\"nofollow\">Improve google-analytics shim</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/5c3ba5b510e147ecf3111b1acfa256cf1d998db…" rel=\"nofollow\">Fix regression causing regex-based filters to be case sensitive</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/7d7cc9c5b291fb2585bd531aa70139a36203b84…" rel=\"nofollow\">Add shim for FingerprintJS (aka Fingerprint v3) </a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/d
d4f21a7b6195aa575427d6da5051075664e58eeb28918a693b534fc8e33938c/https%3A//g…" rel=\"nofollow\">Disable the suspending of network requests when installing the extension</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/810553680cb033078ceb67123d580374838a7eb…" rel=\"nofollow\">Do not select background images as best candidate in picker</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/b61ce516eb9943cd301adddafb0f5bb795aa3b9…" rel=\"nofollow\">Add \"blockedDetails\" section to troubleshooting information</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/8ac61cb733558e013223612b1003542bb7221e7…
12ee84957cf2545a6de9353\" rel=\"nofollow\">Remove \"ABP X Files\" from stock filter lists</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/087a2413630067a9e96de488b95fb402c35893e…" rel=\"nofollow\">Add setting to control suspension on network activity at launch</a></li><li><a href=\"https://outgoing.prod.mozaws.net/v1/0b74d2e26c9f2ac6dcea7342fe82ddfc68c7f1c…" rel=\"nofollow\">Make FilterJustOrigin derive from FilterOriginHitSet</a></li></ul>\n<a href=\"https://outgoing.prod.mozaws.net/v1/b2646c023bf1885a71d15d36ac28d885170e845…" rel=\"nofollow\">Commits history since last version</a>."
},
"reviewed": null,
- "version": "1.40.2",
+ "version": "1.41.2",
"files": [
{
- "id": 3886236,
- "created": "2021-12-26T12:44:41Z",
- "hash": "sha256:837915929c950651e46ed5cc30aa9faed84136e7715e74369a2eadf328bea065",
+ "id": 3907015,
+ "created": "2022-02-10T17:20:04Z",
+ "hash": "sha256:0ff07ab9f13d793ee8e5796937b3f06fa80bdcbec40d742374b0ce7ad04ff46b",
"is_restart_required": false,
"is_webextension": true,
"is_mozilla_signed_extension": false,
"platform": "all",
- "size": 2961920,
+ "size": 2990953,
"status": "public",
- "url": "https://addons.mozilla.org/firefox/downloads/file/3886236/ublock_origin-1.4…",
+ "url": "https://addons.mozilla.org/firefox/downloads/file/3907015/ublock_origin-1.4…",
"permissions": [
"dns",
"menus",
@@ -1551,7 +1554,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2022-01-11T16:21:16Z",
+ "last_updated": "2022-02-10T18:06:03Z",
"name": {
"ar": "uBlock Origin",
"bg": "uBlock Origin",
@@ -1696,10 +1699,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.7716,
- "bayesian_average": 4.771167323690879,
- "count": 12979,
- "text_count": 3480
+ "average": 4.7727,
+ "bayesian_average": 4.7722698916892465,
+ "count": 13179,
+ "text_count": 3535
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/reviews/",
"requires_payment": false,
@@ -1761,7 +1764,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/versions/",
- "weekly_downloads": 125806
+ "weekly_downloads": 130353
},
"notes": null
},
@@ -1777,7 +1780,7 @@
"picture_url": null
}
],
- "average_daily_users": 87644,
+ "average_daily_users": 90982,
"categories": {
"android": [
"photos-media"
@@ -1874,10 +1877,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.5126,
- "bayesian_average": 4.507501759055358,
- "count": 1030,
- "text_count": 390
+ "average": 4.514,
+ "bayesian_average": 4.508879840067731,
+ "count": 1033,
+ "text_count": 392
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/re…",
"requires_payment": false,
@@ -1899,7 +1902,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/ve…",
- "weekly_downloads": 253
+ "weekly_downloads": 277
},
"notes": null
},
@@ -1915,7 +1918,7 @@
"picture_url": null
}
],
- "average_daily_users": 92805,
+ "average_daily_users": 97346,
"categories": {
"android": [
"experimental",
@@ -2033,10 +2036,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4497,
- "bayesian_average": 4.435310659280604,
- "count": 358,
- "text_count": 99
+ "average": 4.4429,
+ "bayesian_average": 4.428818352287396,
+ "count": 368,
+ "text_count": 101
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/reviews/",
"requires_payment": false,
@@ -2058,7 +2061,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/versions/",
- "weekly_downloads": 1674
+ "weekly_downloads": 1492
},
"notes": null
},
@@ -2074,7 +2077,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/12/12929/12929064.png?modifi…"
}
],
- "average_daily_users": 196402,
+ "average_daily_users": 206601,
"categories": {
"android": [
"photos-media",
@@ -2291,10 +2294,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.6529,
- "bayesian_average": 4.6476691311094935,
- "count": 1040,
- "text_count": 203
+ "average": 4.6569,
+ "bayesian_average": 4.6516867041491325,
+ "count": 1055,
+ "text_count": 208
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/reviews/",
"requires_payment": false,
@@ -2315,7 +2318,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/versions/",
- "weekly_downloads": 3713
+ "weekly_downloads": 3623
},
"notes": null
},
@@ -2338,7 +2341,7 @@
"picture_url": null
}
],
- "average_daily_users": 69156,
+ "average_daily_users": 72119,
"categories": {
"android": [
"other"
@@ -2620,10 +2623,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4399,
- "bayesian_average": 4.43514347208329,
- "count": 1082,
- "text_count": 293
+ "average": 4.4415,
+ "bayesian_average": 4.436746886056066,
+ "count": 1094,
+ "text_count": 296
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/reviews/",
"requires_payment": false,
@@ -2643,7 +2646,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/versions/",
- "weekly_downloads": 108
+ "weekly_downloads": 37
},
"notes": null
},
@@ -2659,7 +2662,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/0/0/143.png?modified=1506804…"
}
],
- "average_daily_users": 348098,
+ "average_daily_users": 369234,
"categories": {
"android": [
"performance",
@@ -2673,7 +2676,7 @@
"contributions_url": "https://www.paypal.me/NoScript?utm_content=product-page-contribute&utm_medi…",
"created": "2005-05-13T10:51:32Z",
"current_version": {
- "id": 5343208,
+ "id": 5362989,
"compatibility": {
"firefox": {
"min": "59.0",
@@ -2684,7 +2687,7 @@
"max": "*"
}
},
- "edit_url": "https://addons.mozilla.org/en-US/developers/addon/noscript/versions/5343208",
+ "edit_url": "https://addons.mozilla.org/en-US/developers/addon/noscript/versions/5362989",
"is_strict_compatibility_enabled": false,
"license": {
"id": 13,
@@ -2695,22 +2698,22 @@
"url": "http://www.gnu.org/licenses/gpl-2.0.html"
},
"release_notes": {
- "en-US": "v 11.2.14\n============================================================\nx [nscl] Updated SyncMessage fixes conflict with other\n content blockers (thanks gwarser, barbaz and Baraoic)"
+ "en-US": "v 11.2.24rc1\n============================================================\nx [nscl] Avoid unnecessary window patching"
},
"reviewed": null,
- "version": "11.2.14",
+ "version": "11.2.24",
"files": [
{
- "id": 3887553,
- "created": "2021-12-29T21:36:54Z",
- "hash": "sha256:47399b94f57dde1162f74a49679c7cb3b6dfe684d4eee49842e65eae64e9982b",
+ "id": 3907334,
+ "created": "2022-02-10T23:13:36Z",
+ "hash": "sha256:be96f5efb1103ba978643348df1255e4f4409dd5d1a494222502be99f20da1d3",
"is_restart_required": false,
"is_webextension": true,
"is_mozilla_signed_extension": false,
"platform": "all",
- "size": 657255,
+ "size": 659553,
"status": "public",
- "url": "https://addons.mozilla.org/firefox/downloads/file/3887553/noscript_security…",
+ "url": "https://addons.mozilla.org/firefox/downloads/file/3907334/noscript_security…",
"permissions": [
"contextMenus",
"storage",
@@ -2775,7 +2778,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2021-12-30T08:43:35Z",
+ "last_updated": "2022-02-11T11:27:15Z",
"name": {
"de": "NoScript",
"el": "NoScript",
@@ -2863,10 +2866,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4086,
- "bayesian_average": 4.405844933901775,
- "count": 1855,
- "text_count": 725
+ "average": 4.407,
+ "bayesian_average": 4.4042376055924235,
+ "count": 1865,
+ "text_count": 729
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/reviews/",
"requires_payment": false,
@@ -2902,7 +2905,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/versions/",
- "weekly_downloads": 9181
+ "weekly_downloads": 8955
},
"notes": null
},
@@ -2918,7 +2921,7 @@
"picture_url": null
}
],
- "average_daily_users": 126321,
+ "average_daily_users": 130864,
"categories": {
"android": [
"performance",
@@ -3030,10 +3033,10 @@
"category": "recommended"
},
"ratings": {
- "average": 3.9132,
- "bayesian_average": 3.908896818499659,
- "count": 1025,
- "text_count": 366
+ "average": 3.9086,
+ "bayesian_average": 3.904281831064806,
+ "count": 1029,
+ "text_count": 369
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/revi…",
"requires_payment": false,
@@ -3052,7 +3055,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/vers…",
- "weekly_downloads": 1454
+ "weekly_downloads": 1601
},
"notes": null
}
diff --git a/projects/tor-browser/config b/projects/tor-browser/config
index 9a711b7..3748f55 100644
--- a/projects/tor-browser/config
+++ b/projects/tor-browser/config
@@ -78,9 +78,9 @@ input_files:
enable: '[% ! c("var/android") %]'
- filename: Bundle-Data
enable: '[% ! c("var/android") %]'
- - URL: https://addons.cdn.mozilla.net/user-media/addons/722/noscript_security_suit…
+ - URL: https://addons.mozilla.org/firefox/downloads/file/3907334/noscript_security…
name: noscript
- sha256sum: 627ad134968a395fd585a6455087c1c1c461b993a4db0c1471f4c5d2c0cc1177
+ sha256sum: be96f5efb1103ba978643348df1255e4f4409dd5d1a494222502be99f20da1d3
- filename: 'RelativeLink/start-tor-browser.desktop'
enable: '[% c("var/linux") %]'
- filename: 'RelativeLink/execdesktop'
1
0
[tor-browser-build/master] Bug 40429: Make an error when incrementals are missing in hash_incrementals
by richard@torproject.org 10 Feb '22
by richard@torproject.org 10 Feb '22
10 Feb '22
commit c2601c6205ca89dd5f5a2c658b73d090e707f050
Author: Nicolas Vigier <boklm(a)torproject.org>
Date: Thu Feb 10 13:36:12 2022 +0100
Bug 40429: Make an error when incrementals are missing in hash_incrementals
---
projects/release/hash_incrementals | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/projects/release/hash_incrementals b/projects/release/hash_incrementals
index 4ab0f4d..7744ddf 100644
--- a/projects/release/hash_incrementals
+++ b/projects/release/hash_incrementals
@@ -9,7 +9,13 @@
[% ELSE -%]
cd [% shell_quote(path(dest_dir)) %]/[% c("var/signed_status") %]/[% c("version") %]-[% c("var/torbrowser_build") %]
[% END -%]
-sha256sum `ls -1 | grep '\.incremental\.mar$' | sort` > sha256sums-[% c("var/signed_status") %]-build.incrementals.txt
+if ls -1 | grep -q '\.incremental\.mar$'
+then
+ sha256sum `ls -1 | grep '\.incremental\.mar$' | sort` > sha256sums-[% c("var/signed_status") %]-build.incrementals.txt
+else
+ echo 'No incrementals were found' >&2
+ exit 1
+fi
[% IF c("var/sign_build") -%]
export HOME="$old_HOME"
gpg -abs [% c("var/sign_build_gpg_opts") %] sha256sums-[% c("var/signed_status") %]-build.incrementals.txt
1
0
[tor-browser-build/master] Merge remote-tracking branch 'gitlab-tpo/merge-requests/403'
by boklm@torproject.org 10 Feb '22
by boklm@torproject.org 10 Feb '22
10 Feb '22
commit e1d065e3059feab31816a930f49ab5066336d650
Merge: e3eb982 ddb8212
Author: Nicolas Vigier <boklm(a)torproject.org>
Date: Thu Feb 10 13:39:50 2022 +0100
Merge remote-tracking branch 'gitlab-tpo/merge-requests/403'
README | 6 ++++++
doc/BUILD_ERRORS.txt | 19 +++++++++++++++++++
2 files changed, 25 insertions(+)
1
0
[tor-browser-build/master] Bug 40298: Add documentation about subuid and subgid
by boklm@torproject.org 10 Feb '22
by boklm@torproject.org 10 Feb '22
10 Feb '22
commit ddb8212aeaaa10ff21faa84312ccc533d44f8100
Author: Nicolas Vigier <boklm(a)torproject.org>
Date: Thu Feb 10 09:14:37 2022 +0100
Bug 40298: Add documentation about subuid and subgid
---
README | 6 ++++++
doc/BUILD_ERRORS.txt | 19 +++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/README b/README
index 5407b0c..1e7a4b9 100644
--- a/README
+++ b/README
@@ -53,6 +53,12 @@ To enable them you can use the following command as root:
You can enable them permanently by adding the setting to /etc/sysctl.d/
+The user you use to build needs to have a range of subordinate uids and
+gids in /etc/subuid and /etc/subgid. Most of the time they are added by
+default when the user is created. If it was not the case, you can use
+usermod(8) with the --add-subuids and --add-subgids options. See also
+the subuid(5) and subgid(5) man pages.
+
Starting a build
----------------
diff --git a/doc/BUILD_ERRORS.txt b/doc/BUILD_ERRORS.txt
index 83f2257..8bad9ea 100644
--- a/doc/BUILD_ERRORS.txt
+++ b/doc/BUILD_ERRORS.txt
@@ -43,3 +43,22 @@ rm /etc/resolv.conf
Restart network-manager
sudo service network-manager restart
+
+
+Could not find uid in /etc/subuid
+---------------------------------
+
+In some cases you can have the error:
+
+ Error: Error starting remote:
+ Error: Could not find uid in /etc/subuid
+ Error: failed to set uidmap
+
+The user you use to build needs to have a range of subordinate uids and
+gids in /etc/subuid and /etc/subgid. Most of the time they are added by
+default when the user is created. If it was not the case, you can use
+usermod(8) with the --add-subuids and --add-subgids options. See also
+the subuid(5) and subgid(5) man pages.
+
+Note that the root user is not exempted from the requirement for a
+valid /etc/subuid and /etc/subgid entry, if you are building as root.
1
0
[tor-launcher/master] Added country code strings to network-settings.dtd and other
by richard@torproject.org 10 Feb '22
by richard@torproject.org 10 Feb '22
10 Feb '22
commit 5fcd4edb836fcab3db21139e670bddc77811e30c
Author: Richard Pospesel <richard(a)torproject.org>
Date: Wed Jan 19 19:09:33 2022 +0100
Added country code strings to network-settings.dtd and other
new about:torconnect strings
needed for tor-browser#40773
---
src/chrome/locale/en-US/network-settings.dtd | 259 +++++++++++++++++++++++++++
1 file changed, 259 insertions(+)
diff --git a/src/chrome/locale/en-US/network-settings.dtd b/src/chrome/locale/en-US/network-settings.dtd
index c5844d2..c1cadc9 100644
--- a/src/chrome/locale/en-US/network-settings.dtd
+++ b/src/chrome/locale/en-US/network-settings.dtd
@@ -94,3 +94,262 @@
<!ENTITY torConnect.connectedConcise "Connected">
<!ENTITY torConnect.notConnectedConcise "Not Connected">
<!ENTITY torConnect.copyLog "Copy Tor Logs">
+<!ENTITY torConnect.configureConnection "Configure Connection…">
+<!ENTITY torConnect.tryBridge "Try a Bridge">
+
+<!-- #40773 about:torconnect country names -->
+<!-- LOCALIZTION NOTE (countries.XX): The country names should be translated such that:
+- some form of the 'official' name is preferred (we try to call countries what they call themselves)
+- the name can be easily found within a sorted list (eg: in English we prefer 'Netherlands' over 'The Netherlands' as most English speakers would not skip down to the 'T' section of the list to find the Netherlands, similarly 'Venezuela' vs 'Bolivarian Republic of Venezuela'). The names in this list will displayed to the user in a sorted order using String.prototype.localeCompare(); see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Ob… for details.
+
+Thank you!
+ -->
+<!ENTITY countries.ad "Andorra">
+<!ENTITY countries.ae "United Arab Emirates">
+<!ENTITY countries.af "Afghanistan">
+<!ENTITY countries.ag "Antigua and Barbuda">
+<!ENTITY countries.ai "Anguilla">
+<!ENTITY countries.al "Albania">
+<!ENTITY countries.am "Armenia">
+<!ENTITY countries.ao "Angola">
+<!ENTITY countries.aq "Antarctica">
+<!ENTITY countries.ar "Argentina">
+<!ENTITY countries.as "American Samoa">
+<!ENTITY countries.at "Austria">
+<!ENTITY countries.au "Australia">
+<!ENTITY countries.aw "Aruba">
+<!ENTITY countries.ax "Åland Islands">
+<!ENTITY countries.az "Azerbaijan">
+<!ENTITY countries.ba "Bosnia and Herzegovina">
+<!ENTITY countries.bb "Barbados">
+<!ENTITY countries.bd "Bangladesh">
+<!ENTITY countries.be "Belgium">
+<!ENTITY countries.bf "Burkina Faso">
+<!ENTITY countries.bg "Bulgaria">
+<!ENTITY countries.bh "Bahrain">
+<!ENTITY countries.bi "Burundi">
+<!ENTITY countries.bj "Benin">
+<!ENTITY countries.bl "Saint Barthélemy">
+<!ENTITY countries.bm "Bermuda">
+<!ENTITY countries.bn "Brunei Darussalam">
+<!ENTITY countries.bo "Bolivia (Plurinational State of)">
+<!ENTITY countries.bq "Bonaire, Sint Eustatius and Saba">
+<!ENTITY countries.br "Brazil">
+<!ENTITY countries.bs "Bahamas">
+<!ENTITY countries.bt "Bhutan">
+<!ENTITY countries.bv "Bouvet Island">
+<!ENTITY countries.bw "Botswana">
+<!ENTITY countries.by "Belarus">
+<!ENTITY countries.bz "Belize">
+<!ENTITY countries.ca "Canada">
+<!ENTITY countries.cc "Cocos (Keeling) Islands">
+<!ENTITY countries.cd "Congo (Kinshasa)">
+<!ENTITY countries.cf "Central African Republic">
+<!ENTITY countries.cg "Congo (Brazzaville)">
+<!ENTITY countries.ch "Switzerland">
+<!ENTITY countries.ci "Côte d'Ivoire">
+<!ENTITY countries.ck "Cook Islands">
+<!ENTITY countries.cl "Chile">
+<!ENTITY countries.cm "Cameroon">
+<!ENTITY countries.cn "China">
+<!ENTITY countries.co "Colombia">
+<!ENTITY countries.cr "Costa Rica">
+<!ENTITY countries.cu "Cuba">
+<!ENTITY countries.cv "Cabo Verde">
+<!ENTITY countries.cw "Curaçao">
+<!ENTITY countries.cx "Christmas Island">
+<!ENTITY countries.cy "Cyprus">
+<!ENTITY countries.cz "Czechia">
+<!ENTITY countries.de "Germany">
+<!ENTITY countries.dj "Djibouti">
+<!ENTITY countries.dk "Denmark">
+<!ENTITY countries.dm "Dominica">
+<!ENTITY countries.do "Dominican Republic">
+<!ENTITY countries.dz "Algeria">
+<!ENTITY countries.ec "Ecuador">
+<!ENTITY countries.ee "Estonia">
+<!ENTITY countries.eg "Egypt">
+<!ENTITY countries.eh "Western Sahara">
+<!ENTITY countries.er "Eritrea">
+<!ENTITY countries.es "Spain">
+<!ENTITY countries.et "Ethiopia">
+<!ENTITY countries.fi "Finland">
+<!ENTITY countries.fj "Fiji">
+<!ENTITY countries.fk "Falkland Islands (Malvinas)">
+<!ENTITY countries.fm "Micronesia (Federated States of)">
+<!ENTITY countries.fo "Faroe Islands">
+<!ENTITY countries.fr "France">
+<!ENTITY countries.ga "Gabon">
+<!ENTITY countries.gb "United Kingdom">
+<!ENTITY countries.gd "Grenada">
+<!ENTITY countries.ge "Georgia">
+<!ENTITY countries.gf "French Guiana">
+<!ENTITY countries.gg "Guernsey">
+<!ENTITY countries.gh "Ghana">
+<!ENTITY countries.gi "Gibraltar">
+<!ENTITY countries.gl "Greenland">
+<!ENTITY countries.gm "Gambia">
+<!ENTITY countries.gn "Guinea">
+<!ENTITY countries.gp "Guadeloupe">
+<!ENTITY countries.gq "Equatorial Guinea">
+<!ENTITY countries.gr "Greece">
+<!ENTITY countries.gs "South Georgia and the South Sandwich Islands">
+<!ENTITY countries.gt "Guatemala">
+<!ENTITY countries.gu "Guam">
+<!ENTITY countries.gw "Guinea-Bissau">
+<!ENTITY countries.gy "Guyana">
+<!ENTITY countries.hk "Hong Kong">
+<!ENTITY countries.hm "Heard Island and McDonald Islands">
+<!ENTITY countries.hn "Honduras">
+<!ENTITY countries.hr "Croatia">
+<!ENTITY countries.ht "Haiti">
+<!ENTITY countries.hu "Hungary">
+<!ENTITY countries.id "Indonesia">
+<!ENTITY countries.ie "Ireland">
+<!ENTITY countries.il "Israel">
+<!ENTITY countries.im "Isle of Man">
+<!ENTITY countries.in "India">
+<!ENTITY countries.io "British Indian Ocean Territory">
+<!ENTITY countries.iq "Iraq">
+<!ENTITY countries.ir "Iran (Islamic Republic of)">
+<!ENTITY countries.is "Iceland">
+<!ENTITY countries.it "Italy">
+<!ENTITY countries.je "Jersey">
+<!ENTITY countries.jm "Jamaica">
+<!ENTITY countries.jo "Jordan">
+<!ENTITY countries.jp "Japan">
+<!ENTITY countries.ke "Kenya">
+<!ENTITY countries.kg "Kyrgyzstan">
+<!ENTITY countries.kh "Cambodia">
+<!ENTITY countries.ki "Kiribati">
+<!ENTITY countries.km "Comoros">
+<!ENTITY countries.kn "Saint Kitts and Nevis">
+<!ENTITY countries.kp "Korea (Democratic People's Republic of)">
+<!ENTITY countries.kr "Korea (Republic of)">
+<!ENTITY countries.kw "Kuwait">
+<!ENTITY countries.ky "Cayman Islands">
+<!ENTITY countries.kz "Kazakhstan">
+<!ENTITY countries.la "Lao People's Democratic Republic">
+<!ENTITY countries.lb "Lebanon">
+<!ENTITY countries.lc "Saint Lucia">
+<!ENTITY countries.li "Liechtenstein">
+<!ENTITY countries.lk "Sri Lanka">
+<!ENTITY countries.lr "Liberia">
+<!ENTITY countries.ls "Lesotho">
+<!ENTITY countries.lt "Lithuania">
+<!ENTITY countries.lu "Luxembourg">
+<!ENTITY countries.lv "Latvia">
+<!ENTITY countries.ly "Libya">
+<!ENTITY countries.ma "Morocco">
+<!ENTITY countries.mc "Monaco">
+<!ENTITY countries.md "Moldova (Republic of)">
+<!ENTITY countries.me "Montenegro">
+<!ENTITY countries.mf "Saint Martin (French part)">
+<!ENTITY countries.mg "Madagascar">
+<!ENTITY countries.mh "Marshall Islands">
+<!ENTITY countries.mk "North Macedonia">
+<!ENTITY countries.ml "Mali">
+<!ENTITY countries.mm "Myanmar">
+<!ENTITY countries.mn "Mongolia">
+<!ENTITY countries.mo "Macao">
+<!ENTITY countries.mp "Northern Mariana Islands">
+<!ENTITY countries.mq "Martinique">
+<!ENTITY countries.mr "Mauritania">
+<!ENTITY countries.ms "Montserrat">
+<!ENTITY countries.mt "Malta">
+<!ENTITY countries.mu "Mauritius">
+<!ENTITY countries.mv "Maldives">
+<!ENTITY countries.mw "Malawi">
+<!ENTITY countries.mx "Mexico">
+<!ENTITY countries.my "Malaysia">
+<!ENTITY countries.mz "Mozambique">
+<!ENTITY countries.na "Namibia">
+<!ENTITY countries.nc "New Caledonia">
+<!ENTITY countries.ne "Niger">
+<!ENTITY countries.nf "Norfolk Island">
+<!ENTITY countries.ng "Nigeria">
+<!ENTITY countries.ni "Nicaragua">
+<!ENTITY countries.nl "Netherlands">
+<!ENTITY countries.no "Norway">
+<!ENTITY countries.np "Nepal">
+<!ENTITY countries.nr "Nauru">
+<!ENTITY countries.nu "Niue">
+<!ENTITY countries.nz "New Zealand">
+<!ENTITY countries.om "Oman">
+<!ENTITY countries.pa "Panama">
+<!ENTITY countries.pe "Peru">
+<!ENTITY countries.pf "French Polynesia">
+<!ENTITY countries.pg "Papua New Guinea">
+<!ENTITY countries.ph "Philippines">
+<!ENTITY countries.pk "Pakistan">
+<!ENTITY countries.pl "Poland">
+<!ENTITY countries.pm "Saint Pierre and Miquelon">
+<!ENTITY countries.pn "Pitcairn">
+<!ENTITY countries.pr "Puerto Rico">
+<!ENTITY countries.ps "Palestine">
+<!ENTITY countries.pt "Portugal">
+<!ENTITY countries.pw "Palau">
+<!ENTITY countries.py "Paraguay">
+<!ENTITY countries.qa "Qatar">
+<!ENTITY countries.re "Réunion">
+<!ENTITY countries.ro "Romania">
+<!ENTITY countries.rs "Serbia">
+<!ENTITY countries.ru "Russian Federation">
+<!ENTITY countries.rw "Rwanda">
+<!ENTITY countries.sa "Saudi Arabia">
+<!ENTITY countries.sb "Solomon Islands">
+<!ENTITY countries.sc "Seychelles">
+<!ENTITY countries.sd "Sudan">
+<!ENTITY countries.se "Sweden">
+<!ENTITY countries.sg "Singapore">
+<!ENTITY countries.sh "Saint Helena, Ascension and Tristan da Cunha">
+<!ENTITY countries.si "Slovenia">
+<!ENTITY countries.sj "Svalbard and Jan Mayen">
+<!ENTITY countries.sk "Slovakia">
+<!ENTITY countries.sl "Sierra Leone">
+<!ENTITY countries.sm "San Marino">
+<!ENTITY countries.sn "Senegal">
+<!ENTITY countries.so "Somalia">
+<!ENTITY countries.sr "Suriname">
+<!ENTITY countries.ss "South Sudan">
+<!ENTITY countries.st "Sao Tome and Principe">
+<!ENTITY countries.sv "El Salvador">
+<!ENTITY countries.sx "Sint Maarten (Dutch part)">
+<!ENTITY countries.sy "Syrian Arab Republic">
+<!ENTITY countries.sz "Eswatini">
+<!ENTITY countries.tc "Turks and Caicos Islands">
+<!ENTITY countries.td "Chad">
+<!ENTITY countries.tf "French Southern Territories">
+<!ENTITY countries.tg "Togo">
+<!ENTITY countries.th "Thailand">
+<!ENTITY countries.tj "Tajikistan">
+<!ENTITY countries.tk "Tokelau">
+<!ENTITY countries.tl "Timor-Leste">
+<!ENTITY countries.tm "Turkmenistan">
+<!ENTITY countries.tn "Tunisia">
+<!ENTITY countries.to "Tonga">
+<!ENTITY countries.tr "Turkey">
+<!ENTITY countries.tt "Trinidad and Tobago">
+<!ENTITY countries.tv "Tuvalu">
+<!ENTITY countries.tw "Taiwan">
+<!ENTITY countries.tz "Tanzania (United Republic of)">
+<!ENTITY countries.ua "Ukraine">
+<!ENTITY countries.ug "Uganda">
+<!ENTITY countries.um "United States Minor Outlying Islands">
+<!ENTITY countries.us "United States of America">
+<!ENTITY countries.uy "Uruguay">
+<!ENTITY countries.uz "Uzbekistan">
+<!ENTITY countries.va "Vatican City">
+<!ENTITY countries.vc "Saint Vincent and the Grenadines">
+<!ENTITY countries.ve "Venezuela (Bolivarian Republic of)">
+<!ENTITY countries.vg "Virgin Islands (British)">
+<!ENTITY countries.vi "Virgin Islands (U.S.)">
+<!ENTITY countries.vn "Vietnam (Socialist Republic of)">
+<!ENTITY countries.vu "Vanuatu">
+<!ENTITY countries.wf "Wallis and Futuna">
+<!ENTITY countries.ws "Samoa">
+<!ENTITY countries.ye "Yemen">
+<!ENTITY countries.yt "Mayotte">
+<!ENTITY countries.za "South Africa">
+<!ENTITY countries.zm "Zambia">
+<!ENTITY countries.zw "Zimbabwe">
1
0
commit e3eb98257a9581c1dc71e6e8dda586f32586485b
Author: Richard Pospesel <richard(a)torproject.org>
Date: Thu Feb 10 12:40:35 2022 +0100
Tag -build2
---
rbm.conf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rbm.conf b/rbm.conf
index 40943dd..cd9b40e 100644
--- a/rbm.conf
+++ b/rbm.conf
@@ -61,7 +61,7 @@ var:
torbrowser_version: '11.5a4'
torbrowser_build: 'build1'
torbrowser_incremental_from:
- - 11.5a3
+ - 11.5a2
project_name: tor-browser
multi_lingual: 0
build_mar: 1
1
0
[tor-browser-build/master] Release prep for 11.5a4 (desktop only)
by richard@torproject.org 09 Feb '22
by richard@torproject.org 09 Feb '22
09 Feb '22
commit aa65127fa1291bf580e84a8fcab3396ae381fe9c
Author: Richard Pospesel <richard(a)torproject.org>
Date: Wed Feb 9 14:03:25 2022 +0100
Release prep for 11.5a4 (desktop only)
Version bumps and changelog update
---
projects/firefox/config | 4 +-
projects/go/config | 4 +-
.../tor-browser/Bundle-Data/Docs/ChangeLog.txt | 55 ++++++++++++++++++++++
projects/tor-browser/config | 4 +-
projects/tor-launcher/config | 2 +-
rbm.conf | 4 +-
6 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/projects/firefox/config b/projects/firefox/config
index 0f28c7d..4365c43 100644
--- a/projects/firefox/config
+++ b/projects/firefox/config
@@ -8,7 +8,7 @@ git_submodule: 1
gpg_keyring: torbutton.gpg
var:
- firefox_platform_version: 91.5.0
+ firefox_platform_version: 91.6.0
firefox_version: '[% c("var/firefox_platform_version") %]esr'
torbrowser_branch: 11.5
branding_directory: 'browser/branding/alpha'
@@ -50,7 +50,7 @@ targets:
branding_directory: 'browser/branding/official'
nightly:
- git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-2'
+ git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-1'
tag_gpg_id: 0
var:
branding_directory: 'browser/branding/nightly'
diff --git a/projects/go/config b/projects/go/config
index b4e2c96..187606a 100644
--- a/projects/go/config
+++ b/projects/go/config
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 1.17.5
+version: 1.17.6
filename: '[% project %]-[% c("version") %]-[% c("var/build_id") %].tar.gz'
var:
@@ -118,7 +118,7 @@ input_files:
enable: '[% ! c("var/linux") %]'
- URL: 'https://golang.org/dl/go[% c("version") %].src.tar.gz'
name: go
- sha256sum: 3defb9a09bed042403195e872dcbc8c6fae1485963332279668ec52e80a95a2d
+ sha256sum: 4dc1bbf3ff61f0c1ff2b19355e6d88151a70126268a47c761477686ef94748c8
- URL: 'https://golang.org/dl/go[% c("var/go14_version") %].src.tar.gz'
name: go14
sha256sum: 9947fc705b0b841b5938c48b22dc33e9647ec0752bae66e50278df4f23f64959
diff --git a/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt b/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
index f55d6ea..476d139 100644
--- a/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
+++ b/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
@@ -1,3 +1,57 @@
+Tor Browser 11.5a4 - February 15 2022
+ * Windows + OS X + Linux
+ * Update Firefox to 91.6.0esr
+ * Update NoScript to 11.2.19
+ * Tor Launcher 0.2.33
+ * Bug 40562: Reorganize patchset [tor-browser]
+ * Bug 40598: Remove legacy settings read from TorSettings module [tor-browser]
+ * Bug 40679: Missing features on first-time launch in esr91 [tor-browser]
+ * Added extensions.torlauncher.launch_delay debug pref to simulate slow tor daemon launch [tor-launcher]
+ * Bug 40752: Misleading UX when about:tor as New Tab [tor-browser]
+ * Bug 40775: about:ion should no tbe labeled as a Tor Browser page [tor-browser]
+ * Bug 40793: moved Tor configuration options from old-configure.in to moz.configure [tor-browser]
+ * Bug 40795: Revert Deutsche Welle v2 redirect [tor-browser]
+ * Build System
+ * Windows + OS X + Linux
+ * Update Go to 1.17.6
+ * Bug 40416: Pick up obfsproxy 0.0.12 [tor-browser-build]
+
+Tor Browser 11.0.6 - February 8 2022
+ * Windows + OS X + Linux
+ * Update Firefox to 91.6.0esr
+ * Update NoScript to 11.2.16
+ * Tor Launcher 0.2.33
+ * Bug 40795: Revert Deutsche Welle v2 redirect [tor-browser]
+ * Bug 40679: Missing features on first-time launch in esr91 [tor-browser]
+ * Added extensions.torlauncher.launch_delay debug pref to simulate slow tor daemon launch [tor-launcher]
+ * Build System
+ * Windows + OS X + Linux + Android
+ * Update Go to 1.16.13
+ * Bug 40413: Removed lsb_release from Python build script [tor-browser-build]
+ * Bug 40416: Pick up obfsproxy 0.0.12 [tor-browser-build]
+
+Tor Browser 11.0.5 - February 3 2022
+ * Android
+ * Update Fenix to 94.1.1
+ * Update Tor to 0.4.6.9
+ * Update NoScript to 11.2.16
+ * Update OpenSSL to 1.1.1m
+ * Bug 40006: Add new default obfs4 bridge "deusexmachina" [tor-android-service]
+ * Bug 40198: Spoof English toggle now overlaps with locale list [fenix]
+ * Bug 40393: Point to a forked version of pion/dtls with fingerprinting fix [tor-browser-build]
+ * Bug 40394: Bump version of Snowflake to 221f1c41 [tor-browser-build]
+ * Bug 40398: Jetify tor-android-service packages [tor-browser-build]
+ * Bug 40682: Disable network.proxy.allow_bypass [tor-browser]
+ * Bug 40736: Disable third-party cookies in Private Browsing Mode [tor-browser]
+ * Build System
+ * Android
+ * Bug 40366: Use bullseye to build https-everywhere [tor-browser-build]
+ * Bug 40368: Use system's python3 for android builds [tor-browser-build]
+ * Bug 40373: Update components for mozilla93 [tor-browser-build]
+ * Bug 40379: Update components for mozilla94 [tor-browser-build]
+ * Bug 40395: Update node to 12.22.1 [tor-browser-build]
+ * Bug 40403: Update Go to 1.16.12 [tor-browser-build]
+
Tor Browser 11.5a3 - January 14 2022
* Android
* Update Fenix to 94.1.1
@@ -19,6 +73,7 @@ Tor Browser 11.5a3 - January 14 2022
* Bug 40373: Update components for mozilla93 [tor-browser-build]
* Bug 40379: Update components for mozilla94 [tor-browser-build]
* Bug 40395: Update node to 12.22.1 [tor-browser-build]
+ * Bug 40413: Removed lsb_release from Python build script [tor-browser-build]
Tor Browser 11.5a2 - January 11 2022
* Windows + OS X + Linux
diff --git a/projects/tor-browser/config b/projects/tor-browser/config
index 76823b6..9a711b7 100644
--- a/projects/tor-browser/config
+++ b/projects/tor-browser/config
@@ -78,9 +78,9 @@ input_files:
enable: '[% ! c("var/android") %]'
- filename: Bundle-Data
enable: '[% ! c("var/android") %]'
- - URL: https://addons.cdn.mozilla.net/user-media/addons/722/noscript_security_suit…
+ - URL: https://addons.cdn.mozilla.net/user-media/addons/722/noscript_security_suit…
name: noscript
- sha256sum: 47399b94f57dde1162f74a49679c7cb3b6dfe684d4eee49842e65eae64e9982b
+ sha256sum: 627ad134968a395fd585a6455087c1c1c461b993a4db0c1471f4c5d2c0cc1177
- filename: 'RelativeLink/start-tor-browser.desktop'
enable: '[% c("var/linux") %]'
- filename: 'RelativeLink/execdesktop'
diff --git a/projects/tor-launcher/config b/projects/tor-launcher/config
index 3fa9cfc..5e9a426 100644
--- a/projects/tor-launcher/config
+++ b/projects/tor-launcher/config
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 0.2.32
+version: 0.2.33
git_url: https://git.torproject.org/tor-launcher.git
git_hash: '[% c("version") %]'
gpg_keyring: torbutton.gpg
diff --git a/rbm.conf b/rbm.conf
index 46e2881..40943dd 100644
--- a/rbm.conf
+++ b/rbm.conf
@@ -58,10 +58,10 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '11.5a3'
+ torbrowser_version: '11.5a4'
torbrowser_build: 'build1'
torbrowser_incremental_from:
- - 11.5a1
+ - 11.5a3
project_name: tor-browser
multi_lingual: 0
build_mar: 1
1
0
[tor-browser/tor-browser-91.6.0esr-11.5-1] Bug 25658: Replace security slider with security level UI
by richard@torproject.org 09 Feb '22
by richard@torproject.org 09 Feb '22
09 Feb '22
commit 3c054057fd7c9198b6c1b8cec1740969c95cc132
Author: Richard Pospesel <richard(a)torproject.org>
Date: Mon Mar 4 16:09:51 2019 -0800
Bug 25658: Replace security slider with security level UI
This patch adds a new 'securitylevel' component to Tor Browser intended
to replace the torbutton 'Security Slider'.
This component adds a new Security Level toolbar button which visually
indicates the current global security level via icon (as defined by the
extensions.torbutton.security_slider pref), a drop-down hanger with a
short description of the current security level, and a new section in
the about:preferences#privacy page where users can change their current
security level. In addition, the hanger and the preferences page will
show a visual warning when the user has modified prefs associated with
the security level and provide a one-click 'Restore Defaults' button to
get the user back on recommended settings.
Strings used by this patch are pulled from the torbutton extension, but
en-US defaults are provided if there is an error loading from the
extension. With this patch applied, the usual work-flow of "./mach build
&& ./mach run" work as expected, even if the torbutton extension is
disabled.
---
browser/base/content/browser.js | 10 +
browser/base/content/browser.xhtml | 2 +
browser/base/content/main-popupset.inc.xhtml | 1 +
browser/base/content/navigator-toolbox.inc.xhtml | 2 +
browser/components/moz.build | 1 +
browser/components/preferences/preferences.xhtml | 1 +
browser/components/preferences/privacy.inc.xhtml | 2 +
browser/components/preferences/privacy.js | 20 +
.../securitylevel/content/securityLevel.js | 527 +++++++++++++++++++++
.../securitylevel/content/securityLevelButton.css | 18 +
.../content/securityLevelButton.inc.xhtml | 7 +
.../securitylevel/content/securityLevelIcon.svg | 40 ++
.../securitylevel/content/securityLevelPanel.css | 74 +++
.../content/securityLevelPanel.inc.xhtml | 47 ++
.../content/securityLevelPreferences.css | 52 ++
.../content/securityLevelPreferences.inc.xhtml | 67 +++
browser/components/securitylevel/jar.mn | 6 +
browser/components/securitylevel/moz.build | 1 +
browser/modules/TorStrings.jsm | 4 +
.../themes/shared/customizableui/panelUI.inc.css | 3 +-
20 files changed, 884 insertions(+), 1 deletion(-)
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index ef8a19198767..abca251bf3aa 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -224,6 +224,11 @@ XPCOMUtils.defineLazyScriptGetter(
["DownloadsButton", "DownloadsIndicatorView"],
"chrome://browser/content/downloads/indicator.js"
);
+XPCOMUtils.defineLazyScriptGetter(
+ this,
+ ["SecurityLevelButton"],
+ "chrome://browser/content/securitylevel/securityLevel.js"
+);
XPCOMUtils.defineLazyScriptGetter(
this,
"gEditItemOverlay",
@@ -1760,6 +1765,9 @@ var gBrowserInit = {
// doesn't flicker as the window is being shown.
DownloadsButton.init();
+ // Init the SecuritySettingsButton
+ SecurityLevelButton.init();
+
// Certain kinds of automigration rely on this notification to complete
// their tasks BEFORE the browser window is shown. SessionStore uses it to
// restore tabs into windows AFTER important parts like gMultiProcessBrowser
@@ -2493,6 +2501,8 @@ var gBrowserInit = {
DownloadsButton.uninit();
+ SecurityLevelButton.uninit();
+
TorBootstrapUrlbar.uninit();
gAccessibilityServiceIndicator.uninit();
diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml
index f16307365728..627e6ac0f8a0 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -21,6 +21,8 @@
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/tabbrowser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPanel.css"?>
+<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelButton.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml
index e5bf9460b75d..3fc665c65d79 100644
--- a/browser/base/content/main-popupset.inc.xhtml
+++ b/browser/base/content/main-popupset.inc.xhtml
@@ -520,6 +520,7 @@
#include ../../components/controlcenter/content/protectionsPanel.inc.xhtml
#include ../../components/downloads/content/downloadsPanel.inc.xhtml
#include ../../../devtools/startup/enableDevToolsPopup.inc.xhtml
+#include ../../components/securitylevel/content/securityLevelPanel.inc.xhtml
#include browser-allTabsMenu.inc.xhtml
<tooltip id="dynamic-shortcut-tooltip"
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index e7f63116ff39..6ac72cb889bc 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -413,6 +413,8 @@
</box>
</toolbarbutton>
+#include ../../components/securitylevel/content/securityLevelButton.inc.xhtml
+
<toolbarbutton id="fxa-toolbar-menu-button" class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
badged="true"
onmousedown="gSync.toggleAccountPanel(this, event)"
diff --git a/browser/components/moz.build b/browser/components/moz.build
index d15ff3051593..ec8fab7fbc8f 100644
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -48,6 +48,7 @@ DIRS += [
"protocolhandler",
"resistfingerprinting",
"search",
+ "securitylevel",
"sessionstore",
"shell",
"syncedtabs",
diff --git a/browser/components/preferences/preferences.xhtml b/browser/components/preferences/preferences.xhtml
index 0923005c8b90..0139abf95cbd 100644
--- a/browser/components/preferences/preferences.xhtml
+++ b/browser/components/preferences/preferences.xhtml
@@ -12,6 +12,7 @@
<?xml-stylesheet href="chrome://browser/skin/preferences/search.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/privacy.css"?>
+<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?>
<?xml-stylesheet href="chrome://browser/content/torpreferences/torPreferences.css"?>
<!DOCTYPE html [
diff --git a/browser/components/preferences/privacy.inc.xhtml b/browser/components/preferences/privacy.inc.xhtml
index 8d51e60cb32b..d2cf2ea9c89c 100644
--- a/browser/components/preferences/privacy.inc.xhtml
+++ b/browser/components/preferences/privacy.inc.xhtml
@@ -919,6 +919,8 @@
<html:h1 data-l10n-id="security-header"/>
</hbox>
+#include ../securitylevel/content/securityLevelPreferences.inc.xhtml
+
<!-- addons, forgery (phishing) UI Security -->
<groupbox id="browsingProtectionGroup" data-category="panePrivacy" hidden="true">
<label><html:h2 data-l10n-id="security-browsing-protection"/></label>
diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js
index 35b37b099e93..bce7bb7e8a9c 100644
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -80,6 +80,13 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
}
});
+// TODO: module import via ChromeUtils.defineModuleGetter
+XPCOMUtils.defineLazyScriptGetter(
+ this,
+ ["SecurityLevelPreferences"],
+ "chrome://browser/content/securitylevel/securityLevel.js"
+);
+
XPCOMUtils.defineLazyServiceGetter(
this,
"listManager",
@@ -308,6 +315,18 @@ function setUpContentBlockingWarnings() {
var gPrivacyPane = {
_pane: null,
+ /**
+ * Show the Security Level UI
+ */
+ _initSecurityLevel() {
+ SecurityLevelPreferences.init();
+ let unload = () => {
+ window.removeEventListener("unload", unload);
+ SecurityLevelPreferences.uninit();
+ };
+ window.addEventListener("unload", unload);
+ },
+
/**
* Whether the prompt to restart Firefox should appear when changing the autostart pref.
*/
@@ -503,6 +522,7 @@ var gPrivacyPane = {
this.trackingProtectionReadPrefs();
this.networkCookieBehaviorReadPrefs();
this._initTrackingProtectionExtensionControl();
+ this._initSecurityLevel();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
diff --git a/browser/components/securitylevel/content/securityLevel.js b/browser/components/securitylevel/content/securityLevel.js
new file mode 100644
index 000000000000..8b8babe5b58e
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevel.js
@@ -0,0 +1,527 @@
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ CustomizableUI: "resource:///modules/CustomizableUI.jsm",
+ PanelMultiView: "resource:///modules/PanelMultiView.jsm",
+});
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TorStrings",
+ "resource:///modules/TorStrings.jsm"
+);
+
+/*
+ Security Level Prefs
+
+ Getters and Setters for relevant torbutton prefs
+*/
+const SecurityLevelPrefs = {
+ security_slider_pref : "extensions.torbutton.security_slider",
+ security_custom_pref : "extensions.torbutton.security_custom",
+
+ get securitySlider() {
+ try {
+ return Services.prefs.getIntPref(this.security_slider_pref);
+ } catch(e) {
+ // init pref to 4 (standard)
+ const val = 4;
+ Services.prefs.setIntPref(this.security_slider_pref, val);
+ return val;
+ }
+ },
+
+ set securitySlider(val) {
+ Services.prefs.setIntPref(this.security_slider_pref, val);
+ },
+
+ get securityCustom() {
+ try {
+ return Services.prefs.getBoolPref(this.security_custom_pref);
+ } catch(e) {
+ // init custom to false
+ const val = false;
+ Services.prefs.setBoolPref(this.security_custom_pref, val);
+ return val;
+ }
+ },
+
+ set securityCustom(val) {
+ Services.prefs.setBoolPref(this.security_custom_pref, val);
+ },
+}; /* Security Level Prefs */
+
+/*
+ Security Level Button Code
+
+ Controls init and update of the security level toolbar button
+*/
+
+const SecurityLevelButton = {
+ _securityPrefsBranch : null,
+
+ _populateXUL : function(securityLevelButton) {
+ if (securityLevelButton != null) {
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.securityLevel);
+ securityLevelButton.setAttribute("label", TorStrings.securityLevel.securityLevel);
+ }
+ },
+
+ _configUIFromPrefs : function(securityLevelButton) {
+ if (securityLevelButton != null) {
+ let securitySlider = SecurityLevelPrefs.securitySlider;
+ securityLevelButton.removeAttribute("level");
+ const securityCustom = SecurityLevelPrefs.securityCustom;
+ switch(securitySlider) {
+ case 4:
+ securityLevelButton.setAttribute("level", `standard${securityCustom ? "_custom" : ""}`);
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.standard.tooltip);
+ break;
+ case 2:
+ securityLevelButton.setAttribute("level", `safer${securityCustom ? "_custom" : ""}`);
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.safer.tooltip);
+ break;
+ case 1:
+ securityLevelButton.setAttribute("level", `safest${securityCustom ? "_custom" : ""}`);
+ securityLevelButton.setAttribute("tooltiptext", TorStrings.securityLevel.safest.tooltip);
+ break;
+ }
+ }
+ },
+
+ get button() {
+ let button = document.getElementById("security-level-button");
+ if (!button) {
+ return null;
+ }
+ return button;
+ },
+
+ get anchor() {
+ let anchor = this.button.icon;
+ if (!anchor) {
+ return null;
+ }
+
+ anchor.setAttribute("consumeanchor", SecurityLevelButton.button.id);
+ return anchor;
+ },
+
+ init : function() {
+ // set the initial class based off of the current pref
+ let button = this.button;
+ this._populateXUL(button);
+ this._configUIFromPrefs(button);
+
+ this._securityPrefsBranch = Services.prefs.getBranch("extensions.torbutton.");
+ this._securityPrefsBranch.addObserver("", this, false);
+
+ CustomizableUI.addListener(this);
+
+ SecurityLevelPanel.init();
+ },
+
+ uninit : function() {
+ CustomizableUI.removeListener(this);
+
+ this._securityPrefsBranch.removeObserver("", this);
+ this._securityPrefsBranch = null;
+
+ SecurityLevelPanel.uninit();
+ },
+
+ observe : function(subject, topic, data) {
+ switch(topic) {
+ case "nsPref:changed":
+ if (data === "security_slider" || data === "security_custom") {
+ this._configUIFromPrefs(this.button);
+ }
+ break;
+ }
+ },
+
+ // callback for entering the 'Customize Firefox' screen to set icon
+ onCustomizeStart : function(window) {
+ let navigatorToolbox = document.getElementById("navigator-toolbox");
+ let button = navigatorToolbox.palette.querySelector("#security-level-button");
+ this._populateXUL(button);
+ this._configUIFromPrefs(button);
+ },
+
+ // callback when CustomizableUI modifies DOM
+ onWidgetAfterDOMChange : function(aNode, aNextNode, aContainer, aWasRemoval) {
+ if (aNode.id == "security-level-button" && !aWasRemoval) {
+ this._populateXUL(aNode);
+ this._configUIFromPrefs(aNode);
+ }
+ },
+
+ // for when the toolbar button needs to be activated and displays the Security Level panel
+ //
+ // In the toolbarbutton xul you'll notice we register this callback for both onkeypress and
+ // onmousedown. We do this to match the behavior of other panel spawning buttons such as Downloads,
+ // Library, and the Hamburger menus. Using oncommand alone would result in only getting fired
+ // after onclick, which is mousedown followed by mouseup.
+ onCommand : function(aEvent) {
+ // snippet borrowed from /browser/components/downloads/content/indicator.js DownloadsIndicatorView.onCommand(evt)
+ if (
+ // On Mac, ctrl-click will send a context menu event from the widget, so
+ // we don't want to bring up the panel when ctrl key is pressed.
+ (aEvent.type == "mousedown" &&
+ (aEvent.button != 0 ||
+ (AppConstants.platform == "macosx" && aEvent.ctrlKey))) ||
+ (aEvent.type == "keypress" && aEvent.key != " " && aEvent.key != "Enter")
+ ) {
+ return;
+ }
+
+ // we need to set this attribute for the button to be shaded correctly to look like it is pressed
+ // while the security level panel is open
+ this.button.setAttribute("open", "true");
+ SecurityLevelPanel.show();
+ aEvent.stopPropagation();
+ },
+}; /* Security Level Button */
+
+/*
+ Security Level Panel Code
+
+ Controls init and update of the panel in the security level hanger
+*/
+
+const SecurityLevelPanel = {
+ _securityPrefsBranch : null,
+ _panel : null,
+ _anchor : null,
+ _populated : false,
+
+ _selectors: Object.freeze({
+ panel: "panel#securityLevel-panel",
+ icon: "vbox#securityLevel-vbox>vbox",
+ header: "h1#securityLevel-header",
+ level: "label#securityLevel-level",
+ custom: "label#securityLevel-custom",
+ summary: "description#securityLevel-summary",
+ learnMore: "label#securityLevel-learnMore",
+ restoreDefaults: "button#securityLevel-restoreDefaults",
+ advancedSecuritySettings: "button#securityLevel-advancedSecuritySettings",
+ }),
+
+ _populateXUL : function() {
+ let selectors = this._selectors;
+
+ this._elements = {
+ panel: document.querySelector(selectors.panel),
+ icon: document.querySelector(selectors.icon),
+ header: document.querySelector(selectors.header),
+ levelLabel: document.querySelector(selectors.level),
+ customLabel: document.querySelector(selectors.custom),
+ summaryDescription: document.querySelector(selectors.summary),
+ learnMoreLabel: document.querySelector(selectors.learnMore),
+ restoreDefaultsButton: document.querySelector(selectors.restoreDefaults),
+ changeButton: document.querySelector(selectors.advancedSecuritySettings),
+ };
+ let elements = this._elements;
+
+ elements.header.textContent = TorStrings.securityLevel.securityLevel;
+ elements.customLabel.setAttribute("value", TorStrings.securityLevel.customWarning);
+ elements.learnMoreLabel.setAttribute("value", TorStrings.securityLevel.learnMore);
+ elements.learnMoreLabel.setAttribute("href", TorStrings.securityLevel.learnMoreURL);
+ elements.restoreDefaultsButton.setAttribute("label", TorStrings.securityLevel.restoreDefaults);
+ elements.changeButton.setAttribute("label", TorStrings.securityLevel.change);
+
+ this._configUIFromPrefs();
+ this._populated = true;
+ },
+
+ _configUIFromPrefs : function() {
+ // get security prefs
+ let securitySlider = SecurityLevelPrefs.securitySlider;
+ let securityCustom = SecurityLevelPrefs.securityCustom;
+
+ // get the panel elements we need to populate
+ let elements = this._elements;
+ let icon = elements.icon;
+ let labelLevel = elements.levelLabel;
+ let labelCustomWarning = elements.customLabel;
+ let summary = elements.summaryDescription;
+ let buttonRestoreDefaults = elements.restoreDefaultsButton;
+ let buttonAdvancedSecuritySettings = elements.changeButton;
+
+ // only visible when user is using custom settings
+ labelCustomWarning.hidden = !securityCustom;
+ buttonRestoreDefaults.hidden = !securityCustom;
+
+ // Descriptions change based on security level
+ switch(securitySlider) {
+ // standard
+ case 4:
+ icon.setAttribute("level", "standard");
+ labelLevel.setAttribute("value", TorStrings.securityLevel.standard.level);
+ summary.textContent = TorStrings.securityLevel.standard.summary;
+ break;
+ // safer
+ case 2:
+ icon.setAttribute("level", "safer");
+ labelLevel.setAttribute("value", TorStrings.securityLevel.safer.level);
+ summary.textContent = TorStrings.securityLevel.safer.summary;
+ break;
+ // safest
+ case 1:
+ icon.setAttribute("level", "safest");
+ labelLevel.setAttribute("value", TorStrings.securityLevel.safest.level);
+ summary.textContent = TorStrings.securityLevel.safest.summary;
+ break;
+ }
+
+ // override the summary text with custom warning
+ if (securityCustom) {
+ summary.textContent = TorStrings.securityLevel.custom.summary;
+ }
+ },
+
+ init : function() {
+ this._securityPrefsBranch = Services.prefs.getBranch("extensions.torbutton.");
+ this._securityPrefsBranch.addObserver("", this, false);
+ },
+
+ uninit : function() {
+ this._securityPrefsBranch.removeObserver("", this);
+ this._securityPrefsBranch = null;
+ },
+
+ show : function() {
+ // we have to defer this until after the browser has finished init'ing before
+ // we can populate the panel
+ if (!this._populated) {
+ this._populateXUL();
+ }
+
+ let panel = document.getElementById("securityLevel-panel");
+ panel.hidden = false;
+ PanelMultiView.openPopup(panel, SecurityLevelButton.anchor, "bottomcenter topright",
+ 0, 0, false, null).catch(Cu.reportError);
+ },
+
+ hide : function() {
+ let panel = document.getElementById("securityLevel-panel");
+ PanelMultiView.hidePopup(panel);
+ },
+
+ restoreDefaults : function() {
+ SecurityLevelPrefs.securityCustom = false;
+ // hide and reshow so that layout re-renders properly
+ this.hide();
+ this.show(this._anchor);
+ },
+
+ openAdvancedSecuritySettings : function() {
+ openPreferences("privacy-securitylevel");
+ this.hide();
+ },
+
+ // callback when prefs change
+ observe : function(subject, topic, data) {
+ switch(topic) {
+ case "nsPref:changed":
+ if (data == "security_slider" || data == "security_custom") {
+ this._configUIFromPrefs();
+ }
+ break;
+ }
+ },
+
+ // callback when the panel is displayed
+ onPopupShown : function(event) {
+ SecurityLevelButton.button.setAttribute("open", "true");
+ },
+
+ // callback when the panel is hidden
+ onPopupHidden : function(event) {
+ SecurityLevelButton.button.removeAttribute("open");
+ }
+}; /* Security Level Panel */
+
+/*
+ Security Level Preferences Code
+
+ Code to handle init and update of security level section in about:preferences#privacy
+*/
+
+const SecurityLevelPreferences =
+{
+ _securityPrefsBranch : null,
+
+ _populateXUL : function() {
+ let groupbox = document.getElementById("securityLevel-groupbox");
+
+ let labelHeader = groupbox.querySelector("#securityLevel-header");
+ labelHeader.textContent = TorStrings.securityLevel.securityLevel;
+
+ let spanOverview = groupbox.querySelector("#securityLevel-overview");
+ spanOverview.textContent = TorStrings.securityLevel.overview;
+
+ let labelLearnMore = groupbox.querySelector("#securityLevel-learnMore");
+ labelLearnMore.setAttribute("value", TorStrings.securityLevel.learnMore);
+ labelLearnMore.setAttribute("href", TorStrings.securityLevel.learnMoreURL);
+
+ let radiogroup = document.getElementById("securityLevel-radiogroup");
+ radiogroup.addEventListener("command", SecurityLevelPreferences.selectSecurityLevel);
+
+ let populateRadioElements = function(vboxQuery, stringStruct) {
+ let vbox = groupbox.querySelector(vboxQuery);
+
+ let radio = vbox.querySelector("radio");
+ radio.setAttribute("label", stringStruct.level);
+
+ let customWarning = vbox.querySelector("#securityLevel-customWarning");
+ customWarning.setAttribute("value", TorStrings.securityLevel.customWarning);
+
+ let labelSummary = vbox.querySelector("#securityLevel-summary");
+ labelSummary.textContent = stringStruct.summary;
+
+ let labelRestoreDefaults = vbox.querySelector("#securityLevel-restoreDefaults");
+ labelRestoreDefaults.setAttribute("value", TorStrings.securityLevel.restoreDefaults);
+ labelRestoreDefaults.addEventListener("click", SecurityLevelPreferences.restoreDefaults);
+
+ let description1 = vbox.querySelector("#securityLevel-description1");
+ if (description1) {
+ description1.textContent = stringStruct.description1;
+ }
+ let description2 = vbox.querySelector("#securityLevel-description2");
+ if (description2) {
+ description2.textContent = stringStruct.description2;
+ }
+ let description3 = vbox.querySelector("#securityLevel-description3");
+ if (description3) {
+ description3.textContent = stringStruct.description3;
+ }
+ };
+
+ populateRadioElements("#securityLevel-vbox-standard", TorStrings.securityLevel.standard);
+ populateRadioElements("#securityLevel-vbox-safer", TorStrings.securityLevel.safer);
+ populateRadioElements("#securityLevel-vbox-safest", TorStrings.securityLevel.safest);
+ },
+
+ _configUIFromPrefs : function() {
+ // read our prefs
+ let securitySlider = SecurityLevelPrefs.securitySlider;
+ let securityCustom = SecurityLevelPrefs.securityCustom;
+
+ // get our elements
+ let groupbox = document.getElementById("securityLevel-groupbox");
+
+ let radiogroup = groupbox.querySelector("#securityLevel-radiogroup");
+ let labelStandardCustom = groupbox.querySelector("#securityLevel-vbox-standard label#securityLevel-customWarning");
+ let labelSaferCustom = groupbox.querySelector("#securityLevel-vbox-safer label#securityLevel-customWarning");
+ let labelSafestCustom = groupbox.querySelector("#securityLevel-vbox-safest label#securityLevel-customWarning");
+ let labelStandardRestoreDefaults = groupbox.querySelector("#securityLevel-vbox-standard label#securityLevel-restoreDefaults");
+ let labelSaferRestoreDefaults = groupbox.querySelector("#securityLevel-vbox-safer label#securityLevel-restoreDefaults");
+ let labelSafestRestoreDefaults = groupbox.querySelector("#securityLevel-vbox-safest label#securityLevel-restoreDefaults");
+
+ // hide custom label by default until we know which level we're at
+ labelStandardCustom.hidden = true;
+ labelSaferCustom.hidden = true;
+ labelSafestCustom.hidden = true;
+
+ labelStandardRestoreDefaults.hidden = true;
+ labelSaferRestoreDefaults.hidden = true;
+ labelSafestRestoreDefaults.hidden = true;
+
+ switch(securitySlider) {
+ // standard
+ case 4:
+ radiogroup.value = "standard";
+ labelStandardCustom.hidden = !securityCustom;
+ labelStandardRestoreDefaults.hidden = !securityCustom;
+ break;
+ // safer
+ case 2:
+ radiogroup.value = "safer";
+ labelSaferCustom.hidden = !securityCustom;
+ labelSaferRestoreDefaults.hidden = !securityCustom;
+ break;
+ // safest
+ case 1:
+ radiogroup.value = "safest";
+ labelSafestCustom.hidden = !securityCustom;
+ labelSafestRestoreDefaults.hidden = !securityCustom;
+ break;
+ }
+ },
+
+ init : function() {
+ // populate XUL with localized strings
+ this._populateXUL();
+
+ // read prefs and populate UI
+ this._configUIFromPrefs();
+
+ // register for pref chagnes
+ this._securityPrefsBranch = Services.prefs.getBranch("extensions.torbutton.");
+ this._securityPrefsBranch.addObserver("", this, false);
+ },
+
+ uninit : function() {
+ // unregister for pref change events
+ this._securityPrefsBranch.removeObserver("", this);
+ this._securityPrefsBranch = null;
+ },
+
+ // callback for when prefs change
+ observe : function(subject, topic, data) {
+ switch(topic) {
+ case "nsPref:changed":
+ if (data == "security_slider" ||
+ data == "security_custom") {
+ this._configUIFromPrefs();
+ }
+ break;
+ }
+ },
+
+ selectSecurityLevel : function() {
+ // radio group elements
+ let radiogroup = document.getElementById("securityLevel-radiogroup");
+
+ // update pref based on selected radio option
+ switch (radiogroup.value) {
+ case "standard":
+ SecurityLevelPrefs.securitySlider = 4;
+ break;
+ case "safer":
+ SecurityLevelPrefs.securitySlider = 2;
+ break;
+ case "safest":
+ SecurityLevelPrefs.securitySlider = 1;
+ break;
+ }
+
+ SecurityLevelPreferences.restoreDefaults();
+ },
+
+ restoreDefaults : function() {
+ SecurityLevelPrefs.securityCustom = false;
+ },
+}; /* Security Level Prefereces */
+
+Object.defineProperty(this, "SecurityLevelButton", {
+ value: SecurityLevelButton,
+ enumerable: true,
+ writable: false
+});
+
+Object.defineProperty(this, "SecurityLevelPanel", {
+ value: SecurityLevelPanel,
+ enumerable: true,
+ writable: false
+});
+
+Object.defineProperty(this, "SecurityLevelPreferences", {
+ value: SecurityLevelPreferences,
+ enumerable: true,
+ writable: false
+});
diff --git a/browser/components/securitylevel/content/securityLevelButton.css b/browser/components/securitylevel/content/securityLevelButton.css
new file mode 100644
index 000000000000..38701250e9c9
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevelButton.css
@@ -0,0 +1,18 @@
+toolbarbutton#security-level-button[level="standard"] {
+ list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#standard");
+}
+toolbarbutton#security-level-button[level="safer"] {
+ list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safer");
+}
+toolbarbutton#security-level-button[level="safest"] {
+ list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safest");
+}
+toolbarbutton#security-level-button[level="standard_custom"] {
+ list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#standard_custom");
+}
+toolbarbutton#security-level-button[level="safer_custom"] {
+ list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safer_custom");
+}
+toolbarbutton#security-level-button[level="safest_custom"] {
+ list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safest_custom");
+}
\ No newline at end of file
diff --git a/browser/components/securitylevel/content/securityLevelButton.inc.xhtml b/browser/components/securitylevel/content/securityLevelButton.inc.xhtml
new file mode 100644
index 000000000000..96ee1ec0ca49
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevelButton.inc.xhtml
@@ -0,0 +1,7 @@
+<toolbarbutton id="security-level-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+ badged="true"
+ removable="true"
+ onmousedown="SecurityLevelButton.onCommand(event);"
+ onkeypress="SecurityLevelButton.onCommand(event);"
+ closemenu="none"
+ cui-areatype="toolbar"/>
diff --git a/browser/components/securitylevel/content/securityLevelIcon.svg b/browser/components/securitylevel/content/securityLevelIcon.svg
new file mode 100644
index 000000000000..38cdbcb68afc
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevelIcon.svg
@@ -0,0 +1,40 @@
+<svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ </style>
+ <defs>
+ <g id="standard_icon" stroke="none" stroke-width="1">
+ <path clip-rule="evenodd" d="m8.49614.283505c-.30743-.175675-.68485-.175675-.99228.000001l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 4.0133-.9164 7-6 7-10v-1.41968c0-.35886-.1923-.6902-.5039-.86824zm-.49614 1.216495-5.75 3.28571v1.2746c0 1.71749.65238 3.7522 1.78726 5.46629 1.07287 1.6204 2.47498 2.8062 3.96274 3.2425 1.48776-.4363 2.8899-1.6221 3.9627-3.2425 1.1349-1.71409 1.7873-3.7488 1.7873-5.46629v-1.2746z" fill-rule="evenodd" />
+ </g>
+ <g id="safer_icon" stroke="none" stroke-width="1">
+ <path clip-rule="evenodd" d="m8.49614.283505c-.30743-.175675-.68485-.175675-.99228.000001l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 4.0133-.9164 7-6 7-10v-1.41968c0-.35886-.1923-.6902-.5039-.86824zm-.49614 1.216495-5.75 3.28571v1.2746c0 1.71749.65238 3.7522 1.78726 5.46629 1.07287 1.6204 2.47498 2.8062 3.96274 3.2425 1.48776-.4363 2.8899-1.6221 3.9627-3.2425 1.1349-1.71409 1.7873-3.7488 1.7873-5.46629v-1.2746z" fill-rule="evenodd"/>
+ <path d="m3.5 6.12062v-.40411c0-.08972.04807-.17255.12597-.21706l4-2.28572c.16666-.09523.37403.02511.37403.21707v10.0766c-1.01204-.408-2.054-1.3018-2.92048-2.6105-1.02134-1.54265-1.57952-3.34117-1.57952-4.77628z"/>
+ </g>
+ <g id="safest_icon" stroke="none" stroke-width="1">
+ <path clip-rule="evenodd" d="m8.49614.283505c-.30743-.175675-.68485-.175675-.99228.000001l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 4.0133-.9164 7-6 7-10v-1.41968c0-.35886-.1923-.6902-.5039-.86824zm-.49614 1.216495-5.75 3.28571v1.2746c0 1.71749.65238 3.7522 1.78726 5.46629 1.07287 1.6204 2.47498 2.8062 3.96274 3.2425 1.48776-.4363 2.8899-1.6221 3.9627-3.2425 1.1349-1.71409 1.7873-3.7488 1.7873-5.46629v-1.2746z" fill-rule="evenodd"/>
+ <path d="m3.5 6.12062v-.40411c0-.08972.04807-.17255.12597-.21706l4.25-2.42857c.07685-.04392.17121-.04392.24806 0l4.24997 2.42857c.0779.04451.126.12734.126.21706v.40411c0 1.43511-.5582 3.23363-1.5795 4.77628-.8665 1.3087-1.90846 2.2025-2.9205 2.6105-1.01204-.408-2.054-1.3018-2.92048-2.6105-1.02134-1.54265-1.57952-3.34117-1.57952-4.77628z"/>
+ </g>
+ <g id="standard_custom_icon" stroke="none" stroke-width="1">
+ <path d="m9.37255.784312-.87641-.500806c-.30743-.175676-.68485-.175676-.99228 0l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 3.7599-.8585 6.6186-5.3745 6.9647-9.23043-.4008.20936-.8392.35666-1.3024.42914-.2132 1.43414-.8072 2.98009-1.6996 4.32789-1.0728 1.6204-2.47494 2.8062-3.9627 3.2425-1.48776-.4363-2.88987-1.6221-3.96274-3.2425-1.13488-1.71409-1.78726-3.7488-1.78726-5.46629v-1.2746l5.75-3.28571.86913.49664c.10502-.43392.27664-.84184.50342-1.212328z"/>
+ <circle cx="13" cy="3" fill="#ffbd2e" r="3"/>
+ </g>
+ <g id="safer_custom_icon" stroke="none" stroke-width="1">
+ <path d="m9.37255.784312-.87641-.500806c-.30743-.175676-.68485-.175676-.99228 0l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 3.7599-.8585 6.6186-5.3745 6.9647-9.23043-.4008.20936-.8392.35666-1.3024.42914-.2132 1.43414-.8072 2.98009-1.6996 4.32789-1.0728 1.6204-2.47494 2.8062-3.9627 3.2425-1.48776-.4363-2.88987-1.6221-3.96274-3.2425-1.13488-1.71409-1.78726-3.7488-1.78726-5.46629v-1.2746l5.75-3.28571.86913.49664c.10502-.43392.27664-.84184.50342-1.212328z"/>
+ <path d="m3.5 6.12062v-.40411c0-.08972.04807-.17255.12597-.21706l4-2.28572c.16666-.09523.37403.02511.37403.21707v10.0766c-1.01204-.408-2.054-1.3018-2.92048-2.6105-1.02134-1.54265-1.57952-3.34117-1.57952-4.77628z"/>
+ <circle cx="13" cy="3" fill="#ffbd2e" r="3"/>
+ </g>
+ <g id="safest_custom_icon" stroke="none" stroke-width="1">
+ <path d="m9.37255.784312-.87641-.500806c-.30743-.175676-.68485-.175676-.99228 0l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 3.7599-.8585 6.6186-5.3745 6.9647-9.23043-.4008.20936-.8392.35666-1.3024.42914-.2132 1.43414-.8072 2.98009-1.6996 4.32789-1.0728 1.6204-2.47494 2.8062-3.9627 3.2425-1.48776-.4363-2.88987-1.6221-3.96274-3.2425-1.13488-1.71409-1.78726-3.7488-1.78726-5.46629v-1.2746l5.75-3.28571.86913.49664c.10502-.43392.27664-.84184.50342-1.212328z"/>
+ <path d="m8.77266 3.44151-.64863-.37064c-.07685-.04392-.17121-.04392-.24806 0l-4.25 2.42857c-.0779.04451-.12597.12735-.12597.21706v.40412c0 1.4351.55818 3.23362 1.57952 4.77618.86648 1.3087 1.90844 2.2026 2.92048 2.6106 1.01204-.408 2.054-1.3018 2.9205-2.6106.7761-1.17217 1.2847-2.49215 1.4843-3.68816-1.9219-.26934-3.43158-1.82403-3.63214-3.76713z"/>
+ <circle cx="13" cy="3" fill="#ffbd2e" r="3"/>
+ </g>
+ </defs>
+ <use id="standard" fill="context-fill" fill-opacity="context-fill-opacity" href="#standard_icon" />
+ <use id="safer" fill="context-fill" fill-opacity="context-fill-opacity" href="#safer_icon" />
+ <use id="safest" fill="context-fill" fill-opacity="context-fill-opacity" href="#safest_icon" />
+ <use id="standard_custom" fill="context-fill" fill-opacity="context-fill-opacity" href="#standard_custom_icon" />
+ <use id="safer_custom" fill="context-fill" fill-opacity="context-fill-opacity" href="#safer_custom_icon" />
+ <use id="safest_custom" fill="context-fill" fill-opacity="context-fill-opacity" href="#safest_custom_icon" />
+</svg>
diff --git a/browser/components/securitylevel/content/securityLevelPanel.css b/browser/components/securitylevel/content/securityLevelPanel.css
new file mode 100644
index 000000000000..6462c02f1594
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevelPanel.css
@@ -0,0 +1,74 @@
+/* Security Level CSS */
+
+panelview#securityLevel-panelview {
+ width: 25em;
+}
+
+vbox#securityLevel-vbox > vbox {
+ background-repeat: no-repeat;
+ /* icon center-line should be in-line with right margin */
+ /* -margin + panelWidth - imageWidth/2 */
+ background-position: calc(-16px + 25em - 4.5em) 0.4em;
+ background-size: 9em 9em;
+ -moz-context-properties: fill, fill-opacity;
+ fill-opacity: 1;
+ fill: var(--button-bgcolor);
+ min-height: 10em;
+}
+
+vbox#securityLevel-vbox > vbox[level="standard"] {
+ background-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#standard");
+}
+vbox#securityLevel-vbox > vbox[level="safer"] {
+ background-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safer");
+}
+vbox#securityLevel-vbox > vbox[level="safest"] {
+ background-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safest");
+}
+
+vbox#securityLevel-vbox > toolbarseparator {
+ margin-inline: 16px;
+}
+
+vbox#securityLevel-vbox > vbox {
+ margin-inline: 0;
+ padding-inline: 16px;
+}
+
+vbox#securityLevel-vbox > vbox * {
+ margin-inline: 0;
+}
+
+vbox#securityLevel-vbox > vbox > hbox {
+}
+
+label#securityLevel-level {
+ font-size: 1.25em;
+ font-weight: 600;
+ padding-top: 0.15em;
+}
+
+label#securityLevel-custom {
+ border-radius: 4px;
+ background-color: var(--yellow-50);
+ color: black;
+ font-size: 1em;
+ height: 1.6em;
+ line-height: 1.0em;
+ padding: 0.4em 0.5em;
+ margin-left: 1em!important;
+}
+
+description#securityLevel-summary {
+ margin-top: 1em;
+ padding-right: 5em;
+}
+
+vbox#securityLevel-vbox > hbox.panel-footer {
+ display: flex;
+}
+
+
+button#securityLevel-advancedSecuritySettings {
+ margin-block: 0;
+}
diff --git a/browser/components/securitylevel/content/securityLevelPanel.inc.xhtml b/browser/components/securitylevel/content/securityLevelPanel.inc.xhtml
new file mode 100644
index 000000000000..02d93b738ff5
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevelPanel.inc.xhtml
@@ -0,0 +1,47 @@
+<panel id="securityLevel-panel"
+ role="group"
+ type="arrow"
+ orient="vertical"
+ level="top"
+ hidden="true"
+ class="panel-no-padding"
+ onpopupshown="SecurityLevelPanel.onPopupShown(event);"
+ onpopuphidden="SecurityLevelPanel.onPopupHidden(event);">
+ <panelmultiview mainViewId="securityLevel-panelview">
+ <panelview id="securityLevel-panelview" descriptionheightworkaround="true">
+ <vbox id="securityLevel-vbox">
+ <box class="panel-header">
+ <html:h1 id="securityLevel-header"/>
+ </box>
+ <toolbarseparator></toolbarseparator>
+ <vbox>
+ <hbox>
+ <label id="securityLevel-level"/>
+ <vbox>
+ <spacer flex="1"/>
+ <label id="securityLevel-custom"/>
+ <spacer flex="1"/>
+ </vbox>
+ <spacer flex="1"/>
+ </hbox>
+ <description id="securityLevel-summary"/>
+ <hbox>
+ <label
+ id="securityLevel-learnMore"
+ class="learnMore text-link"
+ onclick="SecurityLevelPanel.hide();"
+ is="text-link"/>
+ <spacer/>
+ </hbox>
+ </vbox>
+ <hbox class="panel-footer">
+ <button id="securityLevel-restoreDefaults"
+ oncommand="SecurityLevelPanel.restoreDefaults();"/>
+ <button id="securityLevel-advancedSecuritySettings"
+ default="true"
+ oncommand="SecurityLevelPanel.openAdvancedSecuritySettings();"/>
+ </hbox>
+ </vbox>
+ </panelview>
+ </panelmultiview>
+</panel>
diff --git a/browser/components/securitylevel/content/securityLevelPreferences.css b/browser/components/securitylevel/content/securityLevelPreferences.css
new file mode 100644
index 000000000000..12a7cccffe09
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevelPreferences.css
@@ -0,0 +1,52 @@
+label#securityLevel-customWarning {
+ border-radius: 4px;
+ background-color: var(--yellow-50);
+ color: black;
+ font-size: 1em;
+ height: 1.6em;
+ padding: 0.4em 0.5em;
+}
+
+radiogroup#securityLevel-radiogroup description {
+ color: var(--in-content-page-color)!important;
+}
+
+radiogroup#securityLevel-radiogroup radio {
+ font-weight: bold;
+}
+
+radiogroup#securityLevel-radiogroup > vbox {
+ border: 1px solid var(--in-content-box-border-color);
+ border-radius: 4px;
+ margin: 3px 0;
+ padding: 9px;
+}
+
+radiogroup#securityLevel-radiogroup[value=standard] > vbox#securityLevel-vbox-standard,
+radiogroup#securityLevel-radiogroup[value=safer] > vbox#securityLevel-vbox-safer,
+radiogroup#securityLevel-radiogroup[value=safest] > vbox#securityLevel-vbox-safest {
+ --section-highlight-background-color: color-mix(in srgb, var(--in-content-accent-color) 20%, transparent);
+ background-color: var(--section-highlight-background-color);
+ border: 1px solid var(--in-content-accent-color);
+
+}
+
+vbox#securityLevel-descriptionList {
+ display: none;
+ margin-inline-start:
+}
+
+radiogroup#securityLevel-radiogroup[value=safer] > vbox#securityLevel-vbox-safer > vbox#securityLevel-descriptionList,
+radiogroup#securityLevel-radiogroup[value=safest] > vbox#securityLevel-vbox-safest > vbox#securityLevel-descriptionList {
+ display: inherit;
+}
+
+vbox#securityLevel-descriptionList > description {
+ display: list-item;
+}
+
+vbox#securityLevel-vbox-standard,
+vbox#securityLevel-vbox-safer,
+vbox#securityLevel-vbox-safest {
+ margin-top: 0.4em;
+}
diff --git a/browser/components/securitylevel/content/securityLevelPreferences.inc.xhtml b/browser/components/securitylevel/content/securityLevelPreferences.inc.xhtml
new file mode 100644
index 000000000000..b050dad81621
--- /dev/null
+++ b/browser/components/securitylevel/content/securityLevelPreferences.inc.xhtml
@@ -0,0 +1,67 @@
+<groupbox id="securityLevel-groupbox" data-category="panePrivacy" hidden="true">
+ <label><html:h2 id="securityLevel-header"/></label>
+ <vbox data-subcategory="securitylevel" flex="1">
+ <description flex="1">
+ <html:span id="securityLevel-overview" class="tail-with-learn-more"/>
+ <label id="securityLevel-learnMore" class="learnMore text-link" is="text-link"/>
+ </description>
+ <radiogroup id="securityLevel-radiogroup">
+ <vbox id="securityLevel-vbox-standard">
+ <hbox>
+ <radio value="standard"/>
+ <vbox>
+ <spacer flex="1"/>
+ <label id="securityLevel-customWarning"/>
+ <spacer flex="1"/>
+ </vbox>
+ <spacer flex="1"/>
+ </hbox>
+ <description flex="1" class="indent">
+ <html:span id="securityLevel-summary" class="tail-with-learn-more"/>
+ <label id="securityLevel-restoreDefaults"
+ class="learnMore text-link"/>
+ </description>
+ </vbox>
+ <vbox id="securityLevel-vbox-safer">
+ <hbox>
+ <radio value="safer"/>
+ <vbox>
+ <spacer flex="1"/>
+ <label id="securityLevel-customWarning"/>
+ <spacer flex="1"/>
+ </vbox>
+ </hbox>
+ <description flex="1" class="indent">
+ <html:span id="securityLevel-summary" class="tail-with-learn-more"/>
+ <label id="securityLevel-restoreDefaults"
+ class="learnMore text-link"/>
+ </description>
+ <vbox id="securityLevel-descriptionList" class="indent">
+ <description id="securityLevel-description1" class="indent"/>
+ <description id="securityLevel-description2" class="indent"/>
+ <description id="securityLevel-description3" class="indent"/>
+ </vbox>
+ </vbox>
+ <vbox id="securityLevel-vbox-safest">
+ <hbox>
+ <radio value="safest"/>
+ <vbox>
+ <spacer flex="1"/>
+ <label id="securityLevel-customWarning"/>
+ <spacer flex="1"/>
+ </vbox>
+ </hbox>
+ <description flex="1" class="indent">
+ <html:span id="securityLevel-summary" class="tail-with-learn-more"/>
+ <label id="securityLevel-restoreDefaults"
+ class="learnMore text-link"/>
+ </description>
+ <vbox id="securityLevel-descriptionList" class="indent">
+ <description id="securityLevel-description1" class="indent"/>
+ <description id="securityLevel-description2" class="indent"/>
+ <description id="securityLevel-description3" class="indent"/>
+ </vbox>
+ </vbox>
+ </radiogroup>
+ </vbox>
+</groupbox>
diff --git a/browser/components/securitylevel/jar.mn b/browser/components/securitylevel/jar.mn
new file mode 100644
index 000000000000..61aa4169f9ec
--- /dev/null
+++ b/browser/components/securitylevel/jar.mn
@@ -0,0 +1,6 @@
+browser.jar:
+ content/browser/securitylevel/securityLevel.js (content/securityLevel.js)
+ content/browser/securitylevel/securityLevelPanel.css (content/securityLevelPanel.css)
+ content/browser/securitylevel/securityLevelButton.css (content/securityLevelButton.css)
+ content/browser/securitylevel/securityLevelPreferences.css (content/securityLevelPreferences.css)
+ content/browser/securitylevel/securityLevelIcon.svg (content/securityLevelIcon.svg)
diff --git a/browser/components/securitylevel/moz.build b/browser/components/securitylevel/moz.build
new file mode 100644
index 000000000000..2661ad7cb9f3
--- /dev/null
+++ b/browser/components/securitylevel/moz.build
@@ -0,0 +1 @@
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/browser/modules/TorStrings.jsm b/browser/modules/TorStrings.jsm
index 96d3de8186e2..0ccbbb41a782 100644
--- a/browser/modules/TorStrings.jsm
+++ b/browser/modules/TorStrings.jsm
@@ -230,6 +230,10 @@ var TorStrings = {
"advanced_security_settings",
"Advanced Security Settings\u2026"
),
+ change: getString(
+ "change",
+ "Change\u2026"
+ ),
};
return retval;
})() /* Security Level Strings */,
diff --git a/browser/themes/shared/customizableui/panelUI.inc.css b/browser/themes/shared/customizableui/panelUI.inc.css
index e1d64c707518..abecf34cdb92 100644
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1430,7 +1430,8 @@ menuitem.panel-subview-footer@menuStateActive@,
#editBookmarkPanel toolbarseparator,
#downloadsPanel-mainView toolbarseparator,
.cui-widget-panelview menuseparator,
-.cui-widget-panel toolbarseparator {
+.cui-widget-panel toolbarseparator,
+#securityLevel-panel toolbarseparator {
appearance: none;
min-height: 0;
border-top: 1px solid var(--panel-separator-color);
1
0
[tor-browser/tor-browser-91.6.0esr-11.5-1] Bug 27511: Add new identity button to toolbar
by richard@torproject.org 09 Feb '22
by richard@torproject.org 09 Feb '22
09 Feb '22
commit 3248308ab7c281fb2e60836dbca4dc23055703f9
Author: Alex Catarineu <acat(a)torproject.org>
Date: Fri Oct 4 19:08:33 2019 +0200
Bug 27511: Add new identity button to toolbar
Also added 'New circuit for this site' button to CustomizableUI, but
not visible by default.
---
browser/base/content/navigator-toolbox.inc.xhtml | 10 ++++++++++
.../components/customizableui/CustomizableUI.jsm | 21 +++++++++++++++++++++
browser/themes/shared/icons/new_circuit.svg | 6 ++++++
browser/themes/shared/icons/new_identity.svg | 9 +++++++++
browser/themes/shared/jar.inc.mn | 3 +++
browser/themes/shared/toolbarbutton-icons.inc.css | 8 ++++++++
6 files changed, 57 insertions(+)
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index 6ac72cb889bc..e10e0580b8ec 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -573,6 +573,16 @@
ondragenter="newWindowButtonObserver.onDragOver(event)"
ondragexit="newWindowButtonObserver.onDragExit(event)"/>
+ <toolbarbutton id="new-identity-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&torbutton.context_menu.new_identity;"
+ oncommand="torbutton_new_identity();"
+ tooltiptext="&torbutton.context_menu.new_identity;"/>
+
+ <toolbarbutton id="new-circuit-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&torbutton.context_menu.new_circuit;"
+ oncommand="torbutton_new_circuit();"
+ tooltiptext="&torbutton.context_menu.new_circuit;"/>
+
<toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
observes="View:FullScreen"
type="checkbox"
diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm
index 8649d93347c4..5c5ab909b9a4 100644
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -79,6 +79,8 @@ const kSubviewEvents = ["ViewShowing", "ViewHiding"];
*/
var kVersion = 17;
+var kTorVersion = 1;
+
/**
* Buttons removed from built-ins by version they were removed. kVersion must be
* bumped any time a new id is added to this. Use the button id as key, and
@@ -619,6 +621,20 @@ var CustomizableUIInternal = {
navbarPlacements.splice(newPosition, 0, "save-to-pocket-button");
}
}
+
+ let currentTorVersion = gSavedState.currentTorVersion;
+ if (currentTorVersion < 1 && gSavedState.placements) {
+ let navbarPlacements = gSavedState.placements[CustomizableUI.AREA_NAVBAR];
+ if (navbarPlacements) {
+ let secLevelIndex = navbarPlacements.indexOf("security-level-button");
+ if (secLevelIndex === -1) {
+ let urlbarIndex = navbarPlacements.indexOf("urlbar-container");
+ secLevelIndex = urlbarIndex + 1;
+ navbarPlacements.splice(secLevelIndex, 0, "security-level-button");
+ }
+ navbarPlacements.splice(secLevelIndex + 1, 0, "new-identity-button");
+ }
+ }
},
_updateForNewProtonVersion() {
@@ -2528,6 +2544,10 @@ var CustomizableUIInternal = {
gSavedState.currentVersion = 0;
}
+ if (!("currentTorVersion" in gSavedState)) {
+ gSavedState.currentTorVersion = 0;
+ }
+
gSeenWidgets = new Set(gSavedState.seen || []);
gDirtyAreaCache = new Set(gSavedState.dirtyAreaCache || []);
gNewElementCount = gSavedState.newElementCount || 0;
@@ -2606,6 +2626,7 @@ var CustomizableUIInternal = {
seen: gSeenWidgets,
dirtyAreaCache: gDirtyAreaCache,
currentVersion: kVersion,
+ currentTorVersion: kTorVersion,
newElementCount: gNewElementCount,
};
diff --git a/browser/themes/shared/icons/new_circuit.svg b/browser/themes/shared/icons/new_circuit.svg
new file mode 100644
index 000000000000..ddc819946818
--- /dev/null
+++ b/browser/themes/shared/icons/new_circuit.svg
@@ -0,0 +1,6 @@
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <g stroke="none" stroke-width="1" fill="context-fill" fill-rule="evenodd" opacity="context-fill-opacity">
+ <path d="m10.707 6h3.993l.3-.3v-3.993c.0002-.09902-.0291-.19586-.084-.27825s-.1331-.14661-.2245-.18453c-.0915-.03792-.1922-.04782-.2893-.02845-.0971.01936-.1863.06713-.2562.13723l-1.459 1.459c-1.2817-1.16743-2.95335-1.813714-4.687-1.812-3.859 0-7 3.141-7 7s3.141 7 7 7c1.74123.007 3.422-.6379 4.7116-1.8079 1.2896-1.1701 2.0945-2.7804 2.2564-4.5141.0156-.1649-.0348-.32927-.1401-.4571s-.2571-.2087-.4219-.2249c-.1644-.01324-.3275.03801-.4548.1429s-.2088.2552-.2272.4191c-.1334 1.42392-.7948 2.7464-1.854 3.7072-1.0593.9609-2.43986 1.4905-3.87 1.4848-3.171 0-5.75-2.579-5.75-5.75s2.579-5.75 5.75-5.75c1.40277-.00207 2.7572.5123 3.805 1.445l-1.451 1.451c-.07.06987-.1178.15895-.1372.25597-.0194.09701-.0096.1976.0282.28903.0378.09144.1019.1696.1841.22461.0823.055.179.08437.2779.08439z"/>
+ <path d="m8 12.5c-2.48528 0-4.5-2.0147-4.5-4.5 0-2.48528 2.01472-4.5 4.5-4.5z"/>
+ </g>
+</svg>
diff --git a/browser/themes/shared/icons/new_identity.svg b/browser/themes/shared/icons/new_identity.svg
new file mode 100644
index 000000000000..096ff169c02f
--- /dev/null
+++ b/browser/themes/shared/icons/new_identity.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <g fill="context-fill" fill-opacity="context-fill-opacity">
+ <path d="m13.5383 14.5627c-.1712-.0053-.3194-.1334-.3505-.3028-.0419-.294-.1441-.5789-.3001-.8369-.2583-.1558-.5436-.2579-.838-.2998-.1694-.0313-.2974-.1793-.3026-.3501-.0053-.1708.1136-.3146.2813-.3402.2944-.0329.5762-.1254.8284-.272.1426-.2476.2313-.5243.2608-.8129.0237-.1679.1662-.2884.3372-.2851.1699.0042.3181.1295.3517.2973.0471.2931.1533.5763.312.8323.2565.1573.5396.263.8326.3109.1682.0345.2929.1836.2958.3536.0028.17-.1171.3116-.2843.3357-.2894.0285-.5669.1172-.8147.2604-.1462.2521-.2386.5335-.2717.8274-.025.167-.1675.2861-.3379.2822z"/>
+ <path d="m6.49858 2.99992c-.14675-.00459-.27377-.11436-.3004-.25961-.03593-.25196-.12354-.49621-.25729-.71731-.22137-.13358-.46594-.22109-.71822-.25699-.14526-.02682-.25492-.15363-.25945-.30004-.00454-.14641.09737-.26967.24112-.29164.25236-.02817.49393-.10747.71013-.233093.12217-.2123.19825-.449454.22353-.696834.0203-.143878.14242-.24714456.28897-.24434753.14565.00358504.27273.11100153.30149.25484453.0404.251183.13139.493923.2674.713349.21988.134841.46256.225461.71364.266481.14417.02957.25114.15744.25358.30313.00244.1457-.10035.26707-.24368.28774-.2481.02441-.48592.10041-.69835.22319-.1253.2161-.20449.45729-.23284.7092-.0214.14312-.14361.24521-.28963.24193z"/>
+ <path d="m1.82093 5.3609c-.15279-.00473-.28512-.11875-.31315-.26981-.02739-.18014-.08781-.35525-.1782-.51643-.16152-.09021-.336989-.15052-.517512-.17788-.151437-.02794-.265749-.16003-.270474-.31254-.004724-.15251.101518-.2809.251381-.30378.181146-.02145.355265-.07593.513815-.16075.08209-.15545.13363-.32622.15197-.50355.02095-.15059.14903-.25861.3025-.25512.15164.00368.28404.11525.31428.26484.03021.18029.09338.35503.18632.51538.16048.09192.33508.15452.51517.18469.1503.0308.26181.164.26435.31577.00254.15176-.10462.27819-.25404.29971-.17764.01914-.34855.07141-.50396.15412-.08502.1582-.13963.33194-.16114.5127-.022.14911-.14912.25571-.30131.25265z"/>
+ <path clip-rule="evenodd" d="m15.3213 1.06694c.2441-.244076.2441-.639804 0-.883882-.2441-.2440775-.6398-.2440774-.8839 0l-5.96506 5.965062h-.50519c-1.996-1.09517-4.49023.42233-6.49079 1.63948-.41545.25277-.80961.49258-1.173597.69335-.16756.10002-.289261.26641-.30145394.48048-.01219156.21407.06079654.41038.21802994.56743l1.243691 1.24224 2.37084-1.02603c.15392-.06661.30331.14022.18601.25753l-1.66213 1.6621 1.46329 1.4616 1.66126-1.6613c.1173-.1173.32413.0321.25752.186l-1.02482 2.3682 1.25462 1.2531c.15724.157.35379.23.56815.2178.19095-.0561.35851-.1561.45869-.3234.20012-.3592.43577-.7455.68321-1.1511 1.22241-2.0039 2.73233-4.47901 1.66484-6.47533v-.49654zm-7.46715 6.55077c1.12692 1.12692.64113 2.69369-.05278 3.70149h-.50137l-3.13-3.1492v-.5c1.00858-.68566 2.56556-1.17088 3.68415-.05229z" fill-rule="evenodd"/>
+ </g>
+</svg>
diff --git a/browser/themes/shared/jar.inc.mn b/browser/themes/shared/jar.inc.mn
index f76184171ddd..b2e469b90aa8 100644
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -236,3 +236,6 @@
skin/classic/browser/places/tree-icons.css (../shared/places/tree-icons.css)
skin/classic/browser/privatebrowsing/aboutPrivateBrowsing.css (../shared/privatebrowsing/aboutPrivateBrowsing.css)
skin/classic/browser/privatebrowsing/favicon.svg (../shared/privatebrowsing/favicon.svg)
+
+ skin/classic/browser/new_circuit.svg (../shared/icons/new_circuit.svg)
+ skin/classic/browser/new_identity.svg (../shared/icons/new_identity.svg)
diff --git a/browser/themes/shared/toolbarbutton-icons.inc.css b/browser/themes/shared/toolbarbutton-icons.inc.css
index 76d3f4212406..e3e6f6486999 100644
--- a/browser/themes/shared/toolbarbutton-icons.inc.css
+++ b/browser/themes/shared/toolbarbutton-icons.inc.css
@@ -253,6 +253,14 @@ toolbar[brighttext]:-moz-lwtheme {
list-style-image: url("chrome://browser/skin/new-tab.svg");
}
+#new-identity-button {
+ list-style-image: url("chrome://browser/skin/new_identity.svg");
+}
+
+#new-circuit-button {
+ list-style-image: url("chrome://browser/skin/new_circuit.svg");
+}
+
#privatebrowsing-button {
list-style-image: url("chrome://browser/skin/privateBrowsing.svg");
}
1
0
[tor-browser/tor-browser-91.6.0esr-11.5-1] Bug 32658: Create a new MAR signing key
by richard@torproject.org 09 Feb '22
by richard@torproject.org 09 Feb '22
09 Feb '22
commit 5cafe8c6fdd1ebcc1a858dfc91480a970591f4ae
Author: Georg Koppen <gk(a)torproject.org>
Date: Fri Jan 17 12:54:31 2020 +0000
Bug 32658: Create a new MAR signing key
It's time for our rotation again: Move the backup key in the front
position and add a new backup key.
Bug 33803: Move our primary nightly MAR signing key to tor-browser
Bug 33803: Add a secondary nightly MAR signing key
---
.../update/updater/nightly_aurora_level3_primary.der | Bin 1225 -> 1245 bytes
.../updater/nightly_aurora_level3_secondary.der | Bin 1225 -> 1245 bytes
toolkit/mozapps/update/updater/release_primary.der | Bin 1225 -> 1229 bytes
toolkit/mozapps/update/updater/release_secondary.der | Bin 1225 -> 1229 bytes
4 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der b/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
index 44fd95dcff89..d579cf801e1a 100644
Binary files a/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der and b/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der differ
diff --git a/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der b/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der
index 90f8e6e82c63..7cbfa77d06e7 100644
Binary files a/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der and b/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der differ
diff --git a/toolkit/mozapps/update/updater/release_primary.der b/toolkit/mozapps/update/updater/release_primary.der
index 1d94f88ad73b..0103a171de88 100644
Binary files a/toolkit/mozapps/update/updater/release_primary.der and b/toolkit/mozapps/update/updater/release_primary.der differ
diff --git a/toolkit/mozapps/update/updater/release_secondary.der b/toolkit/mozapps/update/updater/release_secondary.der
index 474706c4b73c..fcee3944e9b7 100644
Binary files a/toolkit/mozapps/update/updater/release_secondary.der and b/toolkit/mozapps/update/updater/release_secondary.der differ
1
0
[tor-browser/tor-browser-91.6.0esr-11.5-1] Bug 23247: Communicating security expectations for .onion
by richard@torproject.org 09 Feb '22
by richard@torproject.org 09 Feb '22
09 Feb '22
commit 4bd02658511f46279b8cdac444cc5137cf62e20d
Author: Richard Pospesel <richard(a)torproject.org>
Date: Fri Jun 8 13:38:40 2018 -0700
Bug 23247: Communicating security expectations for .onion
Encrypting pages hosted on Onion Services with SSL/TLS is redundant
(in terms of hiding content) as all traffic within the Tor network is
already fully encrypted. Therefore, serving HTTP pages from an Onion
Service is more or less fine.
Prior to this patch, Tor Browser would mostly treat pages delivered
via Onion Services as well as pages delivered in the ordinary fashion
over the internet in the same way. This created some inconsistencies
in behaviour and misinformation presented to the user relating to the
security of pages delivered via Onion Services:
- HTTP Onion Service pages did not have any 'lock' icon indicating
the site was secure
- HTTP Onion Service pages would be marked as unencrypted in the Page
Info screen
- Mixed-mode content restrictions did not apply to HTTP Onion Service
pages embedding Non-Onion HTTP content
This patch fixes the above issues, and also adds several new 'Onion'
icons to the mix to indicate all of the various permutations of Onion
Services hosted HTTP or HTTPS pages with HTTP or HTTPS content.
Strings for Onion Service Page Info page are pulled from Torbutton's
localization strings.
---
browser/base/content/browser-siteIdentity.js | 39 ++++++++-----
browser/base/content/pageinfo/security.js | 64 ++++++++++++++++++----
.../shared/identity-block/identity-block.inc.css | 19 +++++++
dom/base/nsContentUtils.cpp | 19 +++++++
dom/base/nsContentUtils.h | 5 ++
dom/base/nsGlobalWindowOuter.cpp | 3 +-
dom/ipc/WindowGlobalActor.cpp | 4 +-
dom/ipc/WindowGlobalChild.cpp | 6 +-
dom/security/nsMixedContentBlocker.cpp | 16 +++++-
.../modules/geckoview/GeckoViewProgress.jsm | 4 ++
security/manager/ssl/nsSecureBrowserUI.cpp | 12 ++++
11 files changed, 160 insertions(+), 31 deletions(-)
diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js
index acbcc79fb21e..6682ae8b096f 100644
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -142,6 +142,10 @@ var gIdentityHandler = {
);
},
+ get _uriIsOnionHost() {
+ return this._uriHasHost ? this._uri.host.toLowerCase().endsWith(".onion") : false;
+ },
+
get _isAboutNetErrorPage() {
return (
gBrowser.selectedBrowser.documentURI &&
@@ -745,9 +749,9 @@ var gIdentityHandler = {
get pointerlockFsWarningClassName() {
// Note that the fullscreen warning does not handle _isSecureInternalUI.
if (this._uriHasHost && this._isSecureConnection) {
- return "verifiedDomain";
+ return this._uriIsOnionHost ? "onionVerifiedDomain" : "verifiedDomain";
}
- return "unknownIdentity";
+ return this._uriIsOnionHost ? "onionUnknownIdentity" : "unknownIdentity";
},
/**
@@ -755,6 +759,10 @@ var gIdentityHandler = {
* built-in (returns false) or imported (returns true).
*/
_hasCustomRoot() {
+ if (!this._secInfo) {
+ return false;
+ }
+
let issuerCert = null;
issuerCert = this._secInfo.succeededCertChain[
this._secInfo.succeededCertChain.length - 1
@@ -797,11 +805,13 @@ var gIdentityHandler = {
"identity.extension.label",
[extensionName]
);
- } else if (this._uriHasHost && this._isSecureConnection) {
+ } else if (this._uriHasHost && this._isSecureConnection && this._secInfo) {
// This is a secure connection.
- this._identityBox.className = "verifiedDomain";
+ // _isSecureConnection implicitly includes onion services, which may not have an SSL certificate
+ const uriIsOnionHost = this._uriIsOnionHost;
+ this._identityBox.className = uriIsOnionHost ? "onionVerifiedDomain" : "verifiedDomain";
if (this._isMixedActiveContentBlocked) {
- this._identityBox.classList.add("mixedActiveBlocked");
+ this._identityBox.classList.add(uriIsOnionHost ? "onionMixedActiveBlocked" : "mixedActiveBlocked");
}
if (!this._isCertUserOverridden) {
// It's a normal cert, verifier is the CA Org.
@@ -812,17 +822,17 @@ var gIdentityHandler = {
}
} else if (this._isBrokenConnection) {
// This is a secure connection, but something is wrong.
- this._identityBox.className = "unknownIdentity";
+ const uriIsOnionHost = this._uriIsOnionHost;
+ this._identityBox.className = uriIsOnionHost ? "onionUnknownIdentity" : "unknownIdentity";
if (this._isMixedActiveContentLoaded) {
- this._identityBox.classList.add("mixedActiveContent");
+ this._identityBox.classList.add(uriIsOnionHost ? "onionMixedActiveContent" : "mixedActiveContent");
} else if (this._isMixedActiveContentBlocked) {
- this._identityBox.classList.add(
- "mixedDisplayContentLoadedActiveBlocked"
- );
+ this._identityBox.classList.add(uriIsOnionHost ? "onionMixedDisplayContentLoadedActiveBlocked" : "mixedDisplayContentLoadedActiveBlocked");
} else if (this._isMixedPassiveContentLoaded) {
- this._identityBox.classList.add("mixedDisplayContent");
+ this._identityBox.classList.add(uriIsOnionHost ? "onionMixedDisplayContent" : "mixedDisplayContent");
} else {
+ // TODO: ignore weak https cipher for onionsites?
this._identityBox.classList.add("weakCipher");
}
} else if (this._isAboutCertErrorPage) {
@@ -835,8 +845,8 @@ var gIdentityHandler = {
// Network errors and blocked pages get a more neutral icon
this._identityBox.className = "unknownIdentity";
} else if (this._isPotentiallyTrustworthy) {
- // This is a local resource (and shouldn't be marked insecure).
- this._identityBox.className = "localResource";
+ // This is a local resource or an onion site (and shouldn't be marked insecure).
+ this._identityBox.className = this._uriIsOnionHost ? "onionUnknownIdentity" : "localResource";
} else {
// This is an insecure connection.
let warnOnInsecure =
@@ -860,7 +870,8 @@ var gIdentityHandler = {
}
if (this._isCertUserOverridden) {
- this._identityBox.classList.add("certUserOverridden");
+ const uriIsOnionHost = this._uriIsOnionHost;
+ this._identityBox.classList.add(uriIsOnionHost ? "onionCertUserOverridden" : "certUserOverridden");
// Cert is trusted because of a security exception, verifier is a special string.
tooltip = gNavigatorBundle.getString(
"identity.identified.verified_by_you"
diff --git a/browser/base/content/pageinfo/security.js b/browser/base/content/pageinfo/security.js
index 1222c8b0ec35..8d10c8df814c 100644
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -22,6 +22,13 @@ ChromeUtils.defineModuleGetter(
"PluralForm",
"resource://gre/modules/PluralForm.jsm"
);
+XPCOMUtils.defineLazyGetter(
+ this,
+ "gTorButtonBundle",
+ function() {
+ return Services.strings.createBundle("chrome://torbutton/locale/torbutton.properties");
+ }
+);
var security = {
async init(uri, windowInfo) {
@@ -60,6 +67,11 @@ var security = {
(Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT |
Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
var isEV = ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
+ var isOnion = false;
+ const hostName = this.windowInfo.hostName;
+ if (hostName && hostName.endsWith(".onion")) {
+ isOnion = true;
+ }
let retval = {
cAName: "",
@@ -69,6 +81,7 @@ var security = {
isBroken,
isMixed,
isEV,
+ isOnion,
cert: null,
certificateTransparency: null,
};
@@ -107,6 +120,7 @@ var security = {
isBroken,
isMixed,
isEV,
+ isOnion,
cert,
certChain: certChainArray,
certificateTransparency: undefined,
@@ -348,22 +362,50 @@ async function securityOnLoad(uri, windowInfo) {
}
msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
} else if (info.encryptionStrength > 0) {
- hdr = pkiBundle.getFormattedString(
- "pageInfo_EncryptionWithBitsAndProtocol",
- [info.encryptionAlgorithm, info.encryptionStrength + "", info.version]
- );
+ if (!info.isOnion) {
+ hdr = pkiBundle.getFormattedString(
+ "pageInfo_EncryptionWithBitsAndProtocol",
+ [info.encryptionAlgorithm, info.encryptionStrength + "", info.version]
+ );
+ } else {
+ try {
+ hdr = gTorButtonBundle.formatStringFromName(
+ "pageInfo_OnionEncryptionWithBitsAndProtocol",
+ [info.encryptionAlgorithm, info.encryptionStrength + "", info.version]
+ );
+ } catch(err) {
+ hdr = "Connection Encrypted (Onion Service, "
+ + info.encryptionAlgorithm
+ + ", "
+ + info.encryptionStrength
+ + " bit keys, "
+ + info.version
+ + ")";
+ }
+ }
msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
} else {
- hdr = pkiBundle.getString("pageInfo_NoEncryption");
- if (windowInfo.hostName != null) {
- msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [
- windowInfo.hostName,
- ]);
+ if (!info.isOnion) {
+ hdr = pkiBundle.getString("pageInfo_NoEncryption");
+ if (windowInfo.hostName != null) {
+ msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [
+ windowInfo.hostName,
+ ]);
+ } else {
+ msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
+ }
+ msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
} else {
- msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
+ try {
+ hdr = gTorButtonBundle.GetStringFromName("pageInfo_OnionEncryption");
+ } catch (err) {
+ hdr = "Connection Encrypted (Onion Service)";
+ }
+
+ msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
+ msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
}
- msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
}
setText("security-technical-shortform", hdr);
setText("security-technical-longform1", msg1);
diff --git a/browser/themes/shared/identity-block/identity-block.inc.css b/browser/themes/shared/identity-block/identity-block.inc.css
index a01e7d705cd7..68ed0beed684 100644
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -203,6 +203,25 @@
list-style-image: url(chrome://global/skin/icons/security-broken.svg);
}
+#identity-box[pageproxystate="valid"].onionUnknownIdentity #identity-icon,
+#identity-box[pageproxystate="valid"].onionVerifiedDomain #identity-icon,
+#identity-box[pageproxystate="valid"].onionMixedActiveBlocked #identity-icon {
+ list-style-image: url(chrome://browser/skin/onion.svg);
+ visibility: visible;
+}
+
+#identity-box[pageproxystate="valid"].onionMixedDisplayContent #identity-icon,
+#identity-box[pageproxystate="valid"].onionMixedDisplayContentLoadedActiveBlocked #identity-icon,
+#identity-box[pageproxystate="valid"].onionCertUserOverridden #identity-icon {
+ list-style-image: url(chrome://browser/skin/onion-warning.svg);
+ visibility: visible;
+}
+
+#identity-box[pageproxystate="valid"].onionMixedActiveContent #identity-icon {
+ list-style-image: url(chrome://browser/skin/onion-slash.svg);
+ visibility: visible;
+}
+
#permissions-granted-icon {
list-style-image: url(chrome://browser/skin/permissions.svg);
}
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index fac49bab17f2..6dd69d1d81a8 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9325,6 +9325,25 @@ bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
return principal->GetIsOriginPotentiallyTrustworthy();
}
+/* static */ bool nsContentUtils::DocumentHasOnionURI(Document* aDocument) {
+ if (!aDocument) {
+ return false;
+ }
+
+ nsIURI* uri = aDocument->GetDocumentURI();
+ if (!uri) {
+ return false;
+ }
+
+ nsAutoCString host;
+ if (NS_SUCCEEDED(uri->GetHost(host))) {
+ bool hasOnionURI = StringEndsWith(host, ".onion"_ns);
+ return hasOnionURI;
+ }
+
+ return false;
+}
+
/* static */
void nsContentUtils::TryToUpgradeElement(Element* aElement) {
NodeInfo* nodeInfo = aElement->NodeInfo();
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index baa75be5b570..bb5c84886af9 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2999,6 +2999,11 @@ class nsContentUtils {
*/
static bool HttpsStateIsModern(Document* aDocument);
+ /**
+ * Returns true of the document's URI is a .onion
+ */
+ static bool DocumentHasOnionURI(Document* aDocument);
+
/**
* Returns true if the channel is for top-level window and is over secure
* context.
diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
index aab4a37e78a8..e981573e9822 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1880,7 +1880,8 @@ bool nsGlobalWindowOuter::ComputeIsSecureContext(Document* aDocument,
return false;
}
- if (nsContentUtils::HttpsStateIsModern(aDocument)) {
+ if (nsContentUtils::HttpsStateIsModern(aDocument) ||
+ nsContentUtils::DocumentHasOnionURI(aDocument)) {
return true;
}
diff --git a/dom/ipc/WindowGlobalActor.cpp b/dom/ipc/WindowGlobalActor.cpp
index 8a3b49edd4d7..9975136e8e18 100644
--- a/dom/ipc/WindowGlobalActor.cpp
+++ b/dom/ipc/WindowGlobalActor.cpp
@@ -21,6 +21,7 @@
#include "mozilla/net/CookieJarSettings.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/nsMixedContentBlocker.h"
#include "nsGlobalWindowInner.h"
#include "nsNetUtil.h"
@@ -131,7 +132,8 @@ WindowGlobalInit WindowGlobalActor::WindowInitializer(
// Init Mixed Content Fields
nsCOMPtr<nsIURI> innerDocURI = NS_GetInnermostURI(doc->GetDocumentURI());
- fields.mIsSecure = innerDocURI && innerDocURI->SchemeIs("https");
+ fields.mIsSecure = innerDocURI && (innerDocURI->SchemeIs("https") ||
+ nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI));
nsCOMPtr<nsITransportSecurityInfo> securityInfo;
if (nsCOMPtr<nsIChannel> channel = doc->GetChannel()) {
diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp
index 84c060c41534..73ac6a0cf96d 100644
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -48,6 +48,8 @@
# include "GeckoProfiler.h"
#endif
+#include "mozilla/dom/nsMixedContentBlocker.h"
+
using namespace mozilla::ipc;
using namespace mozilla::dom::ipc;
@@ -234,7 +236,9 @@ void WindowGlobalChild::OnNewDocument(Document* aDocument) {
nsCOMPtr<nsIURI> innerDocURI =
NS_GetInnermostURI(aDocument->GetDocumentURI());
if (innerDocURI) {
- txn.SetIsSecure(innerDocURI->SchemeIs("https"));
+ txn.SetIsSecure(
+ innerDocURI->SchemeIs("https") ||
+ nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI));
}
MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp
index 01c7877e020d..dab3f19bad40 100644
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -634,8 +634,8 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
return NS_OK;
}
- // Check the parent scheme. If it is not an HTTPS page then mixed content
- // restrictions do not apply.
+ // Check the parent scheme. If it is not an HTTPS or .onion page then mixed
+ // content restrictions do not apply.
nsCOMPtr<nsIURI> innerRequestingLocation =
NS_GetInnermostURI(requestingLocation);
if (!innerRequestingLocation) {
@@ -650,6 +650,17 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
bool parentIsHttps = innerRequestingLocation->SchemeIs("https");
if (!parentIsHttps) {
+ bool parentIsOnion = IsPotentiallyTrustworthyOnion(innerRequestingLocation);
+ if (!parentIsOnion) {
+ *aDecision = ACCEPT;
+ return NS_OK;
+ }
+ }
+
+ bool isHttpScheme = innerContentLocation->SchemeIs("http");
+ // .onion URLs are encrypted and authenticated. Don't treat them as mixed
+ // content if potentially trustworthy (i.e. whitelisted).
+ if (isHttpScheme && IsPotentiallyTrustworthyOnion(innerContentLocation)) {
*aDecision = ACCEPT;
MOZ_LOG(sMCBLog, LogLevel::Verbose,
(" -> decision: Request will be allowed because the requesting "
@@ -676,7 +687,6 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
return NS_OK;
}
- bool isHttpScheme = innerContentLocation->SchemeIs("http");
if (isHttpScheme && IsPotentiallyTrustworthyOrigin(innerContentLocation)) {
*aDecision = ACCEPT;
return NS_OK;
diff --git a/mobile/android/modules/geckoview/GeckoViewProgress.jsm b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
index 17069dbe657f..c1346b1858cf 100644
--- a/mobile/android/modules/geckoview/GeckoViewProgress.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewProgress.jsm
@@ -145,6 +145,10 @@ var IdentityHandler = {
result.host = uri.host;
}
+ if (!aBrowser.securityUI.secInfo) {
+ return result;
+ }
+
const cert = aBrowser.securityUI.secInfo.serverCert;
result.certificate = aBrowser.securityUI.secInfo.serverCert.getBase64DERString();
diff --git a/security/manager/ssl/nsSecureBrowserUI.cpp b/security/manager/ssl/nsSecureBrowserUI.cpp
index b4de1a331ffc..f1ce39582854 100644
--- a/security/manager/ssl/nsSecureBrowserUI.cpp
+++ b/security/manager/ssl/nsSecureBrowserUI.cpp
@@ -9,6 +9,7 @@
#include "mozilla/Logging.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/Document.h"
+#include "mozilla/dom/nsMixedContentBlocker.h"
#include "nsContentUtils.h"
#include "nsIChannel.h"
#include "nsDocShell.h"
@@ -85,6 +86,17 @@ void nsSecureBrowserUI::RecomputeSecurityFlags() {
}
}
}
+
+ // any protocol routed over tor is secure
+ if (!(mState & nsIWebProgressListener::STATE_IS_SECURE)) {
+ nsCOMPtr<nsIURI> innerDocURI = NS_GetInnermostURI(win->GetDocumentURI());
+ if (innerDocURI &&
+ nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI)) {
+ MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, (" is onion"));
+ mState = (mState & ~nsIWebProgressListener::STATE_IS_INSECURE) |
+ nsIWebProgressListener::STATE_IS_SECURE;
+ }
+ }
}
// Add upgraded-state flags when request has been
1
0
[tor-browser/tor-browser-91.6.0esr-11.5-1] Bug 13379: Sign our MAR files.
by richard@torproject.org 09 Feb '22
by richard@torproject.org 09 Feb '22
09 Feb '22
commit aac9aa4f552c3e474b80890222dec0bdddabf50e
Author: Kathy Brade <brade(a)pearlcrescent.com>
Date: Wed Dec 17 16:37:11 2014 -0500
Bug 13379: Sign our MAR files.
Configure with --enable-verify-mar (when updating, require a valid
signature on the MAR file before it is applied).
Use the Tor Browser version instead of the Firefox version inside the
MAR file info block (necessary to prevent downgrade attacks).
Use NSS on all platforms for checking MAR signatures (instead of using
OS-native APIs, which Mozilla does on Mac OS and Windows). So that the
NSS and NSPR libraries the updater depends on can be found at runtime,
we add the firefox directory to the shared library search path on macOS.
On Linux, rpath is used by Mozilla to solve that problem, but that
approach won't work on macOS because the updater executable is copied
during the update process to a location that is under TorBrowser-Data,
and the location of TorBrowser-Data varies.
Also includes the fix for bug 18900.
Bug 19121: reinstate the update.xml hash check
Revert most changes from Mozilla Bug 1373267 "Remove hashFunction and
hashValue attributes from nsIUpdatePatch and code related to these
attributes." Changes to the tests were not reverted; the tests have
been changed significantly and we do not run automated updater tests
for Tor Browser at this time.
Also partial revert of commit f1241db6986e4b54473a1ed870f7584c75d51122.
Revert the nsUpdateService.js changes from Mozilla Bug 862173 "don't
verify mar file hash when using mar signing to verify the mar file
(lessens main thread I/O)."
Changes to the tests were not reverted; the tests have been changed
significantly and we do not run automated updater tests for
Tor Browser at this time.
We kept the addition to the AppConstants API in case other JS code
references it in the future.
---
.mozconfig | 1 +
.mozconfig-asan | 1 +
.mozconfig-mac | 1 +
.mozconfig-mingw | 1 +
modules/libmar/tool/mar.c | 6 +--
modules/libmar/tool/moz.build | 12 +++--
modules/libmar/verify/moz.build | 14 ++---
toolkit/modules/AppConstants.jsm | 7 +++
toolkit/mozapps/update/UpdateService.jsm | 63 +++++++++++++++++++++-
toolkit/mozapps/update/UpdateTelemetry.jsm | 1 +
toolkit/mozapps/update/nsIUpdateService.idl | 11 ++++
.../mozapps/update/updater/updater-common.build | 24 +++++++--
toolkit/mozapps/update/updater/updater.cpp | 25 +++++----
toolkit/xre/moz.build | 3 ++
toolkit/xre/nsUpdateDriver.cpp | 50 +++++++++++++++++
15 files changed, 194 insertions(+), 26 deletions(-)
diff --git a/.mozconfig b/.mozconfig
index 7fe8633a9ef4..7655f628415e 100755
--- a/.mozconfig
+++ b/.mozconfig
@@ -37,3 +37,4 @@ ac_add_options MOZ_TELEMETRY_REPORTING=
ac_add_options --enable-tor-launcher
ac_add_options --with-tor-browser-version=dev-build
ac_add_options --disable-tor-browser-update
+ac_add_options --enable-verify-mar
diff --git a/.mozconfig-asan b/.mozconfig-asan
index 98ea6ac6f3fe..8bee813bfee8 100644
--- a/.mozconfig-asan
+++ b/.mozconfig-asan
@@ -30,6 +30,7 @@ ac_add_options --enable-official-branding
ac_add_options --enable-default-toolkit=cairo-gtk3
ac_add_options --enable-tor-browser-update
+ac_add_options --enable-verify-mar
ac_add_options --disable-strip
ac_add_options --disable-install-strip
diff --git a/.mozconfig-mac b/.mozconfig-mac
index 26e2b6b92fdb..5b4624ef1f67 100644
--- a/.mozconfig-mac
+++ b/.mozconfig-mac
@@ -43,6 +43,7 @@ ac_add_options --disable-debug
ac_add_options --enable-tor-browser-data-outside-app-dir
ac_add_options --enable-tor-browser-update
+ac_add_options --enable-verify-mar
ac_add_options --disable-crashreporter
ac_add_options --disable-webrtc
diff --git a/.mozconfig-mingw b/.mozconfig-mingw
index 3ec6ff18a3e9..ce6ace1dad67 100644
--- a/.mozconfig-mingw
+++ b/.mozconfig-mingw
@@ -15,6 +15,7 @@ ac_add_options --enable-strip
ac_add_options --enable-official-branding
ac_add_options --enable-tor-browser-update
+ac_add_options --enable-verify-mar
ac_add_options --disable-bits-download
# Let's make sure no preference is enabling either Adobe's or Google's CDM.
diff --git a/modules/libmar/tool/mar.c b/modules/libmar/tool/mar.c
index 0bf2cb4bd1d4..ea2b79924914 100644
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -65,7 +65,7 @@ static void print_usage() {
"signed_input_archive.mar base_64_encoded_signature_file "
"changed_signed_output.mar\n");
printf("(i) is the index of the certificate to extract\n");
-# if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(MAR_NSS))
+# if (defined(XP_MACOSX) || defined(XP_WIN)) && !defined(MAR_NSS)
printf("Verify a MAR file:\n");
printf(" mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
printf(
@@ -149,7 +149,7 @@ int main(int argc, char** argv) {
memset((void*)certBuffers, 0, sizeof(certBuffers));
#endif
#if !defined(NO_SIGN_VERIFY) && \
- ((!defined(MAR_NSS) && defined(XP_WIN)) || defined(XP_MACOSX))
+ (!defined(MAR_NSS) && (defined(XP_WIN) || defined(XP_MACOSX)))
memset(DERFilePaths, 0, sizeof(DERFilePaths));
memset(fileSizes, 0, sizeof(fileSizes));
#endif
@@ -181,7 +181,7 @@ int main(int argc, char** argv) {
argc -= 2;
}
#if !defined(NO_SIGN_VERIFY)
-# if (!defined(MAR_NSS) && defined(XP_WIN)) || defined(XP_MACOSX)
+# if (!defined(MAR_NSS) && (defined(XP_WIN) || defined(XP_MACOSX)))
/* -D DERFilePath, also matches -D[index] DERFilePath
We allow an index for verifying to be symmetric
with the import and export command line arguments. */
diff --git a/modules/libmar/tool/moz.build b/modules/libmar/tool/moz.build
index a6d26c66a668..d6fa1677ddf1 100644
--- a/modules/libmar/tool/moz.build
+++ b/modules/libmar/tool/moz.build
@@ -43,15 +43,21 @@ if CONFIG["MOZ_BUILD_APP"] != "tools/update-packaging":
"verifymar",
]
+ if CONFIG["TOR_BROWSER_UPDATE"]:
+ DEFINES["MAR_NSS"] = True
+
if CONFIG["OS_ARCH"] == "WINNT":
USE_STATIC_LIBS = True
OS_LIBS += [
"ws2_32",
- "crypt32",
- "advapi32",
]
- elif CONFIG["OS_ARCH"] == "Darwin":
+ if not CONFIG["TOR_BROWSER_UPDATE"]:
+ OS_LIBS += [
+ "crypt32",
+ "advapi32",
+ ]
+ elif CONFIG["OS_ARCH"] == "Darwin" and not CONFIG["TOR_BROWSER_UPDATE"]:
OS_LIBS += [
"-framework Security",
]
diff --git a/modules/libmar/verify/moz.build b/modules/libmar/verify/moz.build
index b07475655f0d..03718eee50b4 100644
--- a/modules/libmar/verify/moz.build
+++ b/modules/libmar/verify/moz.build
@@ -16,15 +16,12 @@ FORCE_STATIC_LIB = True
if CONFIG["OS_ARCH"] == "WINNT":
USE_STATIC_LIBS = True
elif CONFIG["OS_ARCH"] == "Darwin":
- UNIFIED_SOURCES += [
- "MacVerifyCrypto.cpp",
- ]
- OS_LIBS += [
- "-framework Security",
+ USE_LIBS += [
+ "nspr",
+ "nss",
+ "signmar",
]
else:
- DEFINES["MAR_NSS"] = True
- LOCAL_INCLUDES += ["../sign"]
USE_LIBS += [
"nspr",
"nss",
@@ -38,6 +35,9 @@ else:
"-Wl,-rpath=\\$$ORIGIN",
]
+DEFINES["MAR_NSS"] = True
+LOCAL_INCLUDES += ["../sign"]
+
LOCAL_INCLUDES += [
"../src",
]
diff --git a/toolkit/modules/AppConstants.jsm b/toolkit/modules/AppConstants.jsm
index ea10dc97535d..3cb1518f2ab3 100644
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -212,6 +212,13 @@ this.AppConstants = Object.freeze({
false,
#endif
+ MOZ_VERIFY_MAR_SIGNATURE:
+#ifdef MOZ_VERIFY_MAR_SIGNATURE
+ true,
+#else
+ false,
+#endif
+
MOZ_MAINTENANCE_SERVICE:
#ifdef MOZ_MAINTENANCE_SERVICE
true,
diff --git a/toolkit/mozapps/update/UpdateService.jsm b/toolkit/mozapps/update/UpdateService.jsm
index 8552240a1df6..f0a48d021638 100644
--- a/toolkit/mozapps/update/UpdateService.jsm
+++ b/toolkit/mozapps/update/UpdateService.jsm
@@ -999,6 +999,20 @@ function LOG(string) {
}
}
+/**
+ * Convert a string containing binary values to hex.
+ */
+function binaryToHex(input) {
+ var result = "";
+ for (var i = 0; i < input.length; ++i) {
+ var hex = input.charCodeAt(i).toString(16);
+ if (hex.length == 1)
+ hex = "0" + hex;
+ result += hex;
+ }
+ return result;
+}
+
/**
* Gets the specified directory at the specified hierarchy under the
* update root directory and creates it if it doesn't exist.
@@ -2022,6 +2036,8 @@ function UpdatePatch(patch) {
}
break;
case "finalURL":
+ case "hashFunction":
+ case "hashValue":
case "state":
case "type":
case "URL":
@@ -2041,6 +2057,8 @@ UpdatePatch.prototype = {
// over writing nsIUpdatePatch attributes.
_attrNames: [
"errorCode",
+ "hashFunction",
+ "hashValue",
"finalURL",
"selected",
"size",
@@ -2054,6 +2072,8 @@ UpdatePatch.prototype = {
*/
serialize: function UpdatePatch_serialize(updates) {
var patch = updates.createElementNS(URI_UPDATE_NS, "patch");
+ patch.setAttribute("hashFunction", this.hashFunction);
+ patch.setAttribute("hashValue", this.hashValue);
patch.setAttribute("size", this.size);
patch.setAttribute("type", this.type);
patch.setAttribute("URL", this.URL);
@@ -5278,7 +5298,42 @@ Downloader.prototype = {
}
LOG("Downloader:_verifyDownload downloaded size == expected size.");
- return true;
+ let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ fileStream.init(destination, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+
+ let digest;
+ try {
+ let hash = Cc["@mozilla.org/security/hash;1"].
+ createInstance(Ci.nsICryptoHash);
+ var hashFunction = Ci.nsICryptoHash[this._patch.hashFunction.toUpperCase()];
+ if (hashFunction == undefined) {
+ throw Cr.NS_ERROR_UNEXPECTED;
+ }
+ hash.init(hashFunction);
+ hash.updateFromStream(fileStream, -1);
+ // NOTE: For now, we assume that the format of _patch.hashValue is hex
+ // encoded binary (such as what is typically output by programs like
+ // sha1sum). In the future, this may change to base64 depending on how
+ // we choose to compute these hashes.
+ digest = binaryToHex(hash.finish(false));
+ } catch (e) {
+ LOG("Downloader:_verifyDownload - failed to compute hash of the " +
+ "downloaded update archive");
+ digest = "";
+ }
+
+ fileStream.close();
+
+ if (digest == this._patch.hashValue.toLowerCase()) {
+ LOG("Downloader:_verifyDownload hashes match.");
+ return true;
+ }
+
+ LOG("Downloader:_verifyDownload hashes do not match. ");
+ AUSTLMY.pingDownloadCode(this.isCompleteUpdate,
+ AUSTLMY.DWNLD_ERR_VERIFY_NO_HASH_MATCH);
+ return false;
},
/**
@@ -5875,6 +5930,9 @@ Downloader.prototype = {
" is higher than patch size: " +
this._patch.size
);
+ // It's important that we use a different code than
+ // NS_ERROR_CORRUPTED_CONTENT so that tests can verify the difference
+ // between a hash error and a wrong download error.
AUSTLMY.pingDownloadCode(
this.isCompleteUpdate,
AUSTLMY.DWNLD_ERR_PATCH_SIZE_LARGER
@@ -5893,6 +5951,9 @@ Downloader.prototype = {
" is not equal to expected patch size: " +
this._patch.size
);
+ // It's important that we use a different code than
+ // NS_ERROR_CORRUPTED_CONTENT so that tests can verify the difference
+ // between a hash error and a wrong download error.
AUSTLMY.pingDownloadCode(
this.isCompleteUpdate,
AUSTLMY.DWNLD_ERR_PATCH_SIZE_NOT_EQUAL
diff --git a/toolkit/mozapps/update/UpdateTelemetry.jsm b/toolkit/mozapps/update/UpdateTelemetry.jsm
index dae76e09acd0..df5b8917970e 100644
--- a/toolkit/mozapps/update/UpdateTelemetry.jsm
+++ b/toolkit/mozapps/update/UpdateTelemetry.jsm
@@ -192,6 +192,7 @@ var AUSTLMY = {
DWNLD_ERR_VERIFY_NO_REQUEST: 13,
DWNLD_ERR_VERIFY_PATCH_SIZE_NOT_EQUAL: 14,
DWNLD_ERR_WRITE_FAILURE: 15,
+ DWNLD_ERR_VERIFY_NO_HASH_MATCH: 16,
// Temporary failure code to see if there are failures without an update phase
DWNLD_UNKNOWN_PHASE_ERR_WRITE_FAILURE: 40,
diff --git a/toolkit/mozapps/update/nsIUpdateService.idl b/toolkit/mozapps/update/nsIUpdateService.idl
index e0a075cfd126..e3b0252b527a 100644
--- a/toolkit/mozapps/update/nsIUpdateService.idl
+++ b/toolkit/mozapps/update/nsIUpdateService.idl
@@ -39,6 +39,17 @@ interface nsIUpdatePatch : nsISupports
*/
attribute AString finalURL;
+ /**
+ * The hash function to use when determining this file's integrity
+ */
+ attribute AString hashFunction;
+
+ /**
+ * The value of the hash function named above that should be computed if
+ * this file is not corrupt.
+ */
+ attribute AString hashValue;
+
/**
* The size of this file, in bytes.
*/
diff --git a/toolkit/mozapps/update/updater/updater-common.build b/toolkit/mozapps/update/updater/updater-common.build
index 13926ea82046..a4173889271b 100644
--- a/toolkit/mozapps/update/updater/updater-common.build
+++ b/toolkit/mozapps/update/updater/updater-common.build
@@ -4,6 +4,10 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+DEFINES["MAR_NSS"] = True
+
+link_with_nss = DEFINES["MAR_NSS"] or (CONFIG["OS_ARCH"] == "Linux" and CONFIG["MOZ_VERIFY_MAR_SIGNATURE"])
+
srcs = [
"archivereader.cpp",
"updater.cpp",
@@ -36,10 +40,14 @@ if CONFIG["OS_ARCH"] == "WINNT":
"ws2_32",
"shell32",
"shlwapi",
- "crypt32",
- "advapi32",
]
+ if not link_with_nss:
+ OS_LIBS += [
+ "crypt32",
+ "advapi32",
+ ]
+
USE_LIBS += [
"bspatch",
"mar",
@@ -47,6 +55,13 @@ USE_LIBS += [
"xz-embedded",
]
+if link_with_nss:
+ USE_LIBS += [
+ "nspr",
+ "nss",
+ "signmar",
+ ]
+
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
have_progressui = 1
srcs += [
@@ -61,9 +76,12 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
]
OS_LIBS += [
"-framework Cocoa",
- "-framework Security",
"-framework SystemConfiguration",
]
+ if not link_with_nss:
+ OS_LIBS += [
+ "-framework Security",
+ ]
UNIFIED_SOURCES += [
"/toolkit/xre/updaterfileutils_osx.mm",
]
diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp
index b9b982367137..f1598ea9b529 100644
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -110,9 +110,11 @@ struct UpdateServerThreadArgs {
# define stat64 stat
#endif
-#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
-# include "nss.h"
-# include "prerror.h"
+#if defined(MOZ_VERIFY_MAR_SIGNATURE)
+# if defined(MAR_NSS) || (!defined(XP_WIN) && !defined(XP_MACOSX))
+# include "nss.h"
+# include "prerror.h"
+# endif
#endif
#include "crctable.h"
@@ -2726,8 +2728,13 @@ static void UpdateThreadFunc(void* param) {
if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) {
rv = UPDATE_SETTINGS_FILE_CHANNEL;
} else {
+# ifdef TOR_BROWSER_UPDATE
+ const char* appVersion = TOR_BROWSER_VERSION_QUOTED;
+# else
+ const char* appVersion = MOZ_APP_VERSION;
+# endif
rv = gArchiveReader.VerifyProductInformation(
- MARStrings.MARChannelID.get(), MOZ_APP_VERSION);
+ MARStrings.MARChannelID.get(), appVersion);
}
}
}
@@ -2963,11 +2970,10 @@ int NS_main(int argc, NS_tchar** argv) {
}
#endif
-#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
- // On Windows and Mac we rely on native APIs to do verifications so we don't
- // need to initialize NSS at all there.
- // Otherwise, minimize the amount of NSS we depend on by avoiding all the NSS
- // databases.
+#if defined(MOZ_VERIFY_MAR_SIGNATURE)
+# if defined(MAR_NSS) || (!defined(XP_WIN) && !defined(XP_MACOSX))
+ // If using NSS for signature verification, initialize NSS but minimize
+ // the portion we depend on by avoiding all of the NSS databases.
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
PRErrorCode error = PR_GetError();
fprintf(stderr, "Could not initialize NSS: %s (%d)", PR_ErrorToName(error),
@@ -2975,6 +2981,7 @@ int NS_main(int argc, NS_tchar** argv) {
_exit(1);
}
#endif
+#endif
#ifdef XP_MACOSX
if (!isElevated) {
diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build
index 90d06481ee9e..56a2d7173d3c 100644
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -233,6 +233,9 @@ for var in ("APP_VERSION", "APP_ID"):
if CONFIG["MOZ_BUILD_APP"] == "browser":
DEFINES["MOZ_BUILD_APP_IS_BROWSER"] = True
+if CONFIG['TOR_BROWSER_UPDATE']:
+ DEFINES['MAR_NSS'] = True
+
LOCAL_INCLUDES += [
"../../other-licenses/nsis/Contrib/CityHash/cityhash",
"../components/find",
diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp
index 2b176266b05f..c230567bd013 100644
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -366,6 +366,42 @@ static nsresult GetUpdateDirFromAppDir(nsIFile* aAppDir, nsIFile** aResult) {
# endif
#endif
+#if defined(TOR_BROWSER_UPDATE) && defined(MOZ_VERIFY_MAR_SIGNATURE) && \
+ defined(MAR_NSS) && defined(XP_MACOSX)
+/**
+ * Ideally we would save and restore the original library path value after
+ * the updater finishes its work (and before firefox is re-launched).
+ * Doing so would avoid potential problems like the following bug:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1434033
+ */
+/**
+ * Appends the specified path to the library path.
+ * This is used so that the updater can find libnss3.dylib and other
+ * shared libs.
+ *
+ * @param pathToAppend A new library path to prepend to the dynamic linker's
+ * search path.
+ */
+# include "prprf.h"
+# define PATH_SEPARATOR ":"
+# define LD_LIBRARY_PATH_ENVVAR_NAME "DYLD_LIBRARY_PATH"
+static void AppendToLibPath(const char* pathToAppend) {
+ char* pathValue = getenv(LD_LIBRARY_PATH_ENVVAR_NAME);
+ if (nullptr == pathValue || '\0' == *pathValue) {
+ // Leak the string because that is required by PR_SetEnv.
+ char* s =
+ Smprintf("%s=%s", LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend).release();
+ PR_SetEnv(s);
+ } else {
+ // Leak the string because that is required by PR_SetEnv.
+ char* s = Smprintf("%s=%s" PATH_SEPARATOR "%s", LD_LIBRARY_PATH_ENVVAR_NAME,
+ pathToAppend, pathValue)
+ .release();
+ PR_SetEnv(s);
+ }
+}
+#endif
+
/**
* Applies, switches, or stages an update.
*
@@ -612,6 +648,20 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
}
+#if defined(TOR_BROWSER_UPDATE) && defined(MOZ_VERIFY_MAR_SIGNATURE) && \
+ defined(MAR_NSS) && defined(XP_MACOSX)
+ // On macOS, append the app directory to the shared library search path
+ // so the system can locate the shared libraries that are needed by the
+ // updater, e.g., libnss3.dylib).
+ nsAutoCString appPath;
+ nsresult rv2 = appDir->GetNativePath(appPath);
+ if (NS_SUCCEEDED(rv2)) {
+ AppendToLibPath(appPath.get());
+ } else {
+ LOG(("ApplyUpdate -- appDir->GetNativePath() failed (0x%x)\n", rv2));
+ }
+#endif
+
LOG(("spawning updater process [%s]\n", updaterPath.get()));
#ifdef DEBUG
dump_argv("ApplyUpdate updater", argv, argc);
1
0