[tbb-commits] [tor-browser/tor-browser-68.6.0esr-9.5-1] Bug 28005: Implement .onion alias urlbar rewrites
sysrqb at torproject.org
sysrqb at torproject.org
Fri Apr 3 02:22:07 UTC 2020
commit 7306a08365be9212f621b396513352d19549c487
Author: Alex Catarineu <acat at 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.
---
browser/actors/ClickHandlerChild.jsm | 20 +++
browser/actors/ContextMenuChild.jsm | 4 +
browser/base/content/browser-places.js | 12 +-
browser/base/content/browser.js | 37 +++-
browser/base/content/nsContextMenu.js | 18 ++
browser/base/content/tabbrowser.js | 7 +
browser/base/content/utilityOverlay.js | 2 +
browser/components/BrowserGlue.jsm | 6 +
.../onionservices/ExtensionMessaging.jsm | 86 +++++++++
.../onionservices/HttpsEverywhereControl.jsm | 119 ++++++++++++
.../components/onionservices/OnionAliasStore.jsm | 199 +++++++++++++++++++++
browser/components/onionservices/moz.build | 6 +
browser/components/urlbar/UrlbarInput.jsm | 8 +-
browser/modules/ContentClick.jsm | 1 +
docshell/base/nsDocShell.cpp | 37 ++++
docshell/base/nsDocShell.h | 4 +
docshell/base/nsDocShellLoadState.cpp | 4 +
docshell/base/nsIDocShell.idl | 5 +
docshell/base/nsIWebNavigation.idl | 5 +
docshell/shistory/nsISHEntry.idl | 5 +
docshell/shistory/nsSHEntry.cpp | 18 +-
docshell/shistory/nsSHEntry.h | 1 +
modules/libpref/init/StaticPrefList.h | 7 +
netwerk/dns/effective_tld_names.dat | 2 +
.../remotebrowserutils/RemoteWebNavigation.jsm | 1 +
toolkit/content/widgets/browser-custom-element.js | 9 +
toolkit/modules/RemoteWebProgress.jsm | 2 +
toolkit/modules/WebProgressChild.jsm | 1 +
toolkit/modules/sessionstore/SessionHistory.jsm | 5 +
29 files changed, 622 insertions(+), 9 deletions(-)
diff --git a/browser/actors/ClickHandlerChild.jsm b/browser/actors/ClickHandlerChild.jsm
index 4375be0067ac..263a5fea053e 100644
--- a/browser/actors/ClickHandlerChild.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -156,6 +156,26 @@ class ClickHandlerChild extends ActorChild {
json.originPrincipal = ownerDoc.nodePrincipal;
json.triggeringPrincipal = ownerDoc.nodePrincipal;
+ // Check if the link needs to be opened with .tor.onion urlbar rewrites
+ // allowed. Only when the owner doc has allowOnionUrlbarRewrites = true
+ // and the same origin we should allow this.
+ json.allowOnionUrlbarRewrites = false;
+ if (this.mm.docShell.allowOnionUrlbarRewrites) {
+ 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.allowOnionUrlbarRewrites = 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/ContextMenuChild.jsm b/browser/actors/ContextMenuChild.jsm
index 546f51e843d6..5915832eae7a 100644
--- a/browser/actors/ContextMenuChild.jsm
+++ b/browser/actors/ContextMenuChild.jsm
@@ -543,6 +543,9 @@ class ContextMenuChild extends ActorChild {
// The same-origin check will be done in nsContextMenu.openLinkInTab.
let parentAllowsMixedContent = !!this.docShell.mixedContentChannel;
+ let parentAllowsOnionUrlbarRewrites = this.docShell
+ .allowOnionUrlbarRewrites;
+
// Get referrer attribute from clicked link and parse it
let referrerAttrValue = Services.netUtils.parseAttributePolicyString(
aEvent.composedTarget.getAttribute("referrerpolicy")
@@ -659,6 +662,7 @@ class ContextMenuChild extends ActorChild {
popupNodeSelectors,
disableSetDesktopBg,
parentAllowsMixedContent,
+ parentAllowsOnionUrlbarRewrites,
};
Services.obs.notifyObservers(
diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js
index 6aa348c609a4..a2dd38a97514 100755
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -440,7 +440,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;
@@ -544,7 +545,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)) {
@@ -1618,14 +1619,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.js b/browser/base/content/browser.js
index 3f9bf006f562..2c89d658d4ec 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -72,6 +72,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
Translation: "resource:///modules/translation/Translation.jsm",
+ OnionAliasStore: "resource:///modules/OnionAliasStore.jsm",
UITour: "resource:///modules/UITour.jsm",
UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
UrlbarInput: "resource:///modules/UrlbarInput.jsm",
@@ -3335,7 +3336,10 @@ function URLBarSetURI(aURI, updatePopupNotifications) {
// bar if the user has deleted the URL and we'd just put the same URL
// back. See bug 304198.
if (value === null) {
- let uri = aURI || gBrowser.currentURI;
+ let uri =
+ aURI ||
+ gBrowser.selectedBrowser.currentOnionAliasURI ||
+ gBrowser.currentURI;
// Strip off usernames and passwords for the location bar
try {
uri = Services.uriFixup.createExposableURI(uri);
@@ -5897,11 +5901,24 @@ var XULBrowserWindow = {
this.reloadCommand.removeAttribute("disabled");
}
+ // The onion memorable alias needs to be used in URLBarSetURI, but also in
+ // other parts of the code (like the bookmarks UI), so we save it.
+ if (gBrowser.selectedBrowser.allowOnionUrlbarRewrites) {
+ 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
- URLBarSetURI(aLocationURI, aIsSimulated);
+ URLBarSetURI(
+ gBrowser.selectedBrowser.currentOnionAliasURI || aLocationURI,
+ aIsSimulated
+ );
BookmarkingUI.onLocationChange();
@@ -7627,6 +7644,21 @@ function handleLinkClick(event, href, linkNode) {
} catch (e) {}
}
+ // Check if the link needs to be opened with .tor.onion urlbar rewrites
+ // allowed. Only when the owner doc has allowOnionUrlbarRewrites = true
+ // and the same origin we should allow this.
+ let persistAllowOnionUrlbarRewritesInChildTab = false;
+ if (where == "tab" && gBrowser.docShell.allowOnionUrlbarRewrites) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ let tURI = makeURI(href);
+ let isPrivateWin =
+ doc.nodePrincipal.originAttributes.privateBrowsingId > 0;
+ sm.checkSameOriginURI(referrerURI, tURI, false, isPrivateWin);
+ persistAllowOnionUrlbarRewritesInChildTab = true;
+ } catch (e) {}
+ }
+
// first get document wide referrer policy, then
// get referrer attribute from clicked link and parse it and
// allow per element referrer to overrule the document wide referrer if enabled
@@ -7659,6 +7691,7 @@ function handleLinkClick(event, href, linkNode) {
triggeringPrincipal: doc.nodePrincipal,
csp,
frameOuterWindowID,
+ allowOnionUrlbarRewrites: persistAllowOnionUrlbarRewritesInChildTab,
};
// The new tab/window must use the same userContextId
diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js
index e7106e5e8fc1..55da2efb7782 100644
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -93,6 +93,7 @@ function openContextMenu(aMessage) {
disableSetDesktopBackground: data.disableSetDesktopBg,
loginFillInfo: data.loginFillInfo,
parentAllowsMixedContent: data.parentAllowsMixedContent,
+ parentAllowsOnionUrlbarRewrites: data.parentAllowsOnionUrlbarRewrites,
userContextId: data.userContextId,
webExtContextData: data.webExtContextData,
};
@@ -1122,9 +1123,26 @@ nsContextMenu.prototype = {
} catch (e) {}
}
+ // Check if the link needs to be opened with .tor.onion urlbar rewrites
+ // allowed. Only when parent has allowOnionUrlbarRewrites = true
+ // and the same origin we should allow this.
+ let persistAllowOnionUrlbarRewrites = false;
+
+ if (gContextMenuContentData.parentAllowsOnionUrlbarRewrites) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ let targetURI = this.linkURI;
+ let isPrivateWin =
+ this.browser.contentPrincipal.originAttributes.privateBrowsingId > 0;
+ sm.checkSameOriginURI(referrerURI, targetURI, false, isPrivateWin);
+ persistAllowOnionUrlbarRewrites = true;
+ } catch (e) {}
+ }
+
let params = {
allowMixedContent: persistAllowMixedContentInChildTab,
userContextId: parseInt(event.target.getAttribute("data-usercontextid")),
+ allowOnionUrlbarRewrites: persistAllowOnionUrlbarRewrites,
};
openLinkIn(this.linkURL, "tab", this._openLinkInParameters(params));
diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js
index 4dd07ff14e7b..53c463fd3263 100644
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1589,6 +1589,7 @@
var aRelatedToCurrent;
var aAllowInheritPrincipal;
var aAllowMixedContent;
+ var aAllowOnionUrlbarRewrites;
var aSkipAnimation;
var aForceNotRemote;
var aPreferredRemoteType;
@@ -1618,6 +1619,7 @@
aRelatedToCurrent = params.relatedToCurrent;
aAllowInheritPrincipal = !!params.allowInheritPrincipal;
aAllowMixedContent = params.allowMixedContent;
+ aAllowOnionUrlbarRewrites = params.allowOnionUrlbarRewrites;
aSkipAnimation = params.skipAnimation;
aForceNotRemote = params.forceNotRemote;
aPreferredRemoteType = params.preferredRemoteType;
@@ -1658,6 +1660,7 @@
relatedToCurrent: aRelatedToCurrent,
skipAnimation: aSkipAnimation,
allowMixedContent: aAllowMixedContent,
+ allowOnionUrlbarRewrites: aAllowOnionUrlbarRewrites,
forceNotRemote: aForceNotRemote,
createLazyBrowser: aCreateLazyBrowser,
preferredRemoteType: aPreferredRemoteType,
@@ -2530,6 +2533,7 @@
{
allowInheritPrincipal,
allowMixedContent,
+ allowOnionUrlbarRewrites,
allowThirdPartyFixup,
bulkOrderedOpen,
charset,
@@ -2902,6 +2906,9 @@
if (allowMixedContent) {
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
}
+ if (allowOnionUrlbarRewrites) {
+ 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 9a1010b2afa9..cc46b14561b4 100644
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -370,6 +370,7 @@ function openLinkIn(url, where, params) {
var aRelatedToCurrent = params.relatedToCurrent;
var aAllowInheritPrincipal = !!params.allowInheritPrincipal;
var aAllowMixedContent = params.allowMixedContent;
+ var aAllowOnionUrlbarRewrites = params.allowOnionUrlbarRewrites;
var aForceAllowDataURI = params.forceAllowDataURI;
var aInBackground = params.inBackground;
var aInitiatingDoc = params.initiatingDoc;
@@ -680,6 +681,7 @@ function openLinkIn(url, where, params) {
relatedToCurrent: aRelatedToCurrent,
skipAnimation: aSkipTabAnimation,
allowMixedContent: aAllowMixedContent,
+ allowOnionUrlbarRewrites: aAllowOnionUrlbarRewrites,
userContextId: aUserContextId,
originPrincipal: aPrincipal,
triggeringPrincipal: aTriggeringPrincipal,
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index 4e851289fbe9..628c6f5db151 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -494,6 +494,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ShellService: "resource:///modules/ShellService.jsm",
TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
TabUnloader: "resource:///modules/TabUnloader.jsm",
+ OnionAliasStore: "resource:///modules/OnionAliasStore.jsm",
UIState: "resource://services-sync/UIState.jsm",
UITour: "resource:///modules/UITour.jsm",
WebChannel: "resource://gre/modules/WebChannel.jsm",
@@ -1839,6 +1840,7 @@ BrowserGlue.prototype = {
Normandy.uninit();
RFPHelper.uninit();
+ OnionAliasStore.uninit();
},
// Set up a listener to enable/disable the screenshots extension
@@ -2109,6 +2111,10 @@ BrowserGlue.prototype = {
RFPHelper.init();
});
+ Services.tm.idleDispatchToMainThread(() => {
+ OnionAliasStore.init();
+ });
+
ChromeUtils.idleDispatch(() => {
Blocklist.loadBlocklistAsync();
});
diff --git a/browser/components/onionservices/ExtensionMessaging.jsm b/browser/components/onionservices/ExtensionMessaging.jsm
new file mode 100644
index 000000000000..b5d69df93807
--- /dev/null
+++ b/browser/components/onionservices/ExtensionMessaging.jsm
@@ -0,0 +1,86 @@
+// 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"
+);
+
+class ExtensionMessaging {
+ constructor() {
+ this._callback = null;
+ this._handlers = new Map();
+ this._messageManager = Services.cpmm;
+ }
+
+ async sendMessage(msg, extensionId) {
+ this._init();
+
+ const addon = await AddonManager.getAddonByID(extensionId);
+ if (!addon) {
+ throw new Error(`extension '${extensionId} does not exist`);
+ }
+ await addon.startupPromise;
+
+ const channelId = ExtensionUtils.getUniqueId();
+ return new Promise((resolve, reject) => {
+ this._handlers.set(channelId, { resolve, reject });
+ this._messageManager.sendAsyncMessage("MessageChannel:Messages", [
+ {
+ messageName: "Extension:Message",
+ sender: {
+ id: extensionId,
+ extensionId,
+ },
+ recipient: { extensionId },
+ data: new StructuredCloneHolder(msg),
+ channelId,
+ responseType: MessageChannel.RESPONSE_FIRST,
+ },
+ ]);
+ });
+ }
+
+ 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..87954461bab1
--- /dev/null
+++ b/browser/components/onionservices/HttpsEverywhereControl.jsm
@@ -0,0 +1,119 @@
+// 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 at eff.org";
+const SECUREDROP_TOR_ONION_CHANNEL = {
+ name: "SecureDropTorOnion",
+ jwk: {
+ kty: "RSA",
+ e: "AQAB",
+ n:
+ "y0iWTVev1uYDVhLdc5uSHWke-9JlbxzqIsGkS95Pk5NsxdlkdbHpqaPr-5xL5FspX8QGo3HAT5hrUcPV_kz8x-HwGEm2-p9BQ6-yEPtr5QXQGGzNoizmj7HH-b0y5qx8iUFwAJ__PJWK4IwSgjIQqHMjmkOLc9N4bmRPULi6ZZMb-97FdeZsh34dVy2tpIzZaijRfRQSfeZkwRXZzOY-siGfOAzY_UcrHFli5zroTZAyDaetFm1z2-TdFLOvN8fi0o3mBbCB9SqhOUImPwSTNWTp2D6bHhI91mt7gr6fLnzHMGrTMh2DQ4vjt_98pe7WTUzuRCLa9Awb6JJgbYA4ySV1akAU0_iq6oCAE9PZbUgUw9UAH1ctRFml87F3HORUMMj5ZCLwRIrEXqrCJbV4f-Ius-ZO2wwlYTsEv8TmUzISpMwVjGOIpXwFIb65R_EX3_vIopauSoyZkvk3klly0Qe6KTy_gg1CZ_h2ZXPpLMwqlfFTDBPv2q8gyuzgkYXQes3FX-PbJ9Dsl5QO4icuEjV2NQ7iPwwIPAtj9cpqCD8-p9TqTKaZjOFC9-ryJpWsivGCbvN2JotmJ5Ax9rmnAMvQM09muCetFj_ZIgllcpeahaw6gxVXlSYIhb0J9V878KNuRJ2yPJFlBmgpFexvCcz8Jqs6JUfIrmUAGUXG9nE",
+ },
+ update_path_prefix: "https://redshiftzero.github.io/securedrop-httpse/",
+ 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 {
+ // 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_ruleset_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..7e006e59490b
--- /dev/null
+++ b/browser/components/onionservices/OnionAliasStore.jsm
@@ -0,0 +1,199 @@
+// 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;
+ }
+
+ 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();
+
+ // Install update channel
+ await this.httpsEverywhereControl.installTorOnionUpdateChannel();
+
+ // 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.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, using http observer as fallback");
+ }
+
+ // Setup checker for https-everywhere ruleset updates
+ 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 7e103239c8d6..e4b6d73f8f40 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 131d2ecc8ca8..8fd87bb8b026 100644
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -1022,7 +1022,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.allowOnionUrlbarRewrites
+ ) {
uri = this.window.gBrowser.currentURI;
} else {
// The value could be:
diff --git a/browser/modules/ContentClick.jsm b/browser/modules/ContentClick.jsm
index 767c67880995..872b06488c1a 100644
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -88,6 +88,7 @@ var ContentClick = {
charset: browser.characterSet,
referrerInfo: E10SUtils.deserializeReferrerInfo(json.referrerInfo),
allowMixedContent: json.allowMixedContent,
+ allowOnionUrlbarRewrites: json.allowOnionUrlbarRewrites,
isContentWindowPrivate: json.isContentWindowPrivate,
originPrincipal: json.originPrincipal,
triggeringPrincipal: json.triggeringPrincipal,
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index cc329a50c109..80678a04e95c 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6543,6 +6543,18 @@ void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
return;
}
+ if (!mAllowOnionUrlbarRewrites) {
+ nsAutoCString oldHost;
+ nsAutoCString newHost;
+ if (NS_SUCCEEDED(oldURI->GetHost(oldHost)) &&
+ StringEndsWith(oldHost, NS_LITERAL_CSTRING(".tor.onion")) &&
+ NS_SUCCEEDED(newURI->GetHost(newHost)) &&
+ StringEndsWith(newHost, NS_LITERAL_CSTRING(".onion")) &&
+ !StringEndsWith(newHost, NS_LITERAL_CSTRING(".tor.onion"))) {
+ mAllowOnionUrlbarRewrites = true;
+ }
+ }
+
// Below a URI visit is saved (see AddURIVisit method doc).
// The visit chain looks something like:
// ...
@@ -9744,6 +9756,10 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
// We're making history navigation or a reload. Make sure our history ID
// points to the same ID as SHEntry's docshell ID.
mHistoryID = aLoadState->SHEntry()->DocshellID();
+ // Loading from session history may not call DoURILoad(), so we set this
+ // flag here.
+ mAllowOnionUrlbarRewrites = aLoadState->HasLoadFlags(
+ INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES);
}
}
@@ -10386,6 +10402,13 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
}
}
+ mAllowOnionUrlbarRewrites =
+ aLoadState->HasLoadFlags(
+ INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES) ||
+ (mAllowOnionUrlbarRewrites && GetCurrentDocChannel() &&
+ NS_SUCCEEDED(
+ nsContentUtils::CheckSameOrigin(GetCurrentDocChannel(), channel)));
+
// hack
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
@@ -11733,6 +11756,7 @@ nsresult nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel,
triggeringPrincipal, // Channel or provided principal
principalToInherit, csp, mHistoryID, mDynamicallyCreated);
+ entry->SetAllowOnionUrlbarRewrites(mAllowOnionUrlbarRewrites);
entry->SetOriginalURI(originalURI);
entry->SetResultPrincipalURI(resultPrincipalURI);
entry->SetLoadReplace(loadReplace);
@@ -11913,6 +11937,10 @@ nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) {
srcdoc = VoidString();
}
+ if (aEntry->GetAllowOnionUrlbarRewrites()) {
+ flags |= INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES;
+ }
+
// If there is no valid triggeringPrincipal, we deny the load
MOZ_ASSERT(triggeringPrincipal,
"need a valid triggeringPrincipal to load from history");
@@ -13817,3 +13845,12 @@ nsDocShell::SetWatchedByDevtools(bool aWatched) {
mWatchedByDevtools = aWatched;
return NS_OK;
}
+
+NS_IMETHODIMP
+nsDocShell::GetAllowOnionUrlbarRewrites(bool* aAllowOnionUrlbarRewrites) {
+ NS_ENSURE_ARG(aAllowOnionUrlbarRewrites);
+ *aAllowOnionUrlbarRewrites =
+ StaticPrefs::browser_urlbar_onionRewrites_enabled() &&
+ mAllowOnionUrlbarRewrites;
+ return NS_OK;
+}
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index 4a81946f7bf4..7de079c0903e 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -153,6 +153,9 @@ class nsDocShell final : public nsDocLoader,
// Whether the load was triggered by user interaction.
INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED = 0x1000,
+
+ // Whether rewriting the urlbar to a short .onion alias is allowed.
+ INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES = 0x2000,
};
// Event type dispatched by RestorePresentation
@@ -1235,6 +1238,7 @@ class nsDocShell final : public nsDocLoader,
bool mCSSErrorReportingEnabled : 1;
bool mAllowAuth : 1;
bool mAllowKeywordFixup : 1;
+ bool mAllowOnionUrlbarRewrites : 1;
bool mIsOffScreenBrowser : 1;
bool mIsActive : 1;
bool mDisableMetaRefreshWhenInactive : 1;
diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp
index d2e21edceeda..80143b49f3fa 100644
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -417,6 +417,10 @@ void nsDocShellLoadState::CalculateLoadURIFlags() {
mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
}
+ if (oldLoadFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES) {
+ mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES;
+ }
+
if (oldLoadFlags & nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) {
mLoadFlags |= nsDocShell::INTERNAL_LOAD_FLAGS_FIRST_LOAD;
}
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
index 542232892c5b..cc0013e80c10 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -1195,4 +1195,9 @@ interface nsIDocShell : nsIDocShellTreeItem
* Whether developer tools are watching activity in this docshell.
*/
[infallible] attribute boolean watchedByDevtools;
+
+ /**
+ * Whether rewriting the urlbar to a short .onion alias is allowed.
+ */
+ [infallible] readonly attribute boolean allowOnionUrlbarRewrites;
};
diff --git a/docshell/base/nsIWebNavigation.idl b/docshell/base/nsIWebNavigation.idl
index cabae3f31d0f..123129d2c259 100644
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -227,6 +227,11 @@ interface nsIWebNavigation : nsISupports
const unsigned long LOAD_FLAGS_IS_REDIRECT = 0x800000;
/**
+ * Allow rewriting the urlbar to a short .onion alias.
+ */
+ const unsigned long LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES = 0x1000000;
+
+ /**
* 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
* however, the URI dispatcher will go through its normal process of content
diff --git a/docshell/shistory/nsISHEntry.idl b/docshell/shistory/nsISHEntry.idl
index 12dbf8172262..f5863b532a28 100644
--- a/docshell/shistory/nsISHEntry.idl
+++ b/docshell/shistory/nsISHEntry.idl
@@ -230,6 +230,11 @@ interface nsISHEntry : nsISupports
[infallible] attribute boolean persist;
/**
+ * Whether rewriting the urlbar to a short .onion alias is allowed.
+ */
+ [infallible] attribute boolean allowOnionUrlbarRewrites;
+
+ /**
* 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 e39b5888c157..27f21a23d2ba 100644
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -42,7 +42,8 @@ nsSHEntry::nsSHEntry()
mIsSrcdocEntry(false),
mScrollRestorationIsManual(false),
mLoadedInThisProcess(false),
- mPersist(true) {}
+ mPersist(true),
+ mAllowOnionUrlbarRewrites(false) {}
nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
: mShared(aOther.mShared),
@@ -68,7 +69,8 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
mIsSrcdocEntry(aOther.mIsSrcdocEntry),
mScrollRestorationIsManual(false),
mLoadedInThisProcess(aOther.mLoadedInThisProcess),
- mPersist(aOther.mPersist) {}
+ mPersist(aOther.mPersist),
+ mAllowOnionUrlbarRewrites(aOther.mAllowOnionUrlbarRewrites) {}
nsSHEntry::~nsSHEntry() {
// Null out the mParent pointers on all our kids.
@@ -855,3 +857,15 @@ nsSHEntry::SetPersist(bool aPersist) {
mPersist = aPersist;
return NS_OK;
}
+
+NS_IMETHODIMP
+nsSHEntry::GetAllowOnionUrlbarRewrites(bool* aAllowOnionUrlbarRewrites) {
+ *aAllowOnionUrlbarRewrites = mAllowOnionUrlbarRewrites;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetAllowOnionUrlbarRewrites(bool aAllowOnionUrlbarRewrites) {
+ mAllowOnionUrlbarRewrites = aAllowOnionUrlbarRewrites;
+ return NS_OK;
+}
diff --git a/docshell/shistory/nsSHEntry.h b/docshell/shistory/nsSHEntry.h
index 4a9d7e466b22..aeb42b307739 100644
--- a/docshell/shistory/nsSHEntry.h
+++ b/docshell/shistory/nsSHEntry.h
@@ -61,6 +61,7 @@ class nsSHEntry final : public nsISHEntry {
bool mScrollRestorationIsManual;
bool mLoadedInThisProcess;
bool mPersist;
+ bool mAllowOnionUrlbarRewrites;
};
#endif /* nsSHEntry_h */
diff --git a/modules/libpref/init/StaticPrefList.h b/modules/libpref/init/StaticPrefList.h
index 47f4626f7e6b..29732ac3e884 100644
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -2650,6 +2650,13 @@ VARCACHE_PREF(
bool, false
)
+// Whether rewriting the urlbar to a short .onion alias is allowed.
+VARCACHE_PREF(
+ "browser.urlbar.onionRewrites.enabled",
+ browser_urlbar_onionRewrites_enabled,
+ bool, true
+)
+
//---------------------------------------------------------------------------
// ChannelClassifier prefs
//---------------------------------------------------------------------------
diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat
index 70e198aa3712..0c53819d860d 100644
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -5495,6 +5495,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/toolkit/components/remotebrowserutils/RemoteWebNavigation.jsm b/toolkit/components/remotebrowserutils/RemoteWebNavigation.jsm
index 6d971c10180a..76ce03dc6897 100644
--- a/toolkit/components/remotebrowserutils/RemoteWebNavigation.jsm
+++ b/toolkit/components/remotebrowserutils/RemoteWebNavigation.jsm
@@ -60,6 +60,7 @@ RemoteWebNavigation.prototype = {
LOAD_FLAGS_ALLOW_POPUPS: 32768,
LOAD_FLAGS_BYPASS_CLASSIFIER: 65536,
LOAD_FLAGS_FORCE_ALLOW_COOKIES: 131072,
+ LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES: 16777216,
STOP_NETWORK: 1,
STOP_CONTENT: 2,
diff --git a/toolkit/content/widgets/browser-custom-element.js b/toolkit/content/widgets/browser-custom-element.js
index f31058012cbb..d26c8f8f0d1d 100644
--- a/toolkit/content/widgets/browser-custom-element.js
+++ b/toolkit/content/widgets/browser-custom-element.js
@@ -283,6 +283,8 @@
this._mayEnableCharacterEncodingMenu = null;
+ this._allowOnionUrlbarRewrites = false;
+
this._contentPrincipal = null;
this._csp = null;
@@ -677,6 +679,12 @@
: this.docShell.mayEnableCharacterEncodingMenu;
}
+ get allowOnionUrlbarRewrites() {
+ return this.isRemoteBrowser
+ ? this._allowOnionUrlbarRewrites
+ : this.docShell.allowOnionUrlbarRewrites;
+ }
+
get contentPrincipal() {
return this.isRemoteBrowser
? this._contentPrincipal
@@ -1870,6 +1878,7 @@
"_textZoom",
"_isSyntheticDocument",
"_innerWindowID",
+ "_allowOnionUrlbarRewrites",
]
);
}
diff --git a/toolkit/modules/RemoteWebProgress.jsm b/toolkit/modules/RemoteWebProgress.jsm
index 0f3c53a17a8c..558c31515570 100644
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -281,6 +281,8 @@ class RemoteWebProgressManager {
this._browser._isSyntheticDocument = json.synthetic;
this._browser._innerWindowID = json.innerWindowID;
this._browser._contentRequestContextID = json.requestContextID;
+ this._browser._allowOnionUrlbarRewrites =
+ json.allowOnionUrlbarRewrites;
}
this.onLocationChange(webProgress, request, location, flags);
diff --git a/toolkit/modules/WebProgressChild.jsm b/toolkit/modules/WebProgressChild.jsm
index 60a1aa88e8ef..26250e361bd4 100644
--- a/toolkit/modules/WebProgressChild.jsm
+++ b/toolkit/modules/WebProgressChild.jsm
@@ -167,6 +167,7 @@ class WebProgressChild {
json.requestContextID = this.mm.content.document.documentLoadGroup
? this.mm.content.document.documentLoadGroup.requestContextID
: null;
+ json.allowOnionUrlbarRewrites = this.mm.docShell.allowOnionUrlbarRewrites;
if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
let uri = aLocationURI;
diff --git a/toolkit/modules/sessionstore/SessionHistory.jsm b/toolkit/modules/sessionstore/SessionHistory.jsm
index e54ab3369cd8..463f147a9ca4 100644
--- a/toolkit/modules/sessionstore/SessionHistory.jsm
+++ b/toolkit/modules/sessionstore/SessionHistory.jsm
@@ -287,6 +287,7 @@ var SessionHistoryInternal = {
}
entry.persist = shEntry.persist;
+ entry.allowOnionUrlbarRewrites = shEntry.allowOnionUrlbarRewrites;
return entry;
},
@@ -559,6 +560,10 @@ var SessionHistoryInternal = {
}
}
+ if (entry.allowOnionUrlbarRewrites) {
+ shEntry.allowOnionUrlbarRewrites = entry.allowOnionUrlbarRewrites;
+ }
+
return shEntry;
},
More information about the tbb-commits
mailing list