tbb-commits
Threads by month
- ----- 2025 -----
- 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
February 2022
- 5 participants
- 371 discussions

[Git][tpo/applications/fenix] Pushed new tag tor-browser-94.1.1-11.0-1-build1
by Matthew Finkel (@sysrqb) 02 Feb '22
by Matthew Finkel (@sysrqb) 02 Feb '22
02 Feb '22
Matthew Finkel pushed new tag tor-browser-94.1.1-11.0-1-build1 at The Tor Project / Applications / fenix
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/fenix/-/tree/tor-browser-94.…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[tor-browser/tor-browser-91.6.0esr-11.0-1] Bug 32658: Create a new MAR signing key
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit 1feccb7a9f8d404e317d8c5d02053c2056cb4382
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.0-1] Orfox: Centralized proxy applied to AbstractCommunicator and BaseResources.
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit ed7d6c6e60616b6e88d7c20618374f463b690e59
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 dbd07a069de1..800c7cf96de8 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
@@ -29,6 +29,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;
@@ -39,7 +43,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/tor-browser-91.6.0esr-11.0-1] Bug 24796 - Comment out excess permissions from GeckoView
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit 1e6f089ef0c47d767185f3351da52bb65a937751
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 a76b6a4754b6..7a2f30708fc3 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/tor-browser-91.6.0esr-11.0-1] Bug 25741 - TBA: Disable GeckoNetworkManager
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit 0ea5299613197497f7b419e37f1619f8a460ada7
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 f084b522ad53..b94d8e803b6b 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
@@ -122,7 +122,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)
@@ -130,7 +132,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/tor-browser-91.6.0esr-11.0-1] Bug 28125 - Prevent non-Necko network connections
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit c976763855f1075e7a4367fb442766b38573082f
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Thu Oct 25 19:17:09 2018 +0000
Bug 28125 - Prevent non-Necko network connections
---
.../gecko/media/GeckoMediaDrmBridgeV21.java | 49 +---------------------
.../exoplayer2/upstream/DefaultHttpDataSource.java | 47 ++-------------------
2 files changed, 4 insertions(+), 92 deletions(-)
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
index 6e75e6fb4dd5..7faa9bc2821d 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
@@ -490,54 +490,7 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
@Override
protected Void doInBackground(final Void... params) {
- HttpURLConnection urlConnection = null;
- BufferedReader in = null;
- try {
- final URI finalURI = new URI(mURL + "&signedRequest=" + URLEncoder.encode(new String(mDrmRequest), "UTF-8"));
- urlConnection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(finalURI);
- urlConnection.setRequestMethod("POST");
- if (DEBUG) Log.d(LOGTAG, "Provisioning, posting url =" + finalURI.toString());
-
- // Add data
- urlConnection.setRequestProperty("Accept", "*/*");
- urlConnection.setRequestProperty("User-Agent", getCDMUserAgent());
- urlConnection.setRequestProperty("Content-Type", "application/json");
-
- // Execute HTTP Post Request
- urlConnection.connect();
-
- final int responseCode = urlConnection.getResponseCode();
- if (responseCode == HttpURLConnection.HTTP_OK) {
- in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), UTF_8));
- String inputLine;
- final StringBuffer response = new StringBuffer();
-
- while ((inputLine = in.readLine()) != null) {
- response.append(inputLine);
- }
- in.close();
- mResponseBody = String.valueOf(response).getBytes(UTF_8);
- if (DEBUG) Log.d(LOGTAG, "Provisioning, response received.");
- if (mResponseBody != null) Log.d(LOGTAG, "response length=" + mResponseBody.length);
- } else {
- Log.d(LOGTAG, "Provisioning, server returned HTTP error code :" + responseCode);
- }
- } catch (final IOException e) {
- Log.e(LOGTAG, "Got exception during posting provisioning request ...", e);
- } catch (final URISyntaxException e) {
- Log.e(LOGTAG, "Got exception during creating uri ...", e);
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- try {
- if (in != null) {
- in.close();
- }
- } catch (final IOException e) {
- Log.e(LOGTAG, "Exception during closing in ...", e);
- }
- }
+ Log.i(LOGTAG, "This is Tor Browser. Skipping.");
return null;
}
diff --git a/mobile/android/geckoview/src/thirdparty/java/org/mozilla/thirdparty/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java b/mobile/android/geckoview/src/thirdparty/java/org/mozilla/thirdparty/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java
index 6e5095b0a4c9..a585e283ed4e 100644
--- a/mobile/android/geckoview/src/thirdparty/java/org/mozilla/thirdparty/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java
+++ b/mobile/android/geckoview/src/thirdparty/java/org/mozilla/thirdparty/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java
@@ -46,6 +46,7 @@ import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import org.mozilla.gecko.util.ProxySelector;
+
/**
* An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}.
*
@@ -516,50 +517,8 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
boolean followRedirects,
Map<String, String> requestParameters)
throws IOException, URISyntaxException {
- /**
- * Tor Project modified the way the connection object was created. For the sake of
- * simplicity, instead of duplicating the whole file we changed the connection object
- * to use the ProxySelector.
- */
- HttpURLConnection connection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(url.toURI());
-
- connection.setConnectTimeout(connectTimeoutMillis);
- connection.setReadTimeout(readTimeoutMillis);
-
- Map<String, String> requestHeaders = new HashMap<>();
- if (defaultRequestProperties != null) {
- requestHeaders.putAll(defaultRequestProperties.getSnapshot());
- }
- requestHeaders.putAll(requestProperties.getSnapshot());
- requestHeaders.putAll(requestParameters);
-
- for (Map.Entry<String, String> property : requestHeaders.entrySet()) {
- connection.setRequestProperty(property.getKey(), property.getValue());
- }
-
- if (!(position == 0 && length == C.LENGTH_UNSET)) {
- String rangeRequest = "bytes=" + position + "-";
- if (length != C.LENGTH_UNSET) {
- rangeRequest += (position + length - 1);
- }
- connection.setRequestProperty("Range", rangeRequest);
- }
- connection.setRequestProperty("User-Agent", userAgent);
- connection.setRequestProperty("Accept-Encoding", allowGzip ? "gzip" : "identity");
- connection.setInstanceFollowRedirects(followRedirects);
- connection.setDoOutput(httpBody != null);
- connection.setRequestMethod(DataSpec.getStringForHttpMethod(httpMethod));
-
- if (httpBody != null) {
- connection.setFixedLengthStreamingMode(httpBody.length);
- connection.connect();
- OutputStream os = connection.getOutputStream();
- os.write(httpBody);
- os.close();
- } else {
- connection.connect();
- }
- return connection;
+ Log.i(TAG, "This is Tor Browser. Skipping.");
+ throw new IOException();
}
/** Creates an {@link HttpURLConnection} that is connected with the {@code url}. */
1
0

[tor-browser/tor-browser-91.6.0esr-11.0-1] Bug 33342: Avoid disconnect search addon error after removal.
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit 541a3c4773be42e0a608fbe7d1bc566d0484ac90
Author: Alex Catarineu <acat(a)torproject.org>
Date: Fri Mar 13 18:19:30 2020 +0100
Bug 33342: Avoid disconnect search addon error after removal.
We removed the addon in #32767, but it was still being loaded
from addonStartup.json.lz4 and throwing an error on startup
because its resource: location is not available anymore.
---
toolkit/mozapps/extensions/internal/XPIProvider.jsm | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
index 04d57a42348e..ba562c92948d 100644
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -959,6 +959,12 @@ var BuiltInLocation = new (class _BuiltInLocation extends XPIStateLocation {
isLinkedAddon(/* aId */) {
return false;
}
+
+ restore(saved) {
+ super.restore(saved);
+ // Bug 33342: avoid restoring disconnect addon from addonStartup.json.lz4.
+ this.removeAddon("disconnect(a)search.mozilla.org");
+ }
})();
/**
1
0

[tor-browser/tor-browser-91.6.0esr-11.0-1] Bug 28005: Implement .onion alias urlbar rewrites
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit d05490879756b6513e6842a2038bc025b4b9eee3
Author: Alex Catarineu <acat(a)torproject.org>
Date: Thu Feb 13 13:24:33 2020 +0100
Bug 28005: Implement .onion alias urlbar rewrites
A custom HTTPS Everywhere update channel is installed,
which provides rules for locally redirecting some memorable
.tor.onion URLs to non-memorable .onion URLs.
When these redirects occur, we also rewrite the URL in the urlbar
to display the human-memorable hostname instead of the actual
.onion.
Bug 34196: Update site info URL with the onion name
Bug 40456: Update the SecureDrop HTTPS-Everywhere update channel
Bug 40478: Onion alias url rewrite is broken
---
browser/actors/ClickHandlerChild.jsm | 20 ++
browser/actors/ClickHandlerParent.jsm | 1 +
browser/actors/ContextMenuChild.jsm | 4 +
browser/base/content/browser-places.js | 12 +-
browser/base/content/browser-siteIdentity.js | 12 +-
browser/base/content/browser.js | 43 ++++-
browser/base/content/nsContextMenu.js | 18 ++
browser/base/content/pageinfo/pageInfo.js | 2 +-
browser/base/content/pageinfo/pageInfo.xhtml | 10 +
browser/base/content/pageinfo/security.js | 17 +-
browser/base/content/tabbrowser.js | 7 +
browser/base/content/utilityOverlay.js | 12 ++
browser/components/BrowserGlue.jsm | 8 +
.../onionservices/ExtensionMessaging.jsm | 77 ++++++++
.../onionservices/HttpsEverywhereControl.jsm | 147 +++++++++++++++
.../components/onionservices/OnionAliasStore.jsm | 201 +++++++++++++++++++++
browser/components/onionservices/moz.build | 6 +
browser/components/urlbar/UrlbarInput.jsm | 13 +-
docshell/base/nsDocShell.cpp | 52 ++++++
docshell/base/nsDocShell.h | 6 +
docshell/base/nsDocShellLoadState.cpp | 4 +
docshell/base/nsIDocShell.idl | 5 +
docshell/base/nsIWebNavigation.idl | 5 +
docshell/shistory/SessionHistoryEntry.cpp | 14 ++
docshell/shistory/SessionHistoryEntry.h | 1 +
docshell/shistory/nsISHEntry.idl | 5 +
docshell/shistory/nsSHEntry.cpp | 22 ++-
docshell/shistory/nsSHEntry.h | 1 +
dom/interfaces/base/nsIBrowser.idl | 3 +-
dom/ipc/BrowserChild.cpp | 2 +
dom/ipc/BrowserParent.cpp | 3 +-
dom/ipc/PBrowser.ipdl | 1 +
modules/libpref/init/StaticPrefList.yaml | 6 +
netwerk/dns/effective_tld_names.dat | 2 +
netwerk/ipc/DocumentLoadListener.cpp | 10 +
toolkit/content/widgets/browser-custom-element.js | 13 +-
toolkit/modules/sessionstore/SessionHistory.jsm | 5 +
xpcom/reflect/xptinfo/xptinfo.h | 3 +-
38 files changed, 750 insertions(+), 23 deletions(-)
diff --git a/browser/actors/ClickHandlerChild.jsm b/browser/actors/ClickHandlerChild.jsm
index 0f3bf42e2290..0f0f9330197f 100644
--- a/browser/actors/ClickHandlerChild.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -146,6 +146,26 @@ class ClickHandlerChild extends JSWindowActorChild {
json.originStoragePrincipal = ownerDoc.effectiveStoragePrincipal;
json.triggeringPrincipal = ownerDoc.nodePrincipal;
+ // Check if the link needs to be opened with .tor.onion urlbar rewrites
+ // allowed. Only when the owner doc has onionUrlbarRewritesAllowed = true
+ // and the same origin we should allow this.
+ json.onionUrlbarRewritesAllowed = false;
+ if (this.docShell.onionUrlbarRewritesAllowed) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ let targetURI = Services.io.newURI(href);
+ let isPrivateWin =
+ ownerDoc.nodePrincipal.originAttributes.privateBrowsingId > 0;
+ sm.checkSameOriginURI(
+ docshell.currentDocumentChannel.URI,
+ targetURI,
+ false,
+ isPrivateWin
+ );
+ json.onionUrlbarRewritesAllowed = true;
+ } catch (e) {}
+ }
+
// If a link element is clicked with middle button, user wants to open
// the link somewhere rather than pasting clipboard content. Therefore,
// when it's clicked with middle button, we should prevent multiple
diff --git a/browser/actors/ClickHandlerParent.jsm b/browser/actors/ClickHandlerParent.jsm
index 89363074ed14..3a5be306be46 100644
--- a/browser/actors/ClickHandlerParent.jsm
+++ b/browser/actors/ClickHandlerParent.jsm
@@ -103,6 +103,7 @@ class ClickHandlerParent extends JSWindowActorParent {
let params = {
charset: browser.characterSet,
referrerInfo: E10SUtils.deserializeReferrerInfo(data.referrerInfo),
+ onionUrlbarRewritesAllowed: data.onionUrlbarRewritesAllowed,
isContentWindowPrivate: data.isContentWindowPrivate,
originPrincipal: data.originPrincipal,
originStoragePrincipal: data.originStoragePrincipal,
diff --git a/browser/actors/ContextMenuChild.jsm b/browser/actors/ContextMenuChild.jsm
index a9521642e495..dd7809eeb1ca 100644
--- a/browser/actors/ContextMenuChild.jsm
+++ b/browser/actors/ContextMenuChild.jsm
@@ -545,6 +545,9 @@ class ContextMenuChild extends JSWindowActorChild {
doc.defaultView
).getFieldContext(aEvent.composedTarget);
+ let parentAllowsOnionUrlbarRewrites = this.docShell
+ .onionUrlbarRewritesAllowed;
+
let disableSetDesktopBackground = null;
// Media related cache info parent needs for saving
@@ -656,6 +659,7 @@ class ContextMenuChild extends JSWindowActorChild {
frameID,
frameBrowsingContextID,
disableSetDesktopBackground,
+ parentAllowsOnionUrlbarRewrites,
};
if (context.inFrame && !context.inSrcdocFrame) {
diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js
index b0c9f6623097..d90dc636f8db 100644
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -470,7 +470,8 @@ var PlacesCommandHook = {
*/
async bookmarkPage() {
let browser = gBrowser.selectedBrowser;
- let url = new URL(browser.currentURI.spec);
+ const uri = browser.currentOnionAliasURI || browser.currentURI;
+ let url = new URL(uri.spec);
let info = await PlacesUtils.bookmarks.fetch({ url });
let isNewBookmark = !info;
let showEditUI = !isNewBookmark || StarUI.showForNewBookmarks;
@@ -581,7 +582,7 @@ var PlacesCommandHook = {
tabs.forEach(tab => {
let browser = tab.linkedBrowser;
- let uri = browser.currentURI;
+ let uri = browser.currentOnionAliasURI || browser.currentURI;
let title = browser.contentTitle || tab.label;
let spec = uri.spec;
if (!(spec in uniquePages)) {
@@ -1828,14 +1829,17 @@ var BookmarkingUI = {
},
onLocationChange: function BUI_onLocationChange() {
- if (this._uri && gBrowser.currentURI.equals(this._uri)) {
+ const uri =
+ gBrowser.selectedBrowser.currentOnionAliasURI || gBrowser.currentURI;
+ if (this._uri && uri.equals(this._uri)) {
return;
}
this.updateStarState();
},
updateStarState: function BUI_updateStarState() {
- this._uri = gBrowser.currentURI;
+ this._uri =
+ gBrowser.selectedBrowser.currentOnionAliasURI || gBrowser.currentURI;
this._itemGuids.clear();
let guids = new Set();
diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js
index 539dfb9206a7..1b2c7bcb22cf 100644
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -656,13 +656,13 @@ var gIdentityHandler = {
* nsIURI for which the identity UI should be displayed, already
* processed by createExposableURI.
*/
- updateIdentity(state, uri) {
+ updateIdentity(state, uri, onionAliasURI) {
let shouldHidePopup = this._uri && this._uri.spec != uri.spec;
this._state = state;
// Firstly, populate the state properties required to display the UI. See
// the documentation of the individual properties for details.
- this.setURI(uri);
+ this.setURI(uri, onionAliasURI);
this._secInfo = gBrowser.securityUI.secInfo;
this._isSecureContext = gBrowser.securityUI.isSecureContext;
@@ -685,17 +685,18 @@ var gIdentityHandler = {
* Attempt to provide proper IDN treatment for host names
*/
getEffectiveHost() {
+ let uri = this._onionAliasURI || this._uri;
if (!this._IDNService) {
this._IDNService = Cc["@mozilla.org/network/idn-service;1"].getService(
Ci.nsIIDNService
);
}
try {
- return this._IDNService.convertToDisplayIDN(this._uri.host, {});
+ return this._IDNService.convertToDisplayIDN(uri.host, {});
} catch (e) {
// If something goes wrong (e.g. host is an IP address) just fail back
// to the full domain.
- return this._uri.host;
+ return uri.host;
}
},
@@ -1138,11 +1139,12 @@ var gIdentityHandler = {
this._identityPopupContentVerif.textContent = verifier;
},
- setURI(uri) {
+ setURI(uri, onionAliasURI) {
if (uri.schemeIs("view-source")) {
uri = Services.io.newURI(uri.spec.replace(/^view-source:/i, ""));
}
this._uri = uri;
+ this._onionAliasURI = onionAliasURI;
try {
// Account for file: urls and catch when "" is the value
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 056e7d8cde57..8d0ac92ae3b0 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -80,6 +80,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
Translation: "resource:///modules/translation/TranslationParent.jsm",
+ OnionAliasStore: "resource:///modules/OnionAliasStore.jsm",
UITour: "resource:///modules/UITour.jsm",
UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
UrlbarInput: "resource:///modules/UrlbarInput.jsm",
@@ -2243,6 +2244,7 @@ var gBrowserInit = {
// [9]: allowInheritPrincipal (bool)
// [10]: csp (nsIContentSecurityPolicy)
// [11]: nsOpenWindowInfo
+ // [12]: onionUrlbarRewritesAllowed (bool)
let userContextId =
window.arguments[5] != undefined
? window.arguments[5]
@@ -2262,7 +2264,8 @@ var gBrowserInit = {
// TODO fix allowInheritPrincipal to default to false.
// Default to true unless explicitly set to false because of bug 1475201.
window.arguments[9] !== false,
- window.arguments[10]
+ window.arguments[10],
+ window.arguments[12]
);
window.focus();
} else {
@@ -3042,7 +3045,8 @@ function loadURI(
forceAboutBlankViewerInCurrent,
triggeringPrincipal,
allowInheritPrincipal = false,
- csp = null
+ csp = null,
+ onionUrlbarRewritesAllowed = false
) {
if (!triggeringPrincipal) {
throw new Error("Must load with a triggering Principal");
@@ -3060,6 +3064,7 @@ function loadURI(
csp,
forceAboutBlankViewerInCurrent,
allowInheritPrincipal,
+ onionUrlbarRewritesAllowed,
});
} catch (e) {
Cu.reportError(e);
@@ -5186,11 +5191,24 @@ var XULBrowserWindow = {
this.reloadCommand.removeAttribute("disabled");
}
+ // The onion memorable alias needs to be used in gURLBar.setURI, but also in
+ // other parts of the code (like the bookmarks UI), so we save it.
+ if (gBrowser.selectedBrowser.onionUrlbarRewritesAllowed) {
+ gBrowser.selectedBrowser.currentOnionAliasURI = OnionAliasStore.getShortURI(
+ aLocationURI
+ );
+ } else {
+ gBrowser.selectedBrowser.currentOnionAliasURI = null;
+ }
+
// We want to update the popup visibility if we received this notification
// via simulated locationchange events such as switching between tabs, however
// if this is a document navigation then PopupNotifications will be updated
// via TabsProgressListener.onLocationChange and we do not want it called twice
- gURLBar.setURI(aLocationURI, aIsSimulated);
+ gURLBar.setURI(
+ gBrowser.selectedBrowser.currentOnionAliasURI || aLocationURI,
+ aIsSimulated
+ );
BookmarkingUI.onLocationChange();
// If we've actually changed document, update the toolbar visibility.
@@ -5413,6 +5431,7 @@ var XULBrowserWindow = {
// Don't need to do anything if the data we use to update the UI hasn't
// changed
let uri = gBrowser.currentURI;
+ let onionAliasURI = gBrowser.selectedBrowser.currentOnionAliasURI;
let spec = uri.spec;
let isSecureContext = gBrowser.securityUI.isSecureContext;
if (
@@ -5436,7 +5455,7 @@ var XULBrowserWindow = {
try {
uri = Services.io.createExposableURI(uri);
} catch (e) {}
- gIdentityHandler.updateIdentity(this._state, uri);
+ gIdentityHandler.updateIdentity(this._state, uri, onionAliasURI);
},
// simulate all change notifications after switching tabs
@@ -6945,6 +6964,21 @@ function handleLinkClick(event, href, linkNode) {
return true;
}
+ // Check if the link needs to be opened with .tor.onion urlbar rewrites
+ // allowed. Only when the owner doc has onionUrlbarRewritesAllowed = true
+ // and the same origin we should allow this.
+ let persistOnionUrlbarRewritesAllowedInChildTab = false;
+ if (where == "tab" && gBrowser.docShell.onionUrlbarRewritesAllowed) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ let tURI = makeURI(href);
+ let isPrivateWin =
+ doc.nodePrincipal.originAttributes.privateBrowsingId > 0;
+ sm.checkSameOriginURI(doc.documentURIObject, tURI, false, isPrivateWin);
+ persistOnionUrlbarRewritesAllowedInChildTab = true;
+ } catch (e) {}
+ }
+
let frameID = WebNavigationFrames.getFrameId(doc.defaultView);
urlSecurityCheck(href, doc.nodePrincipal);
@@ -6956,6 +6990,7 @@ function handleLinkClick(event, href, linkNode) {
triggeringPrincipal: doc.nodePrincipal,
csp: doc.csp,
frameID,
+ onionUrlbarRewritesAllowed: persistOnionUrlbarRewritesAllowedInChildTab,
};
// The new tab/window must use the same userContextId
diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js
index 1ff16ffbab9f..e96df23142f8 100644
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -58,6 +58,7 @@ function openContextMenu(aMessage, aBrowser, aActor) {
selectionInfo: data.selectionInfo,
disableSetDesktopBackground: data.disableSetDesktopBackground,
loginFillInfo: data.loginFillInfo,
+ parentAllowsOnionUrlbarRewrites: data.parentAllowsOnionUrlbarRewrites,
userContextId: data.userContextId,
webExtContextData: data.webExtContextData,
cookieJarSettings: E10SUtils.deserializeCookieJarSettings(
@@ -1197,6 +1198,7 @@ class nsContextMenu {
triggeringPrincipal: this.principal,
csp: this.csp,
frameID: this.contentData.frameID,
+ onionUrlbarRewritesAllowed: false,
};
for (let p in extra) {
params[p] = extra[p];
@@ -1220,6 +1222,22 @@ class nsContextMenu {
}
params.referrerInfo = referrerInfo;
+
+ // Check if the link needs to be opened with .tor.onion urlbar rewrites
+ // allowed. Only when parent has onionUrlbarRewritesAllowed = true
+ // and the same origin we should allow this.
+ if (this.contentData.parentAllowsOnionUrlbarRewrites) {
+ let referrerURI = this.contentData.documentURIObject;
+ const sm = Services.scriptSecurityManager;
+ try {
+ let targetURI = this.linkURI;
+ let isPrivateWin =
+ this.browser.contentPrincipal.originAttributes.privateBrowsingId > 0;
+ sm.checkSameOriginURI(referrerURI, targetURI, false, isPrivateWin);
+ params.onionUrlbarRewritesAllowed = true;
+ } catch (e) {}
+ }
+
return params;
}
diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js
index cd02b73bd0c7..dd1a4a90fedf 100644
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -398,7 +398,7 @@ async function onNonMediaPageInfoLoad(browser, pageInfoData, imageInfo) {
);
}
onLoadPermission(uri, principal);
- securityOnLoad(uri, windowInfo);
+ securityOnLoad(uri, windowInfo, browser.currentOnionAliasURI);
}
function resetPageInfo(args) {
diff --git a/browser/base/content/pageinfo/pageInfo.xhtml b/browser/base/content/pageinfo/pageInfo.xhtml
index f40ffd3778d8..a23f2bb5748c 100644
--- a/browser/base/content/pageinfo/pageInfo.xhtml
+++ b/browser/base/content/pageinfo/pageInfo.xhtml
@@ -312,6 +312,16 @@
<input id="security-identity-domain-value" readonly="readonly"/>
</td>
</tr>
+ <!-- Onion Alias -->
+ <tr id="security-view-identity-onionalias-row">
+ <th>
+ <xul:label id="security-view-identity-onionalias"
+ control="security-view-identity-onionalias-value"/>
+ </th>
+ <td>
+ <input id="security-view-identity-onionalias-value" readonly="true"/>
+ </td>
+ </tr>
<!-- Owner -->
<tr>
<th>
diff --git a/browser/base/content/pageinfo/security.js b/browser/base/content/pageinfo/security.js
index 8d10c8df814c..2e22f4670503 100644
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -248,7 +248,7 @@ var security = {
},
};
-async function securityOnLoad(uri, windowInfo) {
+async function securityOnLoad(uri, windowInfo, onionAliasURI) {
await security.init(uri, windowInfo);
let info = security.securityInfo;
@@ -261,6 +261,21 @@ async function securityOnLoad(uri, windowInfo) {
}
document.getElementById("securityTab").hidden = false;
+ if (onionAliasURI) {
+ setText(
+ "security-view-identity-onionalias",
+ gTorButtonBundle.GetStringFromName("pageInfo_OnionName")
+ );
+ setText("security-view-identity-onionalias-value", onionAliasURI.host);
+ document.getElementById(
+ "security-view-identity-onionalias-row"
+ ).hidden = false;
+ } else {
+ document.getElementById(
+ "security-view-identity-onionalias-row"
+ ).hidden = true;
+ }
+
/* Set Identity section text */
setText("security-identity-domain-value", windowInfo.hostName);
diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js
index e47c81541bfa..520fea7cc345 100644
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1635,6 +1635,7 @@
var aFromExternal;
var aRelatedToCurrent;
var aAllowInheritPrincipal;
+ var aOnionUrlbarRewritesAllowed;
var aSkipAnimation;
var aForceNotRemote;
var aPreferredRemoteType;
@@ -1664,6 +1665,7 @@
aFromExternal = params.fromExternal;
aRelatedToCurrent = params.relatedToCurrent;
aAllowInheritPrincipal = !!params.allowInheritPrincipal;
+ aOnionUrlbarRewritesAllowed = params.onionUrlbarRewritesAllowed;
aSkipAnimation = params.skipAnimation;
aForceNotRemote = params.forceNotRemote;
aPreferredRemoteType = params.preferredRemoteType;
@@ -1704,6 +1706,7 @@
fromExternal: aFromExternal,
relatedToCurrent: aRelatedToCurrent,
skipAnimation: aSkipAnimation,
+ onionUrlbarRewritesAllowed: aOnionUrlbarRewritesAllowed,
forceNotRemote: aForceNotRemote,
createLazyBrowser: aCreateLazyBrowser,
preferredRemoteType: aPreferredRemoteType,
@@ -2536,6 +2539,7 @@
aURI,
{
allowInheritPrincipal,
+ onionUrlbarRewritesAllowed,
allowThirdPartyFixup,
bulkOrderedOpen,
charset,
@@ -2877,6 +2881,9 @@
// lands.
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIRST_LOAD;
}
+ if (onionUrlbarRewritesAllowed) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES;
+ }
if (!allowInheritPrincipal) {
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
}
diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js
index 3b14beaa5b1e..7b6dc5b921de 100644
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -289,6 +289,7 @@ function openLinkIn(url, where, params) {
: new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, null);
var aRelatedToCurrent = params.relatedToCurrent;
var aAllowInheritPrincipal = !!params.allowInheritPrincipal;
+ var aOnionUrlbarRewritesAllowed = params.onionUrlbarRewritesAllowed;
var aForceAllowDataURI = params.forceAllowDataURI;
var aInBackground = params.inBackground;
var aInitiatingDoc = params.initiatingDoc;
@@ -405,6 +406,11 @@ function openLinkIn(url, where, params) {
].createInstance(Ci.nsISupportsPRBool);
allowThirdPartyFixupSupports.data = aAllowThirdPartyFixup;
+ var onionUrlbarRewritesAllowed = Cc[
+ "@mozilla.org/supports-PRBool;1"
+ ].createInstance(Ci.nsISupportsPRBool);
+ onionUrlbarRewritesAllowed.data = aOnionUrlbarRewritesAllowed;
+
var userContextIdSupports = Cc[
"@mozilla.org/supports-PRUint32;1"
].createInstance(Ci.nsISupportsPRUint32);
@@ -421,6 +427,8 @@ function openLinkIn(url, where, params) {
sa.appendElement(aTriggeringPrincipal);
sa.appendElement(null); // allowInheritPrincipal
sa.appendElement(aCsp);
+ sa.appendElement(null); // nsOpenWindowInfo
+ sa.appendElement(onionUrlbarRewritesAllowed);
const sourceWindow = w || window;
let win;
@@ -538,6 +546,9 @@ function openLinkIn(url, where, params) {
if (aForceAllowDataURI) {
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
}
+ if (aOnionUrlbarRewritesAllowed) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES;
+ }
let { URI_INHERITS_SECURITY_CONTEXT } = Ci.nsIProtocolHandler;
if (
@@ -584,6 +595,7 @@ function openLinkIn(url, where, params) {
allowThirdPartyFixup: aAllowThirdPartyFixup,
relatedToCurrent: aRelatedToCurrent,
skipAnimation: aSkipTabAnimation,
+ onionUrlbarRewritesAllowed: aOnionUrlbarRewritesAllowed,
userContextId: aUserContextId,
originPrincipal: aPrincipal,
originStoragePrincipal: aStoragePrincipal,
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index e40dbe5d3261..9d1a7e20abc5 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -85,6 +85,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
TabUnloader: "resource:///modules/TabUnloader.jsm",
TelemetryUtils: "resource://gre/modules/TelemetryUtils.jsm",
TRRRacer: "resource:///modules/TRRPerformance.jsm",
+ OnionAliasStore: "resource:///modules/OnionAliasStore.jsm",
UIState: "resource://services-sync/UIState.jsm",
UrlbarQuickSuggest: "resource:///modules/UrlbarQuickSuggest.jsm",
UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
@@ -1933,6 +1934,7 @@ BrowserGlue.prototype = {
Normandy.uninit();
RFPHelper.uninit();
ASRouterNewTabHook.destroy();
+ OnionAliasStore.uninit();
},
// Set up a listener to enable/disable the screenshots extension
@@ -2437,6 +2439,12 @@ BrowserGlue.prototype = {
},
},
+ {
+ task: () => {
+ OnionAliasStore.init();
+ },
+ },
+
{
task: () => {
Blocklist.loadBlocklistAsync();
diff --git a/browser/components/onionservices/ExtensionMessaging.jsm b/browser/components/onionservices/ExtensionMessaging.jsm
new file mode 100644
index 000000000000..c93b8c6edf85
--- /dev/null
+++ b/browser/components/onionservices/ExtensionMessaging.jsm
@@ -0,0 +1,77 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+const EXPORTED_SYMBOLS = ["ExtensionMessaging"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { ExtensionUtils } = ChromeUtils.import(
+ "resource://gre/modules/ExtensionUtils.jsm"
+);
+const { MessageChannel } = ChromeUtils.import(
+ "resource://gre/modules/MessageChannel.jsm"
+);
+const { AddonManager } = ChromeUtils.import(
+ "resource://gre/modules/AddonManager.jsm"
+);
+
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ ExtensionParent: "resource://gre/modules/ExtensionParent.jsm",
+});
+
+class ExtensionMessaging {
+ constructor() {
+ this._callback = null;
+ this._handlers = new Map();
+ this._messageManager = Services.cpmm;
+ }
+
+ async sendMessage(message, extensionId) {
+ const addon = await AddonManager.getAddonByID(extensionId);
+ if (!addon) {
+ throw new Error(`extension '${extensionId} does not exist`);
+ }
+ await addon.startupPromise;
+
+ const { torSendExtensionMessage } = ExtensionParent;
+ return torSendExtensionMessage(extensionId, message);
+ }
+
+ unload() {
+ if (this._callback) {
+ this._handlers.clear();
+ this._messageManager.removeMessageListener(
+ "MessageChannel:Response",
+ this._callback
+ );
+ this._callback = null;
+ }
+ }
+
+ _onMessage({ data }) {
+ const channelId = data.messageName;
+ if (this._handlers.has(channelId)) {
+ const { resolve, reject } = this._handlers.get(channelId);
+ this._handlers.delete(channelId);
+ if (data.error) {
+ reject(new Error(data.error.message));
+ } else {
+ resolve(data.value);
+ }
+ }
+ }
+
+ _init() {
+ if (this._callback === null) {
+ this._callback = this._onMessage.bind(this);
+ this._messageManager.addMessageListener(
+ "MessageChannel:Response",
+ this._callback
+ );
+ }
+ }
+}
diff --git a/browser/components/onionservices/HttpsEverywhereControl.jsm b/browser/components/onionservices/HttpsEverywhereControl.jsm
new file mode 100644
index 000000000000..525ed5233be7
--- /dev/null
+++ b/browser/components/onionservices/HttpsEverywhereControl.jsm
@@ -0,0 +1,147 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+const EXPORTED_SYMBOLS = ["HttpsEverywhereControl"];
+
+const { ExtensionMessaging } = ChromeUtils.import(
+ "resource:///modules/ExtensionMessaging.jsm"
+);
+const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+
+const EXTENSION_ID = "https-everywhere-eff(a)eff.org";
+const SECUREDROP_TOR_ONION_CHANNEL_2020 = {
+ name: "SecureDropTorOnion",
+ jwk: {
+ kty: "RSA",
+ e: "AQAB",
+ n:
+ "p10BbUVc5Xj2S_-MH3bACNBaISo_r9e3PVPyTTjsGsdg2qSXvqUO42fBtpFAy0zUzIGS83v4JjiRdvKJaZTIvbC8AcpymzdsTqujMm8RPTSy3hO_8mXzGa4DEsIB1uNLnUWRBKXvSGCmT9kFyxhTpkYqokNBzafVihTU34tN2Md1xFHnmZGqfYtPtbJLWAa5Z1M11EyR4lIyUxIiPTV9t1XstDbWr3iS83REJrGEFmjG1-BAgx8_lDUTa41799N2yYEhgZud7bL0M3ei8s5OERjiion5uANkUV3-s2QqUZjiVA-XR_HizXjciaUWNd683KqekpNOZ_0STh_UGwpcwU-KwG07QyiCrLrRpz8S_vH8CqGrrcWY3GSzYe9dp34jJdO65oA-G8tK6fMXtvTCFDZI6oNNaXJH71F5J0YbqO2ZqwKYc2WSi0gKVl2wd9roOVjaBmkJqvocntYuNM7t38fDEWHn5KUkmrTbiG68Cy56tDUfpKl3D9Uj4LaMvxJ1tKGvzQ4k_60odT7gIxu6DqYjXUHZpwPsSGBq3njaD7boe4CUXF2K7ViOc87BsKxRNCzDD8OklRjjXzOTOBH3PqFJ93CJ-4ECE5t9STU20aZ8E-2zKB8vjKyCySE4-kcIvBBsnkwVaJTPy9Ft1qYybo-soXEWVEZATANNWklBt8k",
+ },
+ update_path_prefix: "https://securedrop.org/https-everywhere/",
+ scope:
+ "^https?:\\/\\/[a-z0-9-]+(?:\\.[a-z0-9-]+)*\\.securedrop\\.tor\\.onion\\/",
+ replaces_default_rulesets: false,
+};
+
+const SECUREDROP_TOR_ONION_CHANNEL = {
+ name: "SecureDropTorOnion2021",
+ jwk: {
+ kty: "RSA",
+ e: "AQAB",
+ n:
+ "vsC7BNafkRe8Uh1DUgCkv6RbPQMdJgAKKnWdSqQd7tQzU1mXfmo_k1Py_2MYMZXOWmqSZ9iwIYkykZYywJ2VyMGve4byj1sLn6YQoOkG8g5Z3V4y0S2RpEfmYumNjTzfq8nxtLnwjaYd4sCUd5wa0SzeLrpRQuXo2bF3QuUF2xcbLJloxX1MmlsMMCdBc-qGNonLJ7bpn_JuyXlDWy1Fkeyw1qgjiOdiRIbMC1x302zgzX6dSrBrNB8Cpsh-vCE0ZjUo8M9caEv06F6QbYmdGJHM0ZZY34OHMSNdf-_qUKIV_SuxuSuFE99tkAeWnbWpyI1V-xhVo1sc7NzChP8ci2TdPvI3_0JyAuCvL6zIFqJUJkZibEUghhg6F09-oNJKpy7rhUJq7zZyLXJsvuXnn0gnIxfjRvMcDfZAKUVMZKRdw7fwWzwQril4Ib0MQOVda9vb_4JMk7Gup-TUI4sfuS4NKwsnKoODIO-2U5QpJWdtp1F4AQ1pBv8ajFl1WTrVGvkRGK0woPWaO6pWyJ4kRnhnxrV2FyNNt3JSR-0JEjhFWws47kjBvpr0VRiVRFppKA-plKs4LPlaaCff39TleYmY3mETe3w1GIGc2Lliad32Jpbx496IgDe1K3FMBEoKFZfhmtlRSXft8NKgSzPt2zkatM9bFKfaCYRaSy7akbk",
+ },
+ update_path_prefix: "https://securedrop.org/https-everywhere-2021/",
+ scope:
+ "^https?:\\/\\/[a-z0-9-]+(?:\\.[a-z0-9-]+)*\\.securedrop\\.tor\\.onion\\/",
+ replaces_default_rulesets: false,
+};
+
+class HttpsEverywhereControl {
+ constructor() {
+ this._extensionMessaging = null;
+ }
+
+ async _sendMessage(type, object) {
+ return this._extensionMessaging.sendMessage(
+ {
+ type,
+ object,
+ },
+ EXTENSION_ID
+ );
+ }
+
+ static async wait(seconds = 1) {
+ return new Promise(resolve => setTimeout(resolve, seconds * 1000));
+ }
+
+ /**
+ * Installs the .tor.onion update channel in https-everywhere
+ */
+ async installTorOnionUpdateChannel(retries = 5) {
+ this._init();
+
+ // TODO: https-everywhere store is initialized asynchronously, so sending a message
+ // immediately results in a `store.get is undefined` error.
+ // For now, let's wait a bit and retry a few times if there is an error, but perhaps
+ // we could suggest https-everywhere to send a message when that happens and listen
+ // for that here.
+ await HttpsEverywhereControl.wait();
+
+ try {
+ // Delete the previous channel signing key, and add the new one below.
+ await this._sendMessage(
+ "delete_update_channel",
+ SECUREDROP_TOR_ONION_CHANNEL_2020.name
+ );
+ } catch (e) {
+ if (retries <= 0) {
+ throw new Error("Could not uninstall SecureDropTorOnion update channel");
+ }
+ await this.installTorOnionUpdateChannel(retries - 1);
+ return;
+ }
+
+ try {
+ // TODO: we may want a way to "lock" this update channel, so that it cannot be modified
+ // by the user via UI, but I think this is not possible at the time of writing via
+ // the existing messages in https-everywhere.
+ await this._sendMessage(
+ "create_update_channel",
+ SECUREDROP_TOR_ONION_CHANNEL.name
+ );
+ } catch (e) {
+ if (retries <= 0) {
+ throw new Error("Could not install SecureDropTorOnion update channel");
+ }
+ await this.installTorOnionUpdateChannel(retries - 1);
+ return;
+ }
+
+ await this._sendMessage(
+ "update_update_channel",
+ SECUREDROP_TOR_ONION_CHANNEL
+ );
+ }
+
+ /**
+ * Returns the .tor.onion rulesets available in https-everywhere
+ */
+ async getTorOnionRules() {
+ return this._sendMessage("get_simple_rules_ending_with", ".tor.onion");
+ }
+
+ /**
+ * Returns the timestamp of the last .tor.onion update channel update.
+ */
+ async getRulesetTimestamp() {
+ const rulesets = await this._sendMessage("get_update_channel_timestamps");
+ const securedrop =
+ rulesets &&
+ rulesets.find(([{ name }]) => name === SECUREDROP_TOR_ONION_CHANNEL.name);
+ if (securedrop) {
+ const [
+ updateChannel, // This has the same structure as SECUREDROP_TOR_ONION_CHANNEL
+ lastUpdatedTimestamp, // An integer, 0 if the update channel was never updated
+ ] = securedrop;
+ void updateChannel; // Ignore eslint unused warning for ruleset
+ return lastUpdatedTimestamp;
+ }
+ return null;
+ }
+
+ unload() {
+ if (this._extensionMessaging) {
+ this._extensionMessaging.unload();
+ this._extensionMessaging = null;
+ }
+ }
+
+ _init() {
+ if (!this._extensionMessaging) {
+ this._extensionMessaging = new ExtensionMessaging();
+ }
+ }
+}
diff --git a/browser/components/onionservices/OnionAliasStore.jsm b/browser/components/onionservices/OnionAliasStore.jsm
new file mode 100644
index 000000000000..66cf569227bf
--- /dev/null
+++ b/browser/components/onionservices/OnionAliasStore.jsm
@@ -0,0 +1,201 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+const EXPORTED_SYMBOLS = ["OnionAliasStore"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+const { setTimeout, clearTimeout } = ChromeUtils.import(
+ "resource://gre/modules/Timer.jsm"
+);
+const { HttpsEverywhereControl } = ChromeUtils.import(
+ "resource:///modules/HttpsEverywhereControl.jsm"
+);
+
+// Logger adapted from CustomizableUI.jsm
+const kPrefOnionAliasDebug = "browser.onionalias.debug";
+XPCOMUtils.defineLazyPreferenceGetter(
+ this,
+ "gDebuggingEnabled",
+ kPrefOnionAliasDebug,
+ false,
+ (pref, oldVal, newVal) => {
+ if (typeof log != "undefined") {
+ log.maxLogLevel = newVal ? "all" : "log";
+ }
+ }
+);
+XPCOMUtils.defineLazyGetter(this, "log", () => {
+ let scope = {};
+ ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
+ let consoleOptions = {
+ maxLogLevel: gDebuggingEnabled ? "all" : "log",
+ prefix: "OnionAlias",
+ };
+ return new scope.ConsoleAPI(consoleOptions);
+});
+
+function observe(topic, callback) {
+ let observer = {
+ observe(aSubject, aTopic, aData) {
+ if (topic === aTopic) {
+ callback(aSubject, aData);
+ }
+ },
+ };
+ Services.obs.addObserver(observer, topic);
+ return () => Services.obs.removeObserver(observer, topic);
+}
+
+class _OnionAliasStore {
+ static get RULESET_CHECK_INTERVAL() {
+ return 1000 * 60; // 1 minute
+ }
+
+ static get RULESET_CHECK_INTERVAL_FAST() {
+ return 1000 * 5; // 5 seconds
+ }
+
+ constructor() {
+ this._onionMap = new Map();
+ this._rulesetTimeout = null;
+ this._removeObserver = () => {};
+ this._canLoadRules = false;
+ this._rulesetTimestamp = null;
+ this._updateChannelInstalled = false;
+ }
+
+ async _periodicRulesetCheck() {
+ // TODO: it would probably be preferable to listen to some message broadcasted by
+ // the https-everywhere extension when some update channel is updated, instead of
+ // polling every N seconds.
+ log.debug("Checking for new rules");
+ const ts = await this.httpsEverywhereControl.getRulesetTimestamp();
+ log.debug(
+ `Found ruleset timestamp ${ts}, current is ${this._rulesetTimestamp}`
+ );
+ if (ts !== this._rulesetTimestamp) {
+ this._rulesetTimestamp = ts;
+ log.debug("New rules found, updating");
+ // We clear the mappings even if we cannot load the rules from https-everywhere,
+ // since we cannot be sure if the stored mappings are correct anymore.
+ this._clear();
+ if (this._canLoadRules) {
+ await this._loadRules();
+ }
+ }
+ // If the timestamp is 0, that means the update channel was not yet updated, so
+ // we schedule a check soon.
+ this._rulesetTimeout = setTimeout(
+ () => this._periodicRulesetCheck(),
+ ts === 0
+ ? _OnionAliasStore.RULESET_CHECK_INTERVAL_FAST
+ : _OnionAliasStore.RULESET_CHECK_INTERVAL
+ );
+ }
+
+ async init() {
+ this.httpsEverywhereControl = new HttpsEverywhereControl();
+
+ // Setup .tor.onion rule loading.
+ // The http observer is a fallback, and is removed in _loadRules() as soon as we are able
+ // to load some rules from HTTPS Everywhere.
+ this._loadHttpObserver();
+ try {
+ await this.httpsEverywhereControl.installTorOnionUpdateChannel();
+ this._updateChannelInstalled = true;
+ await this.httpsEverywhereControl.getTorOnionRules();
+ this._canLoadRules = true;
+ } catch (e) {
+ // Loading rules did not work, probably because "get_simple_rules_ending_with" is not yet
+ // working in https-everywhere. Use an http observer as a fallback for learning the rules.
+ log.debug(`Could not load rules: ${e.message}`);
+ }
+
+ // Setup checker for https-everywhere ruleset updates
+ if (this._updateChannelInstalled) {
+ this._periodicRulesetCheck();
+ }
+ }
+
+ /**
+ * Loads the .tor.onion mappings from https-everywhere.
+ */
+ async _loadRules() {
+ const rules = await this.httpsEverywhereControl.getTorOnionRules();
+ // Remove http observer if we are able to load some rules directly.
+ if (rules.length) {
+ this._removeObserver();
+ this._removeObserver = () => {};
+ }
+ this._clear();
+ log.debug(`Loading ${rules.length} rules`, rules);
+ for (const rule of rules) {
+ // Here we are trusting that the securedrop ruleset follows some conventions so that we can
+ // assume there is a host mapping from `rule.host` to the hostname of the URL in `rule.to`.
+ try {
+ const url = new URL(rule.to);
+ const shortHost = rule.host;
+ const longHost = url.hostname;
+ this._addMapping(shortHost, longHost);
+ } catch (e) {
+ log.error("Could not process rule:", rule);
+ }
+ }
+ }
+
+ /**
+ * Loads a http observer to listen for local redirects for populating
+ * the .tor.onion -> .onion mappings. Should only be used if we cannot ask https-everywhere
+ * directly for the mappings.
+ */
+ _loadHttpObserver() {
+ this._removeObserver = observe("http-on-before-connect", channel => {
+ if (
+ channel.isMainDocumentChannel &&
+ channel.originalURI.host.endsWith(".tor.onion")
+ ) {
+ this._addMapping(channel.originalURI.host, channel.URI.host);
+ }
+ });
+ }
+
+ uninit() {
+ this._clear();
+ this._removeObserver();
+ this._removeObserver = () => {};
+ if (this.httpsEverywhereControl) {
+ this.httpsEverywhereControl.unload();
+ delete this.httpsEverywhereControl;
+ }
+ clearTimeout(this._rulesetTimeout);
+ this._rulesetTimeout = null;
+ this._rulesetTimestamp = null;
+ }
+
+ _clear() {
+ this._onionMap.clear();
+ }
+
+ _addMapping(shortOnionHost, longOnionHost) {
+ this._onionMap.set(longOnionHost, shortOnionHost);
+ }
+
+ getShortURI(onionURI) {
+ if (
+ (onionURI.schemeIs("http") || onionURI.schemeIs("https")) &&
+ this._onionMap.has(onionURI.host)
+ ) {
+ return onionURI
+ .mutate()
+ .setHost(this._onionMap.get(onionURI.host))
+ .finalize();
+ }
+ return null;
+ }
+}
+
+let OnionAliasStore = new _OnionAliasStore();
diff --git a/browser/components/onionservices/moz.build b/browser/components/onionservices/moz.build
index 2661ad7cb9f3..815685322024 100644
--- a/browser/components/onionservices/moz.build
+++ b/browser/components/onionservices/moz.build
@@ -1 +1,7 @@
JAR_MANIFESTS += ["jar.mn"]
+
+EXTRA_JS_MODULES += [
+ "ExtensionMessaging.jsm",
+ "HttpsEverywhereControl.jsm",
+ "OnionAliasStore.jsm",
+]
diff --git a/browser/components/urlbar/UrlbarInput.jsm b/browser/components/urlbar/UrlbarInput.jsm
index 4e1ccac6bd17..27261544127c 100644
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -330,7 +330,10 @@ class UrlbarInput {
// user makes the input empty, switches tabs, and switches back, we want the
// URI to become visible again so the user knows what URI they're viewing.
if (value === null || (!value && dueToTabSwitch)) {
- uri = uri || this.window.gBrowser.currentURI;
+ uri =
+ uri ||
+ this.window.gBrowser.selectedBrowser.currentOnionAliasURI ||
+ this.window.gBrowser.currentURI;
// Strip off usernames and passwords for the location bar
try {
uri = Services.io.createExposableURI(uri);
@@ -2100,7 +2103,13 @@ class UrlbarInput {
}
let uri;
- if (this.getAttribute("pageproxystate") == "valid") {
+ // When we rewrite .onion to an alias, gBrowser.currentURI will be different than
+ // the URI displayed in the urlbar. We need to use the urlbar value to copy the
+ // alias instead of the actual .onion URI that is loaded.
+ if (
+ this.getAttribute("pageproxystate") == "valid" &&
+ !this.window.gBrowser.selectedBrowser.currentOnionAliasURI
+ ) {
uri = this.window.gBrowser.currentURI;
} else {
// The value could be:
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index cb5196bb6151..5c3acda391f2 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5764,6 +5764,10 @@ void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
return;
}
+ if (!mOnionUrlbarRewritesAllowed && IsTorOnionRedirect(oldURI, newURI)) {
+ mOnionUrlbarRewritesAllowed = true;
+ }
+
// DocumentChannel adds redirect chain to global history in the parent
// process. The redirect chain can't be queried from the content process, so
// there's no need to update global history here.
@@ -9184,6 +9188,20 @@ static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
}
+/* static */
+bool nsDocShell::IsTorOnionRedirect(nsIURI* aOldURI, nsIURI* aNewURI) {
+ nsAutoCString oldHost;
+ nsAutoCString newHost;
+ if (aOldURI && aNewURI && NS_SUCCEEDED(aOldURI->GetHost(oldHost)) &&
+ StringEndsWith(oldHost, ".tor.onion"_ns) &&
+ NS_SUCCEEDED(aNewURI->GetHost(newHost)) &&
+ StringEndsWith(newHost, ".onion"_ns) &&
+ !StringEndsWith(newHost, ".tor.onion"_ns)) {
+ return true;
+ }
+ return false;
+}
+
nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
Maybe<uint32_t> aCacheKey) {
MOZ_ASSERT(aLoadState, "need a load state!");
@@ -9337,6 +9355,30 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
+
+ if (mOnionUrlbarRewritesAllowed) {
+ mOnionUrlbarRewritesAllowed = false;
+ nsCOMPtr<nsIURI> referrer;
+ nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
+ if (referrerInfo) {
+ referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
+ bool isPrivateWin = false;
+ Document* doc = GetDocument();
+ if (doc) {
+ isPrivateWin =
+ doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ mOnionUrlbarRewritesAllowed =
+ secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
+ aLoadState->URI(), referrer, false, isPrivateWin));
+ }
+ }
+ }
+ mOnionUrlbarRewritesAllowed =
+ mOnionUrlbarRewritesAllowed ||
+ aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES);
+
mURIResultedInDocument = false; // reset the clock...
// See if this is actually a load between two history entries for the same
@@ -11710,6 +11752,7 @@ nsresult nsDocShell::AddToSessionHistory(
HistoryID(), GetCreatedDynamically(), originalURI,
resultPrincipalURI, loadReplace, referrerInfo, srcdoc,
srcdocEntry, baseURI, saveLayoutState, expired, userActivation);
+ entry->SetOnionUrlbarRewritesAllowed(mOnionUrlbarRewritesAllowed);
if (mBrowsingContext->IsTop() && GetSessionHistory()) {
bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
@@ -13628,3 +13671,12 @@ void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
mChannelToDisconnectOnPageHide = 0;
}
}
+
+NS_IMETHODIMP
+nsDocShell::GetOnionUrlbarRewritesAllowed(bool* aOnionUrlbarRewritesAllowed) {
+ NS_ENSURE_ARG(aOnionUrlbarRewritesAllowed);
+ *aOnionUrlbarRewritesAllowed =
+ StaticPrefs::browser_urlbar_onionRewrites_enabled() &&
+ mOnionUrlbarRewritesAllowed;
+ return NS_OK;
+}
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index 19b09f3bd470..06a5e1cb64e2 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -134,6 +134,9 @@ class nsDocShell final : public nsDocLoader,
// Whether the load should go through LoadURIDelegate.
INTERNAL_LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE = 0x2000,
+
+ // Whether rewriting the urlbar to a short .onion alias is allowed.
+ INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES = 0x4000,
};
// Event type dispatched by RestorePresentation
@@ -567,6 +570,8 @@ class nsDocShell final : public nsDocLoader,
virtual void DestroyChildren() override;
+ static bool IsTorOnionRedirect(nsIURI* aOldURI, nsIURI* aNewURI);
+
// Overridden from nsDocLoader, this provides more information than the
// normal OnStateChange with flags STATE_REDIRECTING
virtual void OnRedirectStateChange(nsIChannel* aOldChannel,
@@ -1256,6 +1261,7 @@ class nsDocShell final : public nsDocLoader,
bool mCSSErrorReportingEnabled : 1;
bool mAllowAuth : 1;
bool mAllowKeywordFixup : 1;
+ bool mOnionUrlbarRewritesAllowed : 1;
bool mDisableMetaRefreshWhenInactive : 1;
bool mIsAppTab : 1;
bool mDeviceSizeIsPageSize : 1;
diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp
index 6cac48a51728..9eb0e9307113 100644
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -874,6 +874,10 @@ void nsDocShellLoadState::CalculateLoadURIFlags() {
mInternalLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_FIRST_LOAD;
}
+ if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES) {
+ mInternalLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES;
+ }
+
if (mLoadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CLASSIFIER) {
mInternalLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
}
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
index 352b70d12030..bd373c54a632 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -838,4 +838,9 @@ interface nsIDocShell : nsIDocShellTreeItem
* until session history state is moved into the parent process.
*/
void persistLayoutHistoryState();
+
+ /**
+ * Whether rewriting the urlbar to a short .onion alias is allowed.
+ */
+ [infallible] readonly attribute boolean onionUrlbarRewritesAllowed;
};
diff --git a/docshell/base/nsIWebNavigation.idl b/docshell/base/nsIWebNavigation.idl
index bec4f13d8b2b..2ee46f3d6886 100644
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -268,6 +268,11 @@ interface nsIWebNavigation : nsISupports
*/
const unsigned long LOAD_FLAGS_USER_ACTIVATION = 0x8000000;
+ /**
+ * Allow rewriting the urlbar to a short .onion alias.
+ */
+ const unsigned long LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES = 0x10000000;
+
/**
* Loads a given URI. This will give priority to loading the requested URI
* in the object implementing this interface. If it can't be loaded here
diff --git a/docshell/shistory/SessionHistoryEntry.cpp b/docshell/shistory/SessionHistoryEntry.cpp
index 0361fb0c28c5..a6ac18d41c36 100644
--- a/docshell/shistory/SessionHistoryEntry.cpp
+++ b/docshell/shistory/SessionHistoryEntry.cpp
@@ -930,6 +930,20 @@ SessionHistoryEntry::SetPersist(bool aPersist) {
return NS_OK;
}
+NS_IMETHODIMP
+SessionHistoryEntry::GetOnionUrlbarRewritesAllowed(
+ bool* aOnionUrlbarRewritesAllowed) {
+ *aOnionUrlbarRewritesAllowed = mInfo->mOnionUrlbarRewritesAllowed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SessionHistoryEntry::SetOnionUrlbarRewritesAllowed(
+ bool aOnionUrlbarRewritesAllowed) {
+ mInfo->mOnionUrlbarRewritesAllowed = aOnionUrlbarRewritesAllowed;
+ return NS_OK;
+}
+
NS_IMETHODIMP
SessionHistoryEntry::GetScrollPosition(int32_t* aX, int32_t* aY) {
*aX = mInfo->mScrollPositionX;
diff --git a/docshell/shistory/SessionHistoryEntry.h b/docshell/shistory/SessionHistoryEntry.h
index a05fa42a2a94..6be4f2c4673f 100644
--- a/docshell/shistory/SessionHistoryEntry.h
+++ b/docshell/shistory/SessionHistoryEntry.h
@@ -170,6 +170,7 @@ class SessionHistoryInfo {
bool mPersist = true;
bool mHasUserInteraction = false;
bool mHasUserActivation = false;
+ bool mOnionUrlbarRewritesAllowed = false;
union SharedState {
SharedState();
diff --git a/docshell/shistory/nsISHEntry.idl b/docshell/shistory/nsISHEntry.idl
index 73ac40551d4e..622402456d07 100644
--- a/docshell/shistory/nsISHEntry.idl
+++ b/docshell/shistory/nsISHEntry.idl
@@ -260,6 +260,11 @@ interface nsISHEntry : nsISupports
*/
[infallible] attribute boolean persist;
+ /**
+ * Whether rewriting the urlbar to a short .onion alias is allowed.
+ */
+ [infallible] attribute boolean onionUrlbarRewritesAllowed;
+
/**
* Set/Get the visual viewport scroll position if session history is
* changed through anchor navigation or pushState.
diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp
index 1e4000eacd2b..41ea6086df8b 100644
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -44,7 +44,8 @@ nsSHEntry::nsSHEntry()
mLoadedInThisProcess(false),
mPersist(true),
mHasUserInteraction(false),
- mHasUserActivation(false) {}
+ mHasUserActivation(false),
+ mOnionUrlbarRewritesAllowed(false) {}
nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
: mShared(aOther.mShared),
@@ -72,7 +73,8 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
mLoadedInThisProcess(aOther.mLoadedInThisProcess),
mPersist(aOther.mPersist),
mHasUserInteraction(false),
- mHasUserActivation(aOther.mHasUserActivation) {}
+ mHasUserActivation(aOther.mHasUserActivation),
+ mOnionUrlbarRewritesAllowed(aOther.mOnionUrlbarRewritesAllowed) {}
nsSHEntry::~nsSHEntry() {
// Null out the mParent pointers on all our kids.
@@ -880,6 +882,18 @@ nsSHEntry::SetPersist(bool aPersist) {
return NS_OK;
}
+NS_IMETHODIMP
+nsSHEntry::GetOnionUrlbarRewritesAllowed(bool* aOnionUrlbarRewritesAllowed) {
+ *aOnionUrlbarRewritesAllowed = mOnionUrlbarRewritesAllowed;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetOnionUrlbarRewritesAllowed(bool aOnionUrlbarRewritesAllowed) {
+ mOnionUrlbarRewritesAllowed = aOnionUrlbarRewritesAllowed;
+ return NS_OK;
+}
+
NS_IMETHODIMP
nsSHEntry::CreateLoadInfo(nsDocShellLoadState** aLoadState) {
nsCOMPtr<nsIURI> uri = GetURI();
@@ -929,6 +943,10 @@ nsSHEntry::CreateLoadInfo(nsDocShellLoadState** aLoadState) {
} else {
srcdoc = VoidString();
}
+ if (GetOnionUrlbarRewritesAllowed()) {
+ flags |= nsDocShell::InternalLoad::
+ INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES;
+ }
loadState->SetSrcdocData(srcdoc);
loadState->SetBaseURI(baseURI);
loadState->SetInternalLoadFlags(flags);
diff --git a/docshell/shistory/nsSHEntry.h b/docshell/shistory/nsSHEntry.h
index 326b0092cf94..76be0ac65050 100644
--- a/docshell/shistory/nsSHEntry.h
+++ b/docshell/shistory/nsSHEntry.h
@@ -66,6 +66,7 @@ class nsSHEntry : public nsISHEntry {
bool mPersist;
bool mHasUserInteraction;
bool mHasUserActivation;
+ bool mOnionUrlbarRewritesAllowed;
};
#endif /* nsSHEntry_h */
diff --git a/dom/interfaces/base/nsIBrowser.idl b/dom/interfaces/base/nsIBrowser.idl
index 973a9244b8f8..b8a25de3629e 100644
--- a/dom/interfaces/base/nsIBrowser.idl
+++ b/dom/interfaces/base/nsIBrowser.idl
@@ -127,7 +127,8 @@ interface nsIBrowser : nsISupports
in boolean aIsSynthetic,
in boolean aHasRequestContextID,
in uint64_t aRequestContextID,
- in AString aContentType);
+ in AString aContentType,
+ in boolean aOnionUrlbarRewritesAllowed);
/**
* Determine what process switching behavior this browser element should have.
diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp
index 9f1bccda2efe..bb368b38c5f4 100644
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -3714,6 +3714,8 @@ NS_IMETHODIMP BrowserChild::OnLocationChange(nsIWebProgress* aWebProgress,
locationChangeData->mayEnableCharacterEncodingMenu() =
docShell->GetMayEnableCharacterEncodingMenu();
+ locationChangeData->onionUrlbarRewritesAllowed() =
+ docShell->GetOnionUrlbarRewritesAllowed();
locationChangeData->contentPrincipal() = document->NodePrincipal();
locationChangeData->contentPartitionedPrincipal() =
diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp
index 4145111ae849..10f94926a53e 100644
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -2788,7 +2788,8 @@ mozilla::ipc::IPCResult BrowserParent::RecvOnLocationChange(
aLocationChangeData->isSyntheticDocument(),
aLocationChangeData->requestContextID().isSome(),
aLocationChangeData->requestContextID().valueOr(0),
- aLocationChangeData->contentType());
+ aLocationChangeData->contentType(),
+ aLocationChangeData->onionUrlbarRewritesAllowed());
}
}
diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl
index 5706c7f5da00..5b21a809251e 100644
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -143,6 +143,7 @@ struct WebProgressLocationChangeData
bool isNavigating;
bool isSyntheticDocument;
bool mayEnableCharacterEncodingMenu;
+ bool onionUrlbarRewritesAllowed;
nsString contentType;
nsString title;
nsString charset;
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
index 3f578a7d37bc..96796d1be185 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -1338,6 +1338,12 @@
value: true
mirror: always
+ # Whether rewriting the urlbar to a short .onion alias is allowed.
+- name: browser.urlbar.onionRewrites.enabled
+ type: RelaxedAtomicBool
+ value: true
+ mirror: always
+
- name: browser.viewport.desktopWidth
type: RelaxedAtomicInt32
value: 980
diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat
index c67235fae029..189625f266ac 100644
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -5528,6 +5528,8 @@ pro.om
// onion : https://tools.ietf.org/html/rfc7686
onion
+tor.onion
+securedrop.tor.onion
// org : https://en.wikipedia.org/wiki/.org
org
diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp
index 0b460750971a..d76a14e1203a 100644
--- a/netwerk/ipc/DocumentLoadListener.cpp
+++ b/netwerk/ipc/DocumentLoadListener.cpp
@@ -2539,6 +2539,16 @@ DocumentLoadListener::AsyncOnChannelRedirect(
"mHaveVisibleRedirect=%c",
this, mHaveVisibleRedirect ? 'T' : 'F'));
+ // Like the code above for allowing mixed content, we need to check this here
+ // in case the redirect is not handled in the docshell.
+ nsCOMPtr<nsIURI> oldURI, newURI;
+ aOldChannel->GetURI(getter_AddRefs(oldURI));
+ aNewChannel->GetURI(getter_AddRefs(newURI));
+ if (nsDocShell::IsTorOnionRedirect(oldURI, newURI)) {
+ mLoadStateInternalLoadFlags |=
+ nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES;
+ }
+
// We need the original URI of the current channel to use to open the real
// channel in the content process. Unfortunately we overwrite the original
// uri of the new channel with the original pre-redirect URI, so grab
diff --git a/toolkit/content/widgets/browser-custom-element.js b/toolkit/content/widgets/browser-custom-element.js
index 59a7a5b43522..8120ca995103 100644
--- a/toolkit/content/widgets/browser-custom-element.js
+++ b/toolkit/content/widgets/browser-custom-element.js
@@ -255,6 +255,8 @@
this._mayEnableCharacterEncodingMenu = null;
+ this._onionUrlbarRewritesAllowed = false;
+
this._contentPrincipal = null;
this._contentPartitionedPrincipal = null;
@@ -583,6 +585,12 @@
}
}
+ get onionUrlbarRewritesAllowed() {
+ return this.isRemoteBrowser
+ ? this._onionUrlbarRewritesAllowed
+ : this.docShell.onionUrlbarRewritesAllowed;
+ }
+
get contentPrincipal() {
return this.isRemoteBrowser
? this._contentPrincipal
@@ -1112,7 +1120,8 @@
aIsSynthetic,
aHaveRequestContextID,
aRequestContextID,
- aContentType
+ aContentType,
+ aOnionUrlbarRewritesAllowed
) {
if (this.isRemoteBrowser && this.messageManager) {
if (aCharset != null) {
@@ -1134,6 +1143,7 @@
this._contentRequestContextID = aHaveRequestContextID
? aRequestContextID
: null;
+ this._onionUrlbarRewritesAllowed = aOnionUrlbarRewritesAllowed;
}
}
@@ -1535,6 +1545,7 @@
"_contentPrincipal",
"_contentPartitionedPrincipal",
"_isSyntheticDocument",
+ "_onionUrlbarRewritesAllowed",
]
);
}
diff --git a/toolkit/modules/sessionstore/SessionHistory.jsm b/toolkit/modules/sessionstore/SessionHistory.jsm
index f02930aa6e22..e78ec8ddf6b7 100644
--- a/toolkit/modules/sessionstore/SessionHistory.jsm
+++ b/toolkit/modules/sessionstore/SessionHistory.jsm
@@ -310,6 +310,7 @@ var SessionHistoryInternal = {
}
entry.persist = shEntry.persist;
+ entry.onionUrlbarRewritesAllowed = shEntry.onionUrlbarRewritesAllowed;
return entry;
},
@@ -604,6 +605,10 @@ var SessionHistoryInternal = {
}
}
+ if (entry.onionUrlbarRewritesAllowed) {
+ shEntry.onionUrlbarRewritesAllowed = entry.onionUrlbarRewritesAllowed;
+ }
+
return shEntry;
},
diff --git a/xpcom/reflect/xptinfo/xptinfo.h b/xpcom/reflect/xptinfo/xptinfo.h
index efee881c1421..4295efb39f1f 100644
--- a/xpcom/reflect/xptinfo/xptinfo.h
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -514,7 +514,8 @@ static_assert(sizeof(nsXPTMethodInfo) == 8, "wrong size");
#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
# define PARAM_BUFFER_COUNT 18
#else
-# define PARAM_BUFFER_COUNT 14
+// The max is currently updateForLocationChange in nsIBrowser.idl
+# define PARAM_BUFFER_COUNT 15
#endif
/**
1
0

[tor-browser/tor-browser-91.6.0esr-11.0-1] Bug 33852: Clean up about:logins (LockWise) to avoid mentioning sync, etc.
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit 729eeb2ef08adbfbbabb57eb352f852fa14a3f04
Author: Kathy Brade <brade(a)pearlcrescent.com>
Date: Tue Jul 14 11:15:07 2020 -0400
Bug 33852: Clean up about:logins (LockWise) to avoid mentioning sync, etc.
Hide elements on about:logins that mention sync, "Firefox LockWise", and
Mozilla's LockWise mobile apps.
Disable the "Create New Login" button when security.nocertdb is true.
---
browser/components/aboutlogins/AboutLoginsParent.jsm | 2 ++
browser/components/aboutlogins/content/aboutLogins.css | 8 +++++++-
browser/components/aboutlogins/content/aboutLogins.js | 6 ++++++
.../aboutlogins/content/components/fxaccounts-button.css | 5 +++++
.../components/aboutlogins/content/components/menu-button.css | 10 ++++++++++
5 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/browser/components/aboutlogins/AboutLoginsParent.jsm b/browser/components/aboutlogins/AboutLoginsParent.jsm
index db0b55d26abc..39fd2356ce99 100644
--- a/browser/components/aboutlogins/AboutLoginsParent.jsm
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -61,6 +61,7 @@ XPCOMUtils.defineLazyGetter(this, "AboutLoginsL10n", () => {
const ABOUT_LOGINS_ORIGIN = "about:logins";
const MASTER_PASSWORD_NOTIFICATION_ID = "master-password-login-required";
+const NOCERTDB_PREF = "security.nocertdb";
// about:logins will always use the privileged content process,
// even if it is disabled for other consumers such as about:newtab.
@@ -273,6 +274,7 @@ class AboutLoginsParent extends JSWindowActorParent {
importVisible:
Services.policies.isAllowed("profileImport") &&
AppConstants.platform != "linux",
+ canCreateLogins: !Services.prefs.getBoolPref(NOCERTDB_PREF, false),
});
await AboutLogins._sendAllLoginRelatedObjects(
diff --git a/browser/components/aboutlogins/content/aboutLogins.css b/browser/components/aboutlogins/content/aboutLogins.css
index e3528ca49b84..eaa224178487 100644
--- a/browser/components/aboutlogins/content/aboutLogins.css
+++ b/browser/components/aboutlogins/content/aboutLogins.css
@@ -69,6 +69,11 @@ login-item {
grid-area: login;
}
+/* Do not promote Mozilla Sync in Tor Browser. */
+login-intro {
+ display: none !important;
+}
+
#branding-logo {
flex-basis: var(--sidebar-width);
flex-shrink: 0;
@@ -83,7 +88,8 @@ login-item {
}
}
-:root:not(.official-branding) #branding-logo {
+/* Hide "Firefox LockWise" branding in Tor Browser. */
+#branding-logo {
visibility: hidden;
}
diff --git a/browser/components/aboutlogins/content/aboutLogins.js b/browser/components/aboutlogins/content/aboutLogins.js
index 494ef5c7a15b..27ff0295f2f6 100644
--- a/browser/components/aboutlogins/content/aboutLogins.js
+++ b/browser/components/aboutlogins/content/aboutLogins.js
@@ -22,6 +22,9 @@ const gElements = {
".menuitem-remove-all-logins"
);
},
+ get createNewLoginButton() {
+ return this.loginList.shadowRoot.querySelector(".create-login-button");
+ },
};
let numberOfLogins = 0;
@@ -128,6 +131,9 @@ window.addEventListener("AboutLoginsChromeToContent", event => {
gElements.loginList.setSortDirection(event.detail.value.selectedSort);
document.documentElement.classList.add("initialized");
gElements.loginList.classList.add("initialized");
+ if (!event.detail.value.canCreateLogins) {
+ gElements.createNewLoginButton.disabled = true;
+ }
break;
}
case "ShowLoginItemError": {
diff --git a/browser/components/aboutlogins/content/components/fxaccounts-button.css b/browser/components/aboutlogins/content/components/fxaccounts-button.css
index c8925f6fc75d..55c2a8810fa1 100644
--- a/browser/components/aboutlogins/content/components/fxaccounts-button.css
+++ b/browser/components/aboutlogins/content/components/fxaccounts-button.css
@@ -8,6 +8,11 @@
align-items: center;
}
+/* Do not promote Mozilla Sync in Tor Browser. */
+.logged-out-view {
+ display: none !important;
+}
+
.fxaccounts-extra-text {
/* Only show at most 3 lines of text to limit the
text from overflowing the header. */
diff --git a/browser/components/aboutlogins/content/components/menu-button.css b/browser/components/aboutlogins/content/components/menu-button.css
index 99ca6a711093..24cdb48773f9 100644
--- a/browser/components/aboutlogins/content/components/menu-button.css
+++ b/browser/components/aboutlogins/content/components/menu-button.css
@@ -92,3 +92,13 @@
.menuitem-preferences {
background-image: url("chrome://global/skin/icons/settings.svg");
}
+
+/*
+ * Do not promote LockWise mobile apps in Tor Browser: hide the menu items
+ * and the separator line that precedes them.
+ */
+.menuitem-mobile-android,
+.menuitem-mobile-ios,
+button[data-event-name="AboutLoginsGetHelp"] + hr {
+ display: none !important;
+}
1
0

[tor-browser/tor-browser-91.6.0esr-11.0-1] Bug 21952: Implement Onion-Location
by richard@torproject.org 02 Feb '22
by richard@torproject.org 02 Feb '22
02 Feb '22
commit 082fd9885cae045736a49eb1fd15cc2be1bb6ddc
Author: Alex Catarineu <acat(a)torproject.org>
Date: Thu Mar 5 22:16:39 2020 +0100
Bug 21952: Implement Onion-Location
Whenever a valid Onion-Location HTTP header (or corresponding HTML
<meta> http-equiv attribute) is found in a document load, we either
redirect to it (if the user opted-in via preference) or notify the
presence of an onionsite alternative with a badge in the urlbar.
---
browser/base/content/browser.js | 12 ++
browser/base/content/navigator-toolbox.inc.xhtml | 3 +
browser/components/BrowserGlue.jsm | 13 ++
.../onionservices/OnionLocationChild.jsm | 39 +++++
.../onionservices/OnionLocationParent.jsm | 168 +++++++++++++++++++++
.../content/onionlocation-notification-icons.css | 5 +
.../onionservices/content/onionlocation-urlbar.css | 60 ++++++++
.../content/onionlocation-urlbar.inc.xhtml | 10 ++
.../onionservices/content/onionlocation.svg | 3 +
.../content/onionlocationPreferences.inc.xhtml | 11 ++
.../content/onionlocationPreferences.js | 31 ++++
browser/components/onionservices/jar.mn | 2 +
browser/components/onionservices/moz.build | 2 +
browser/components/preferences/privacy.inc.xhtml | 2 +
browser/components/preferences/privacy.js | 17 +++
browser/themes/shared/notification-icons.inc.css | 2 +
browser/themes/shared/urlbar-searchbar.inc.css | 2 +
dom/base/Document.cpp | 34 ++++-
dom/base/Document.h | 2 +
dom/webidl/Document.webidl | 8 +
modules/libpref/init/StaticPrefList.yaml | 5 +
xpcom/ds/StaticAtoms.py | 1 +
22 files changed, 431 insertions(+), 1 deletion(-)
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 8d0ac92ae3b0..ac429023ab35 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -48,6 +48,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
NetUtil: "resource://gre/modules/NetUtil.jsm",
NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
+ OnionLocationParent: "resource:///modules/OnionLocationParent.jsm",
PageActions: "resource:///modules/PageActions.jsm",
PageThumbs: "resource://gre/modules/PageThumbs.jsm",
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
@@ -5297,6 +5298,7 @@ var XULBrowserWindow = {
CFRPageActions.updatePageActions(gBrowser.selectedBrowser);
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
+ OnionLocationParent.updateOnionLocationBadge(gBrowser.selectedBrowser);
if (!gMultiProcessBrowser) {
// Bug 1108553 - Cannot rotate images with e10s
@@ -5787,6 +5789,16 @@ var CombinedStopReload = {
var TabsProgressListener = {
onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // Clear OnionLocation UI
+ if (
+ aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aRequest &&
+ aWebProgress.isTopLevel
+ ) {
+ OnionLocationParent.onStateChange(aBrowser);
+ }
+
// Collect telemetry data about tab load times.
if (
aWebProgress.isTopLevel &&
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index 7a2715e9e604..1aad36ab3bfc 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -358,6 +358,9 @@
onclick="FullZoom.reset(); FullZoom.resetScalingZoom();"
tooltip="dynamic-shortcut-tooltip"
hidden="true"/>
+
+#include ../../components/onionservices/content/onionlocation-urlbar.inc.xhtml
+
<hbox id="pageActionButton"
class="urlbar-page-action urlbar-icon-wrapper"
role="button"
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index 9d1a7e20abc5..32df8e7b8c47 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -545,6 +545,19 @@ let JSWINDOWACTORS = {
allFrames: true,
},
+ OnionLocation: {
+ parent: {
+ moduleURI: "resource:///modules/OnionLocationParent.jsm",
+ },
+ child: {
+ moduleURI: "resource:///modules/OnionLocationChild.jsm",
+ events: {
+ pageshow: { mozSystemGroup: true },
+ },
+ },
+ messageManagerGroups: ["browsers"],
+ },
+
PageInfo: {
child: {
moduleURI: "resource:///actors/PageInfoChild.jsm",
diff --git a/browser/components/onionservices/OnionLocationChild.jsm b/browser/components/onionservices/OnionLocationChild.jsm
new file mode 100644
index 000000000000..9e00054ac56c
--- /dev/null
+++ b/browser/components/onionservices/OnionLocationChild.jsm
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["OnionLocationChild"];
+
+class OnionLocationChild extends JSWindowActorChild {
+ handleEvent(event) {
+ this.onPageShow(event);
+ }
+
+ onPageShow(event) {
+ if (event.target != this.document) {
+ return;
+ }
+ const onionLocationURI = this.document.onionLocationURI;
+ if (onionLocationURI) {
+ this.sendAsyncMessage("OnionLocation:Set");
+ }
+ }
+
+ receiveMessage(aMessage) {
+ if (aMessage.name == "OnionLocation:Refresh") {
+ const doc = this.document;
+ const docShell = this.docShell;
+ const onionLocationURI = doc.onionLocationURI;
+ const refreshURI = docShell.QueryInterface(Ci.nsIRefreshURI);
+ if (onionLocationURI && refreshURI) {
+ refreshURI.refreshURI(
+ onionLocationURI,
+ doc.nodePrincipal,
+ 0,
+ false,
+ true
+ );
+ }
+ }
+ }
+}
diff --git a/browser/components/onionservices/OnionLocationParent.jsm b/browser/components/onionservices/OnionLocationParent.jsm
new file mode 100644
index 000000000000..f6250e554862
--- /dev/null
+++ b/browser/components/onionservices/OnionLocationParent.jsm
@@ -0,0 +1,168 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["OnionLocationParent"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+// Prefs
+const NOTIFICATION_PREF = "privacy.prioritizeonions.showNotification";
+const PRIORITIZE_ONIONS_PREF = "privacy.prioritizeonions.enabled";
+
+// Element IDs
+const ONIONLOCATION_BOX_ID = "onion-location-box";
+const ONIONLOCATION_BUTTON_ID = "onion-location-button";
+const ONIONLOCATION_LABEL_ID = "onion-label";
+
+// Notification IDs
+const NOTIFICATION_ID = "onion-location";
+const NOTIFICATION_ANCHOR_ID = "onionlocation";
+
+// Strings
+const STRING_ONION_AVAILABLE = TorStrings.onionLocation.onionAvailable;
+const NOTIFICATION_CANCEL_LABEL = TorStrings.onionLocation.notNow;
+const NOTIFICATION_CANCEL_ACCESSKEY = TorStrings.onionLocation.notNowAccessKey;
+const NOTIFICATION_OK_LABEL = TorStrings.onionLocation.alwaysPrioritize;
+const NOTIFICATION_OK_ACCESSKEY =
+ TorStrings.onionLocation.alwaysPrioritizeAccessKey;
+const NOTIFICATION_TITLE = TorStrings.onionLocation.tryThis;
+const NOTIFICATION_DESCRIPTION = TorStrings.onionLocation.description;
+const NOTIFICATION_LEARN_MORE_URL = TorStrings.onionLocation.learnMoreURL;
+
+class OnionLocationParent extends JSWindowActorParent {
+ // Listeners are added in BrowserGlue.jsm
+ receiveMessage(aMsg) {
+ switch (aMsg.name) {
+ case "OnionLocation:Set":
+ let browser = this.browsingContext.embedderElement;
+ OnionLocationParent.setOnionLocation(browser);
+ break;
+ }
+ }
+
+ static buttonClick(event) {
+ if (event.button !== 0) {
+ return;
+ }
+ const win = event.target.ownerGlobal;
+ if (win.gBrowser) {
+ const browser = win.gBrowser.selectedBrowser;
+ OnionLocationParent.redirect(browser);
+ }
+ }
+
+ static redirect(browser) {
+ let windowGlobal = browser.browsingContext.currentWindowGlobal;
+ let actor = windowGlobal.getActor("OnionLocation");
+ if (actor) {
+ actor.sendAsyncMessage("OnionLocation:Refresh", {});
+ OnionLocationParent.setDisabled(browser);
+ }
+ }
+
+ static onStateChange(browser) {
+ delete browser._onionLocation;
+ OnionLocationParent.hideNotification(browser);
+ }
+
+ static setOnionLocation(browser) {
+ browser._onionLocation = true;
+ let tabBrowser = browser.getTabBrowser();
+ if (tabBrowser && browser === tabBrowser.selectedBrowser) {
+ OnionLocationParent.updateOnionLocationBadge(browser);
+ }
+ }
+
+ static hideNotification(browser) {
+ const win = browser.ownerGlobal;
+ if (browser._onionLocationPrompt) {
+ win.PopupNotifications.remove(browser._onionLocationPrompt);
+ }
+ }
+
+ static showNotification(browser) {
+ const mustShow = Services.prefs.getBoolPref(NOTIFICATION_PREF, true);
+ if (!mustShow) {
+ return;
+ }
+
+ const win = browser.ownerGlobal;
+ Services.prefs.setBoolPref(NOTIFICATION_PREF, false);
+
+ const mainAction = {
+ label: NOTIFICATION_OK_LABEL,
+ accessKey: NOTIFICATION_OK_ACCESSKEY,
+ callback() {
+ Services.prefs.setBoolPref(PRIORITIZE_ONIONS_PREF, true);
+ OnionLocationParent.redirect(browser);
+ win.openPreferences("privacy-onionservices");
+ },
+ };
+
+ const cancelAction = {
+ label: NOTIFICATION_CANCEL_LABEL,
+ accessKey: NOTIFICATION_CANCEL_ACCESSKEY,
+ callback: () => {},
+ };
+
+ const options = {
+ autofocus: true,
+ persistent: true,
+ removeOnDismissal: false,
+ eventCallback(aTopic) {
+ if (aTopic === "removed") {
+ delete browser._onionLocationPrompt;
+ delete browser.onionpopupnotificationanchor;
+ }
+ },
+ learnMoreURL: NOTIFICATION_LEARN_MORE_URL,
+ displayURI: {
+ hostPort: NOTIFICATION_TITLE, // This is hacky, but allows us to have a title without extra markup/css.
+ },
+ hideClose: true,
+ popupIconClass: "onionlocation-notification-icon",
+ };
+
+ // A hacky way of setting the popup anchor outside the usual url bar icon box
+ // onionlocationpopupnotificationanchor comes from `${ANCHOR_ID}popupnotificationanchor`
+ // From https://searchfox.org/mozilla-esr68/rev/080f9ed47742644d2ff84f7aa0b10aea5c4…
+ browser.onionlocationpopupnotificationanchor = win.document.getElementById(
+ ONIONLOCATION_BUTTON_ID
+ );
+
+ browser._onionLocationPrompt = win.PopupNotifications.show(
+ browser,
+ NOTIFICATION_ID,
+ NOTIFICATION_DESCRIPTION,
+ NOTIFICATION_ANCHOR_ID,
+ mainAction,
+ [cancelAction],
+ options
+ );
+ }
+
+ static setEnabled(browser) {
+ const win = browser.ownerGlobal;
+ const label = win.document.getElementById(ONIONLOCATION_LABEL_ID);
+ label.textContent = STRING_ONION_AVAILABLE;
+ const elem = win.document.getElementById(ONIONLOCATION_BOX_ID);
+ elem.removeAttribute("hidden");
+ }
+
+ static setDisabled(browser) {
+ const win = browser.ownerGlobal;
+ const elem = win.document.getElementById(ONIONLOCATION_BOX_ID);
+ elem.setAttribute("hidden", true);
+ }
+
+ static updateOnionLocationBadge(browser) {
+ if (browser._onionLocation) {
+ OnionLocationParent.setEnabled(browser);
+ OnionLocationParent.showNotification(browser);
+ } else {
+ OnionLocationParent.setDisabled(browser);
+ }
+ }
+}
diff --git a/browser/components/onionservices/content/onionlocation-notification-icons.css b/browser/components/onionservices/content/onionlocation-notification-icons.css
new file mode 100644
index 000000000000..7c8a6d892c6f
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-notification-icons.css
@@ -0,0 +1,5 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+
+.onionlocation-notification-icon {
+ display: none;
+}
\ No newline at end of file
diff --git a/browser/components/onionservices/content/onionlocation-urlbar.css b/browser/components/onionservices/content/onionlocation-urlbar.css
new file mode 100644
index 000000000000..7b7051ace675
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-urlbar.css
@@ -0,0 +1,60 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+
+#onion-location-box {
+ height: 28px;
+
+ background-color: var(--purple-60);
+ -moz-context-properties: fill;
+ fill: white;
+}
+
+#onion-location-box:hover {
+ background-color: var(--purple-70);
+}
+
+#onion-location-box:active {
+ background-color: var(--purple-80);
+}
+
+@media (prefers-color-scheme: dark) {
+ #onion-location-box {
+ background-color: var(--purple-50);
+ }
+
+ #onion-location-box:hover {
+ background-color: var(--purple-60);
+ }
+
+ #onion-location-box:active {
+ background-color: var(--purple-70);
+ }
+}
+
+#onion-location-button {
+ list-style-image: url(chrome://browser/content/onionservices/onionlocation.svg);
+ padding-inline-start: 0.5em;
+}
+
+label#onion-label {
+ line-height: 28px;
+ margin: 0;
+ padding-block: 0;
+ padding-inline: 0.5em;
+ color: white;
+ font-weight: bold;
+}
+
+/* set appropriate sizes for the non-standard ui densities */
+:root[uidensity=compact] hbox.urlbar-page-action#onion-location-box {
+ height: 24px;
+}
+:root[uidensity=compact] label#onion-label {
+ line-height: 24px;
+}
+
+:root[uidensity=touch] hbox.urlbar-page-action#onion-location-box {
+ height: 30px;
+}
+:root[uidensity=touch] label#onion-label {
+ line-height: 30px;
+}
diff --git a/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml b/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml
new file mode 100644
index 000000000000..b612a4236f3c
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml
@@ -0,0 +1,10 @@
+# Copyright (c) 2020, The Tor Project, Inc.
+
+<hbox id="onion-location-box"
+ class="urlbar-icon-wrapper urlbar-page-action"
+ role="button"
+ hidden="true"
+ onclick="OnionLocationParent.buttonClick(event);">
+ <image id="onion-location-button" role="presentation"/>
+ <hbox id="onion-label-container"><label id="onion-label"/></hbox>
+</hbox>
diff --git a/browser/components/onionservices/content/onionlocation.svg b/browser/components/onionservices/content/onionlocation.svg
new file mode 100644
index 000000000000..37f40ac1812f
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation.svg
@@ -0,0 +1,3 @@
+<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">
+ <path fill="context-fill" fill-opacity="context-fill-opacity" d="m8.016411 14.54499v-0.969784c3.071908-0.0089 5.559239-2.501304 5.559239-5.575429 0-3.073903-2.487331-5.566336-5.559239-5.575206v-0.9697843c3.607473 0.00909 6.528802 2.935521 6.528802 6.544991 0 3.609691-2.921329 6.536342-6.528802 6.545213zm0-3.394356c1.732661-0.0091 3.135111-1.415756 3.135111-3.150857 0-1.734878-1.402451-3.141542-3.135111-3.150634v-0.9695626c2.268448 0.00887 4.104895 1.849753 4.104895 4.120197 0 2.270666-1.836447 4.111549-4.104895 4.120419zm0-4.846926c0.9294227 0.00887 1.680545 0.7644289 1.680545 1.696069 0 0.9318627-0.7511226 1.687421-1.680545 1.696291zm-8.016411 1.696069c0 4.418473 3.581527 8.000222 8 8.000222 4.418251 0 8-3.581749 8-8.000222 0-4.418251-3.581749-7.999778-8-7.999778-4.418473 0-8 3.581527-8 7.999778z" />
+</svg>
\ No newline at end of file
diff --git a/browser/components/onionservices/content/onionlocationPreferences.inc.xhtml b/browser/components/onionservices/content/onionlocationPreferences.inc.xhtml
new file mode 100644
index 000000000000..c285f403f99b
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocationPreferences.inc.xhtml
@@ -0,0 +1,11 @@
+# Copyright (c) 2020, The Tor Project, Inc.
+
+<groupbox id="onionServicesGroup" data-category="panePrivacy" data-subcategory="onionservices" hidden="true">
+ <label><html:h2 id="onionServicesTitle"></html:h2></label>
+ <label><label class="tail-with-learn-more" id="prioritizeOnionsDesc"></label><label
+ class="learnMore" is="text-link" id="onionServicesLearnMore"></label></label>
+ <radiogroup id="prioritizeOnionsRadioGroup" aria-labelledby="prioritizeOnionsDesc" preference="privacy.prioritizeonions.enabled">
+ <radio id="onionServicesRadioAlways" value="true"/>
+ <radio id="onionServicesRadioAsk" value="false"/>
+ </radiogroup>
+</groupbox>
diff --git a/browser/components/onionservices/content/onionlocationPreferences.js b/browser/components/onionservices/content/onionlocationPreferences.js
new file mode 100644
index 000000000000..aa569b54721c
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocationPreferences.js
@@ -0,0 +1,31 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "TorStrings",
+ "resource:///modules/TorStrings.jsm"
+);
+
+const OnionLocationPreferences = {
+ init() {
+ document.getElementById("onionServicesTitle").textContent =
+ TorStrings.onionLocation.onionServicesTitle;
+ document.getElementById("prioritizeOnionsDesc").textContent =
+ TorStrings.onionLocation.prioritizeOnionsDescription;
+ const learnMore = document.getElementById("onionServicesLearnMore");
+ learnMore.textContent = TorStrings.onionLocation.learnMore;
+ learnMore.href = TorStrings.onionLocation.learnMoreURL;
+ document.getElementById("onionServicesRadioAlways").label =
+ TorStrings.onionLocation.always;
+ document.getElementById("onionServicesRadioAsk").label =
+ TorStrings.onionLocation.askEverytime;
+ },
+};
+
+Object.defineProperty(this, "OnionLocationPreferences", {
+ value: OnionLocationPreferences,
+ enumerable: true,
+ writable: false,
+});
diff --git a/browser/components/onionservices/jar.mn b/browser/components/onionservices/jar.mn
index 9d6ce88d1841..f45b16dc5d29 100644
--- a/browser/components/onionservices/jar.mn
+++ b/browser/components/onionservices/jar.mn
@@ -7,3 +7,5 @@ browser.jar:
content/browser/onionservices/onionservices.css (content/onionservices.css)
content/browser/onionservices/savedKeysDialog.js (content/savedKeysDialog.js)
content/browser/onionservices/savedKeysDialog.xhtml (content/savedKeysDialog.xhtml)
+ content/browser/onionservices/onionlocationPreferences.js (content/onionlocationPreferences.js)
+ content/browser/onionservices/onionlocation.svg (content/onionlocation.svg)
diff --git a/browser/components/onionservices/moz.build b/browser/components/onionservices/moz.build
index 815685322024..8027233d65a6 100644
--- a/browser/components/onionservices/moz.build
+++ b/browser/components/onionservices/moz.build
@@ -4,4 +4,6 @@ EXTRA_JS_MODULES += [
"ExtensionMessaging.jsm",
"HttpsEverywhereControl.jsm",
"OnionAliasStore.jsm",
+ "OnionLocationChild.jsm",
+ "OnionLocationParent.jsm",
]
diff --git a/browser/components/preferences/privacy.inc.xhtml b/browser/components/preferences/privacy.inc.xhtml
index 18d01fee6b33..66d7ef724f83 100644
--- a/browser/components/preferences/privacy.inc.xhtml
+++ b/browser/components/preferences/privacy.inc.xhtml
@@ -14,6 +14,8 @@
<html:h1 data-l10n-id="privacy-header"/>
</hbox>
+#include ../onionservices/content/onionlocationPreferences.inc.xhtml
+
<!-- Tracking / Content Blocking -->
<groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" aria-describedby="contentBlockingDescription">
<label id="contentBlockingHeader"><html:h2 data-l10n-id="content-blocking-enhanced-tracking-protection"/></label>
diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js
index 932d4291e486..1985b5489fc1 100644
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -93,6 +93,12 @@ XPCOMUtils.defineLazyScriptGetter(
"chrome://browser/content/securitylevel/securityLevel.js"
);
+XPCOMUtils.defineLazyScriptGetter(
+ this,
+ ["OnionLocationPreferences"],
+ "chrome://browser/content/onionservices/onionlocationPreferences.js"
+);
+
XPCOMUtils.defineLazyServiceGetter(
this,
"listManager",
@@ -169,6 +175,9 @@ Preferences.addAll([
// Do not track
{ id: "privacy.donottrackheader.enabled", type: "bool" },
+ // Onion Location
+ { id: "privacy.prioritizeonions.enabled", type: "bool" },
+
// Media
{ id: "media.autoplay.default", type: "int" },
@@ -333,6 +342,13 @@ var gPrivacyPane = {
window.addEventListener("unload", unload);
},
+ /**
+ * Show the OnionLocation preferences UI
+ */
+ _initOnionLocation() {
+ OnionLocationPreferences.init();
+ },
+
/**
* Whether the prompt to restart Firefox should appear when changing the autostart pref.
*/
@@ -530,6 +546,7 @@ var gPrivacyPane = {
this._initTrackingProtectionExtensionControl();
OnionServicesAuthPreferences.init();
this._initSecurityLevel();
+ this._initOnionLocation();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
diff --git a/browser/themes/shared/notification-icons.inc.css b/browser/themes/shared/notification-icons.inc.css
index 67dd640baf16..83248f71c60d 100644
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -449,3 +449,5 @@
-moz-context-properties: fill;
fill: var(--panel-banner-item-warning-icon-bgcolor);
}
+
+%include ../../components/onionservices/content/onionlocation-notification-icons.css
diff --git a/browser/themes/shared/urlbar-searchbar.inc.css b/browser/themes/shared/urlbar-searchbar.inc.css
index 82675dae2041..0158597991ec 100644
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -745,3 +745,5 @@ moz-input-box > menupopup .context-menu-add-engine > .menu-iconic-left::after {
.searchbar-textbox::placeholder {
opacity: 0.69;
}
+
+%include ../../components/onionservices/content/onionlocation-urlbar.css
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index a58e76cb5258..b00399e2eccb 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -2779,6 +2779,7 @@ void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
// mDocumentURI.
mDocumentBaseURI = nullptr;
mChromeXHRDocBaseURI = nullptr;
+ mOnionLocationURI = nullptr;
// Check if the current document is the top-level DevTools document.
// For inner DevTools frames, mIsDevToolsDocument will be set when
@@ -6486,6 +6487,22 @@ void Document::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const {
}
}
+static bool IsValidOnionLocation(nsIURI* aDocumentURI,
+ nsIURI* aOnionLocationURI) {
+ bool isHttpish;
+ nsAutoCString host;
+ return aDocumentURI && aOnionLocationURI &&
+ NS_SUCCEEDED(aDocumentURI->SchemeIs("https", &isHttpish)) &&
+ isHttpish && NS_SUCCEEDED(aDocumentURI->GetAsciiHost(host)) &&
+ !StringEndsWith(host, ".onion"_ns) &&
+ ((NS_SUCCEEDED(aOnionLocationURI->SchemeIs("http", &isHttpish)) &&
+ isHttpish) ||
+ (NS_SUCCEEDED(aOnionLocationURI->SchemeIs("https", &isHttpish)) &&
+ isHttpish)) &&
+ NS_SUCCEEDED(aOnionLocationURI->GetAsciiHost(host)) &&
+ StringEndsWith(host, ".onion"_ns);
+}
+
void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
if (!aHeaderField) {
NS_ERROR("null headerField");
@@ -6560,6 +6577,21 @@ void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
aHeaderField == nsGkAtoms::handheldFriendly) {
mViewportType = Unknown;
}
+
+ if (aHeaderField == nsGkAtoms::headerOnionLocation && !aData.IsEmpty()) {
+ nsCOMPtr<nsIURI> onionURI;
+ if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(onionURI), aData)) &&
+ IsValidOnionLocation(Document::GetDocumentURI(), onionURI)) {
+ if (StaticPrefs::privacy_prioritizeonions_enabled()) {
+ nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
+ if (refresher) {
+ refresher->RefreshURI(onionURI, NodePrincipal(), 0, false, true);
+ }
+ } else {
+ mOnionLocationURI = onionURI;
+ }
+ }
+ }
}
void Document::TryChannelCharset(nsIChannel* aChannel, int32_t& aCharsetSource,
@@ -10697,7 +10729,7 @@ void Document::RetrieveRelevantHeaders(nsIChannel* aChannel) {
static const char* const headers[] = {
"default-style", "content-style-type", "content-language",
"content-disposition", "refresh", "x-dns-prefetch-control",
- "x-frame-options",
+ "x-frame-options", "onion-location",
// add more http headers if you need
// XXXbz don't add content-location support without reading bug
// 238654 and its dependencies/dups first.
diff --git a/dom/base/Document.h b/dom/base/Document.h
index 7165496397f3..c8de049526ea 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3365,6 +3365,7 @@ class Document : public nsINode,
void ReleaseCapture() const;
void MozSetImageElement(const nsAString& aImageElementId, Element* aElement);
nsIURI* GetDocumentURIObject() const;
+ nsIURI* GetOnionLocationURI() const { return mOnionLocationURI; }
// Not const because all the fullscreen goop is not const
const char* GetFullscreenError(CallerType);
bool FullscreenEnabled(CallerType aCallerType) {
@@ -4348,6 +4349,7 @@ class Document : public nsINode,
nsCOMPtr<nsIURI> mChromeXHRDocURI;
nsCOMPtr<nsIURI> mDocumentBaseURI;
nsCOMPtr<nsIURI> mChromeXHRDocBaseURI;
+ nsCOMPtr<nsIURI> mOnionLocationURI;
// The base domain of the document for third-party checks.
nsCString mBaseDomain;
diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl
index a139ace11d4a..d934cf8da045 100644
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -711,3 +711,11 @@ partial interface Document {
[ChromeOnly]
void setNotifyFormOrPasswordRemoved(boolean aShouldNotify);
};
+
+/**
+ * Extension to allows chrome JS to know whether the document has a valid
+ * Onion-Location that we could redirect to.
+ */
+partial interface Document {
+ [ChromeOnly] readonly attribute URI? onionLocationURI;
+};
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
index 96796d1be185..8567b4e5a227 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -10478,6 +10478,11 @@
value: ""
mirror: never
+- name: privacy.prioritizeonions.enabled
+ type: RelaxedAtomicBool
+ value: false
+ mirror: always
+
#---------------------------------------------------------------------------
# Prefs starting with "prompts."
#---------------------------------------------------------------------------
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
index 2f5be143517b..f620f57a1213 100644
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -821,6 +821,7 @@ STATIC_ATOMS = [
Atom("oninputsourceschange", "oninputsourceschange"),
Atom("oninstall", "oninstall"),
Atom("oninvalid", "oninvalid"),
+ Atom("headerOnionLocation", "onion-location"),
Atom("onkeydown", "onkeydown"),
Atom("onkeypress", "onkeypress"),
Atom("onkeyup", "onkeyup"),
1
0