lists.torproject.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

tbb-commits

Thread Start a new thread
Download
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
tbb-commits@lists.torproject.org

March 2021

  • 4 participants
  • 345 discussions
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 1650281 - P1: Widen `gCombinedSizes` once the buffers grow r=gerald
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 701d7cd66cc1ba2b4cc3c2b6633ff34f3c6cdf2f Author: Chun-Min Chang <chun.m.chang(a)gmail.com> Date: Tue Jul 21 23:38:57 2020 +0000 Bug 1650281 - P1: Widen `gCombinedSizes` once the buffers grow r=gerald The `gCombinedSizes` need to be enlarged once the inner buffer within `MemoryBlockCache` grows. Otherwise, when the `MemoryBlockCache` is released, subtracting the buffer-size of the `MemoryBlockCache` from `gCombinedSizes` lead to a underflow. Differential Revision: https://phabricator.services.mozilla.com/D84273 --- dom/media/MemoryBlockCache.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dom/media/MemoryBlockCache.cpp b/dom/media/MemoryBlockCache.cpp index 2d31119dca0a..bf073e6769d0 100644 --- a/dom/media/MemoryBlockCache.cpp +++ b/dom/media/MemoryBlockCache.cpp @@ -114,6 +114,10 @@ bool MemoryBlockCache::EnsureBufferCanContain(size_t aContentLength) { // possibly bypass some future growths that would fit in this new capacity. mBuffer.SetLength(capacity); } + const size_t newSizes = gCombinedSizes += (extra + extraCapacity); + LOG("EnsureBufferCanContain(%zu) - buffer size %zu + requested %zu + bonus " + "%zu = %zu; combined sizes %zu", + aContentLength, initialLength, extra, extraCapacity, capacity, newSizes); mHasGrown = true; return true; }
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 40025: Remove Mozilla add-on install permissions
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit fcbd144593d2f6a31c6a8a257b2e70b5931eddf8 Author: Alex Catarineu <acat(a)torproject.org> Date: Mon Jul 27 18:12:55 2020 +0200 Bug 40025: Remove Mozilla add-on install permissions --- browser/app/permissions | 6 ------ 1 file changed, 6 deletions(-) diff --git a/browser/app/permissions b/browser/app/permissions index da6b6728043d..5c4c302f5ba5 100644 --- a/browser/app/permissions +++ b/browser/app/permissions @@ -11,12 +11,6 @@ origin uitour 1 https://3g2upl4pq6kufc4m.onion origin uitour 1 about:tor -# XPInstall -origin install 1 https://addons.mozilla.org - # Remote troubleshooting origin remote-troubleshooting 1 https://support.mozilla.org -# addon install -origin install 1 https://private-network.firefox.com -origin install 1 https://fpn.firefox.com
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 1650281 - P2: Make sure `gCombinedSizes` won't be underflowed r=gerald
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 41374666825e9905d2a6399031d7a600b8e7e438 Author: Chun-Min Chang <chun.m.chang(a)gmail.com> Date: Tue Jul 21 23:39:14 2020 +0000 Bug 1650281 - P2: Make sure `gCombinedSizes` won't be underflowed r=gerald In any case, `gCombinedSizes` should be larger than or equal to the buffer within `MemoryClockCache`. Differential Revision: https://phabricator.services.mozilla.com/D84274 --- dom/media/MemoryBlockCache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/media/MemoryBlockCache.cpp b/dom/media/MemoryBlockCache.cpp index bf073e6769d0..2848a3f3812c 100644 --- a/dom/media/MemoryBlockCache.cpp +++ b/dom/media/MemoryBlockCache.cpp @@ -52,6 +52,7 @@ MemoryBlockCache::MemoryBlockCache(int64_t aContentLength) } MemoryBlockCache::~MemoryBlockCache() { + MOZ_ASSERT(gCombinedSizes >= mBuffer.Length()); size_t sizes = static_cast<size_t>(gCombinedSizes -= mBuffer.Length()); LOG("~MemoryBlockCache() - destroying buffer of size %zu; combined sizes now " "%zu",
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 33852: Clean up about:logins (LockWise) to avoid mentioning sync, etc.
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 354ede1b0c9eb34ae22a4d630ca7dea8de83890b 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 a3b47f8f8527..5d0a0e4e83b9 100644 --- a/browser/components/aboutlogins/AboutLoginsParent.jsm +++ b/browser/components/aboutlogins/AboutLoginsParent.jsm @@ -62,6 +62,7 @@ const PASSWORD_SYNC_NOTIFICATION_ID = "enable-password-sync"; const HIDE_MOBILE_FOOTER_PREF = "signon.management.page.hideMobileFooter"; const SHOW_PASSWORD_SYNC_NOTIFICATION_PREF = "signon.management.page.showPasswordSyncNotification"; +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. @@ -431,6 +432,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 7ed29bda8297..dca63da2e649 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 da7d9016a2eb..361b2b0d02bf 100644 --- a/browser/components/aboutlogins/content/aboutLogins.js +++ b/browser/components/aboutlogins/content/aboutLogins.js @@ -19,6 +19,9 @@ const gElements = { get loginFooter() { return this.loginItem.shadowRoot.querySelector("login-footer"); }, + get createNewLoginButton() { + return this.loginList.shadowRoot.querySelector(".create-login-button"); + }, }; let numberOfLogins = 0; @@ -100,6 +103,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 aefda548c84d..a02707980158 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 3c93d409b2c7..2d7380b2ea37 100644 --- a/browser/components/aboutlogins/content/components/menu-button.css +++ b/browser/components/aboutlogins/content/components/menu-button.css @@ -85,3 +85,13 @@ .menuitem-mobile-android { background-image: url("chrome://browser/skin/logo-android.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
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 1658881 - When failing to create a channel and an image request, make sure to set the image blocking status appropriately. r=tnikkel
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 4e82e7b9aa35672b47822eb86f52f78cdd042503 Author: Emilio Cobos Álvarez <emilio(a)crisal.io> Date: Wed Sep 9 22:58:29 2020 +0000 Bug 1658881 - When failing to create a channel and an image request, make sure to set the image blocking status appropriately. r=tnikkel This is the same status as we do for known no-data protocols here: https://searchfox.org/mozilla-central/rev/ac142717cc067d875e83e4b1316f004f6… This ensures we treat these two cases the same. Differential Revision: https://phabricator.services.mozilla.com/D89382 --- dom/base/nsImageLoadingContent.cpp | 7 ++++++- layout/reftests/image/reftest.list | 1 + layout/reftests/image/unknown-protocol-ref.html | 1 + layout/reftests/image/unknown-protocol.html | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dom/base/nsImageLoadingContent.cpp b/dom/base/nsImageLoadingContent.cpp index 23b1fd791c1f..85de63bef02d 100644 --- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -1207,7 +1207,12 @@ nsresult nsImageLoadingContent::LoadImage(nsIURI* aNewURI, bool aForce, MOZ_ASSERT(!req, "Shouldn't have non-null request here"); // If we don't have a current URI, we might as well store this URI so people // know what we tried (and failed) to load. - if (!mCurrentRequest) mCurrentURI = aNewURI; + if (!mCurrentRequest) { + mCurrentURI = aNewURI; + if (mImageBlockingStatus == nsIContentPolicy::ACCEPT) { + mImageBlockingStatus = nsIContentPolicy::REJECT_REQUEST; + } + } FireEvent(NS_LITERAL_STRING("error")); FireEvent(NS_LITERAL_STRING("loadend")); diff --git a/layout/reftests/image/reftest.list b/layout/reftests/image/reftest.list index a8a91c13ed3a..3c561fe3a7c8 100644 --- a/layout/reftests/image/reftest.list +++ b/layout/reftests/image/reftest.list @@ -69,3 +69,4 @@ random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == image-srcset-basic-selec pref(dom.image-lazy-loading.enabled,true) == moz-broken-matching-lazy-load.html moz-broken-matching-1-ref.html == img-invalidation-local-transform-1.html img-invalidation-local-transform-1-ref.html +== unknown-protocol.html unknown-protocol-ref.html diff --git a/layout/reftests/image/unknown-protocol-ref.html b/layout/reftests/image/unknown-protocol-ref.html new file mode 100644 index 000000000000..b5bb326eef57 --- /dev/null +++ b/layout/reftests/image/unknown-protocol-ref.html @@ -0,0 +1 @@ +<img src="mailto://foo"> diff --git a/layout/reftests/image/unknown-protocol.html b/layout/reftests/image/unknown-protocol.html new file mode 100644 index 000000000000..ef06881b7bcb --- /dev/null +++ b/layout/reftests/image/unknown-protocol.html @@ -0,0 +1 @@ +<img src="foobar://baz">
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 40091: Load HTTPS Everywhere as a builtin addon in desktop
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 7a3323a72d31a591f2fbfaad47137b79300488fd Author: Alex Catarineu <acat(a)torproject.org> Date: Fri Sep 4 12:34:35 2020 +0200 Bug 40091: Load HTTPS Everywhere as a builtin addon in desktop This loads HTTPS Everywhere as a builtin addon from a hardcoded resource:// URI in desktop. It also ensures that the non-builtin HTTPS Everywhere addon is always uninstalled on browser startup. The reason of making this desktop-only is that there are some issues when installing a builtin extension from geckoview side, making the extension not available on first startup. So, at least for now we handle the Fenix case separately. See #40118 for a followup for investigating these. --- browser/components/BrowserGlue.jsm | 37 ++++++++++++++++++++++ .../mozapps/extensions/internal/XPIProvider.jsm | 13 ++++++++ 2 files changed, 50 insertions(+) diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index ec38d0ca8b33..057a2121533c 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -56,6 +56,13 @@ XPCOMUtils.defineLazyServiceGetter( "nsIPushService" ); +XPCOMUtils.defineLazyServiceGetters(this, { + resProto: [ + "@mozilla.org/network/protocol;1?name=resource", + "nsISubstitutingProtocolHandler", + ], +}); + const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state"; /** @@ -675,6 +682,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { "resource://gre/modules/ContextualIdentityService.jsm", Corroborate: "resource://gre/modules/Corroborate.jsm", Discovery: "resource:///modules/Discovery.jsm", + ExtensionData: "resource://gre/modules/Extension.jsm", ExtensionsUI: "resource:///modules/ExtensionsUI.jsm", FirefoxMonitor: "resource:///modules/FirefoxMonitor.jsm", FxAccounts: "resource://gre/modules/FxAccounts.jsm", @@ -1330,6 +1338,35 @@ BrowserGlue.prototype = { "resource:///modules/themes/dark/" ); + // Install https-everywhere builtin addon if needed. + (async () => { + const HTTPS_EVERYWHERE_ID = "https-everywhere-eff(a)eff.org"; + const HTTPS_EVERYWHERE_BUILTIN_URL = + "resource://torbutton/content/extensions/https-everywhere/"; + // This does something similar as GeckoViewWebExtension.jsm: it tries + // to load the manifest to retrieve the version of the builtin and + // compares it to the currently installed one to see whether we need + // to install or not. Here we delegate that to + // AddonManager.maybeInstallBuiltinAddon. + try { + const resolvedURI = Services.io.newURI( + resProto.resolveURI(Services.io.newURI(HTTPS_EVERYWHERE_BUILTIN_URL)) + ); + const extensionData = new ExtensionData(resolvedURI); + const manifest = await extensionData.loadManifest(); + + await AddonManager.maybeInstallBuiltinAddon( + HTTPS_EVERYWHERE_ID, + manifest.version, + HTTPS_EVERYWHERE_BUILTIN_URL + ); + } catch (e) { + const log = Log.repository.getLogger("HttpsEverywhereBuiltinLoader"); + log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter())); + log.error("Could not install https-everywhere extension", e); + } + })(); + if (AppConstants.MOZ_NORMANDY) { Normandy.init(); } diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 794c206fb453..dc5362bce3d8 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -1491,6 +1491,19 @@ var XPIStates = { continue; } + // Uninstall HTTPS Everywhere if it is installed in the user profile. + if ( + id === "https-everywhere-eff(a)eff.org" && + loc.name === KEY_APP_PROFILE + ) { + logger.debug( + "Uninstalling the HTTPS Everywhere extension from user profile." + ); + loc.installer.uninstallAddon(id); + changed = true; + continue; + } + let xpiState = loc.get(id); if (!xpiState) { // If the location is not supported for sideloading, skip new
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 28005: Implement .onion alias urlbar rewrites
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 39597ebdb8d9d2e022fa1ade89224fab3f306289 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 --- 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 | 86 +++++++++ .../onionservices/HttpsEverywhereControl.jsm | 119 ++++++++++++ .../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 + 37 files changed, 729 insertions(+), 22 deletions(-) diff --git a/browser/actors/ClickHandlerChild.jsm b/browser/actors/ClickHandlerChild.jsm index 7dd060e83061..206a8fc97a4d 100644 --- a/browser/actors/ClickHandlerChild.jsm +++ b/browser/actors/ClickHandlerChild.jsm @@ -138,6 +138,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 454c0fe69b27..42ab7a0f6e2a 100644 --- a/browser/actors/ClickHandlerParent.jsm +++ b/browser/actors/ClickHandlerParent.jsm @@ -102,6 +102,7 @@ class ClickHandlerParent extends JSWindowActorParent { charset: browser.characterSet, referrerInfo: E10SUtils.deserializeReferrerInfo(data.referrerInfo), allowMixedContent: data.allowMixedContent, + 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 16a3f8ecbc81..1a1b38aa6375 100644 --- a/browser/actors/ContextMenuChild.jsm +++ b/browser/actors/ContextMenuChild.jsm @@ -575,6 +575,9 @@ class ContextMenuChild extends JSWindowActorChild { // The same-origin check will be done in nsContextMenu.openLinkInTab. let parentAllowsMixedContent = !!this.docShell.mixedContentChannel; + let parentAllowsOnionUrlbarRewrites = this.docShell + .onionUrlbarRewritesAllowed; + let disableSetDesktopBackground = null; // Media related cache info parent needs for saving @@ -687,6 +690,7 @@ class ContextMenuChild extends JSWindowActorChild { frameBrowsingContextID, disableSetDesktopBackground, parentAllowsMixedContent, + parentAllowsOnionUrlbarRewrites, }; if (context.inFrame && !context.inSrcdocFrame) { diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index d4b71f87da04..9a9bb3e0dad4 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -452,7 +452,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; @@ -556,7 +557,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)) { @@ -1655,14 +1656,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 b8172d6a85f6..b7df1ebaa187 100644 --- a/browser/base/content/browser-siteIdentity.js +++ b/browser/base/content/browser-siteIdentity.js @@ -474,13 +474,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; @@ -566,17 +566,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; } }, @@ -1010,8 +1011,9 @@ var gIdentityHandler = { this.updateSitePermissions(); }, - setURI(uri) { + setURI(uri, onionAliasURI) { 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 547e49df6e1a..70d3f43bf0ef 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -77,6 +77,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", @@ -2325,6 +2326,7 @@ var gBrowserInit = { // [9]: allowInheritPrincipal (bool) // [10]: csp (nsIContentSecurityPolicy) // [11]: nsOpenWindowInfo + // [12]: onionUrlbarRewritesAllowed (bool) let userContextId = window.arguments[5] != undefined ? window.arguments[5] @@ -2344,7 +2346,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 { @@ -3230,7 +3233,8 @@ function loadURI( forceAboutBlankViewerInCurrent, triggeringPrincipal, allowInheritPrincipal = false, - csp = null + csp = null, + onionUrlbarRewritesAllowed = false ) { if (!triggeringPrincipal) { throw new Error("Must load with a triggering Principal"); @@ -3248,6 +3252,7 @@ function loadURI( csp, forceAboutBlankViewerInCurrent, allowInheritPrincipal, + onionUrlbarRewritesAllowed, }); } catch (e) { Cu.reportError(e); @@ -5359,11 +5364,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(); @@ -5534,6 +5552,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; if (this._state == aState && this._lastLocation == spec) { // Switching to a tab of the same URL doesn't change most security @@ -5551,7 +5570,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 @@ -7011,6 +7030,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 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 frameOuterWindowID = WebNavigationFrames.getFrameId(doc.defaultView); urlSecurityCheck(href, doc.nodePrincipal); @@ -7023,6 +7057,7 @@ function handleLinkClick(event, href, linkNode) { triggeringPrincipal: doc.nodePrincipal, csp: doc.csp, frameOuterWindowID, + 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 354b504ce0b4..ca351c71f1cb 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -57,6 +57,7 @@ function openContextMenu(aMessage, aBrowser, aActor) { disableSetDesktopBackground: data.disableSetDesktopBackground, loginFillInfo: data.loginFillInfo, parentAllowsMixedContent: data.parentAllowsMixedContent, + parentAllowsOnionUrlbarRewrites: data.parentAllowsOnionUrlbarRewrites, userContextId: data.userContextId, webExtContextData: data.webExtContextData, }; @@ -1059,6 +1060,7 @@ class nsContextMenu { triggeringPrincipal: this.principal, csp: this.csp, frameOuterWindowID: this.contentData.frameOuterWindowID, + onionUrlbarRewritesAllowed: false, }; for (let p in extra) { params[p] = extra[p]; @@ -1082,6 +1084,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 664dace84d9f..cd03cea3b6d9 100644 --- a/browser/base/content/pageinfo/pageInfo.js +++ b/browser/base/content/pageinfo/pageInfo.js @@ -390,7 +390,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 4331ebc4b219..29395e96ce57 100644 --- a/browser/base/content/pageinfo/security.js +++ b/browser/base/content/pageinfo/security.js @@ -250,7 +250,7 @@ var security = { }, }; -async function securityOnLoad(uri, windowInfo) { +async function securityOnLoad(uri, windowInfo, onionAliasURI) { await security.init(uri, windowInfo); let info = security.securityInfo; @@ -263,6 +263,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 8ce4a2710cb8..8c313eaaffcb 100644 --- a/browser/base/content/tabbrowser.js +++ b/browser/base/content/tabbrowser.js @@ -1547,6 +1547,7 @@ var aRelatedToCurrent; var aAllowInheritPrincipal; var aAllowMixedContent; + var aOnionUrlbarRewritesAllowed; var aSkipAnimation; var aForceNotRemote; var aPreferredRemoteType; @@ -1577,6 +1578,7 @@ aRelatedToCurrent = params.relatedToCurrent; aAllowInheritPrincipal = !!params.allowInheritPrincipal; aAllowMixedContent = params.allowMixedContent; + aOnionUrlbarRewritesAllowed = params.onionUrlbarRewritesAllowed; aSkipAnimation = params.skipAnimation; aForceNotRemote = params.forceNotRemote; aPreferredRemoteType = params.preferredRemoteType; @@ -1618,6 +1620,7 @@ relatedToCurrent: aRelatedToCurrent, skipAnimation: aSkipAnimation, allowMixedContent: aAllowMixedContent, + onionUrlbarRewritesAllowed: aOnionUrlbarRewritesAllowed, forceNotRemote: aForceNotRemote, createLazyBrowser: aCreateLazyBrowser, preferredRemoteType: aPreferredRemoteType, @@ -2504,6 +2507,7 @@ { allowInheritPrincipal, allowMixedContent, + onionUrlbarRewritesAllowed, allowThirdPartyFixup, bulkOrderedOpen, charset, @@ -2833,6 +2837,9 @@ if (allowMixedContent) { flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT; } + 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 a23d6f05e6a7..eb13d5a3435c 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -367,6 +367,7 @@ function openLinkIn(url, where, params) { var aRelatedToCurrent = params.relatedToCurrent; var aAllowInheritPrincipal = !!params.allowInheritPrincipal; var aAllowMixedContent = params.allowMixedContent; + var aOnionUrlbarRewritesAllowed = params.onionUrlbarRewritesAllowed; var aForceAllowDataURI = params.forceAllowDataURI; var aInBackground = params.inBackground; var aInitiatingDoc = params.initiatingDoc; @@ -482,6 +483,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); @@ -498,6 +504,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; @@ -614,6 +622,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 ( @@ -661,6 +672,7 @@ function openLinkIn(url, where, params) { relatedToCurrent: aRelatedToCurrent, skipAnimation: aSkipTabAnimation, allowMixedContent: aAllowMixedContent, + onionUrlbarRewritesAllowed: aOnionUrlbarRewritesAllowed, userContextId: aUserContextId, originPrincipal: aPrincipal, originStoragePrincipal: aStoragePrincipal, diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index 7d147d01d561..d30abff54562 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -703,6 +703,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm", TabUnloader: "resource:///modules/TabUnloader.jsm", TRRRacer: "resource:///modules/TRRPerformance.jsm", + OnionAliasStore: "resource:///modules/OnionAliasStore.jsm", UIState: "resource://services-sync/UIState.jsm", WebChannel: "resource://gre/modules/WebChannel.jsm", WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm", @@ -2046,6 +2047,7 @@ BrowserGlue.prototype = { Normandy.uninit(); RFPHelper.uninit(); + OnionAliasStore.uninit(); }, // Set up a listener to enable/disable the screenshots extension @@ -2412,6 +2414,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..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..60c3b5fca282 --- /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(a)eff.org"; +const SECUREDROP_TOR_ONION_CHANNEL = { + 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, +}; + +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..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 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 db1c497bcace..13b1279105f2 100644 --- a/browser/components/urlbar/UrlbarInput.jsm +++ b/browser/components/urlbar/UrlbarInput.jsm @@ -277,7 +277,10 @@ class UrlbarInput { // bar if the user has deleted the URL and we'd just put the same URL // back. See bug 304198. if (value === null) { - 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); @@ -1541,7 +1544,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 8731e44dd4eb..bf9639c82612 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -5657,6 +5657,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. @@ -8693,6 +8697,20 @@ nsresult nsDocShell::HandleSameDocumentNavigation( return NS_OK; } +/* static */ +bool nsDocShell::IsTorOnionRedirect(nsIURI* aOldURI, nsIURI* aNewURI) { + nsAutoCString oldHost; + nsAutoCString newHost; + if (aOldURI && aNewURI && NS_SUCCEEDED(aOldURI->GetHost(oldHost)) && + StringEndsWith(oldHost, NS_LITERAL_CSTRING(".tor.onion")) && + NS_SUCCEEDED(aNewURI->GetHost(newHost)) && + StringEndsWith(newHost, NS_LITERAL_CSTRING(".onion")) && + !StringEndsWith(newHost, NS_LITERAL_CSTRING(".tor.onion"))) { + return true; + } + return false; + } + nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, nsIDocShell** aDocShell, nsIRequest** aRequest) { @@ -8844,6 +8862,30 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, mAllowKeywordFixup = aLoadState->HasLoadFlags(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->HasLoadFlags(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 @@ -10996,6 +11038,7 @@ nsresult nsDocShell::AddToSessionHistory( mDynamicallyCreated, originalURI, resultPrincipalURI, loadReplace, referrerInfo, srcdoc, srcdocEntry, baseURI, saveLayoutState, expired); + entry->SetOnionUrlbarRewritesAllowed(mOnionUrlbarRewritesAllowed); if (root == static_cast<nsIDocShellTreeItem*>(this) && GetSessionHistory()) { bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel); @@ -12793,3 +12836,12 @@ bool nsDocShell::GetIsAttemptingToNavigate() { return false; } + +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 dcffed8e5537..d403a06f4c9f 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -152,6 +152,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 @@ -582,6 +585,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, @@ -1264,6 +1269,7 @@ class nsDocShell final : public nsDocLoader, bool mCSSErrorReportingEnabled : 1; bool mAllowAuth : 1; bool mAllowKeywordFixup : 1; + bool mOnionUrlbarRewritesAllowed : 1; bool mIsOffScreenBrowser : 1; bool mDisableMetaRefreshWhenInactive : 1; bool mIsAppTab : 1; diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp index d91bb6034f9d..fe1216bd2509 100644 --- a/docshell/base/nsDocShellLoadState.cpp +++ b/docshell/base/nsDocShellLoadState.cpp @@ -730,6 +730,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 01dc4734eb19..305acfd3a98f 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -1019,4 +1019,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 bbc3eb7583a2..a4cceb52b2a0 100644 --- a/docshell/base/nsIWebNavigation.idl +++ b/docshell/base/nsIWebNavigation.idl @@ -237,6 +237,11 @@ interface nsIWebNavigation : nsISupports */ const unsigned long LOAD_FLAGS_BYPASS_LOAD_URI_DELEGATE = 0x4000000; + /** + * Allow rewriting the urlbar to a short .onion alias. + */ + const unsigned long LOAD_FLAGS_ALLOW_ONION_URLBAR_REWRITES = 0x8000000; + /** * 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 07a28d1291c9..566b7d68fae5 100644 --- a/docshell/shistory/SessionHistoryEntry.cpp +++ b/docshell/shistory/SessionHistoryEntry.cpp @@ -490,6 +490,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 fa6108952688..c51682a65b0a 100644 --- a/docshell/shistory/SessionHistoryEntry.h +++ b/docshell/shistory/SessionHistoryEntry.h @@ -58,6 +58,7 @@ class SessionHistoryInfo { bool mIsSrcdocEntry = false; bool mScrollRestorationIsManual = false; bool mPersist = false; + bool mOnionUrlbarRewritesAllowed = false; }; // XXX Not sure that the id shouldn't just live in SessionHistoryInfo. diff --git a/docshell/shistory/nsISHEntry.idl b/docshell/shistory/nsISHEntry.idl index 8bbf2a68687c..bf410bfcc05a 100644 --- a/docshell/shistory/nsISHEntry.idl +++ b/docshell/shistory/nsISHEntry.idl @@ -242,6 +242,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 229b15eff25d..12d38f4c55fe 100644 --- a/docshell/shistory/nsSHEntry.cpp +++ b/docshell/shistory/nsSHEntry.cpp @@ -44,7 +44,8 @@ nsSHEntry::nsSHEntry(nsISHistory* aSHistory) mIsSrcdocEntry(false), mScrollRestorationIsManual(false), mLoadedInThisProcess(false), - mPersist(true) {} + mPersist(true), + mOnionUrlbarRewritesAllowed(false) {} nsSHEntry::nsSHEntry(const nsSHEntry& aOther) : mShared(aOther.mShared), @@ -70,7 +71,8 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther) mIsSrcdocEntry(aOther.mIsSrcdocEntry), mScrollRestorationIsManual(false), mLoadedInThisProcess(aOther.mLoadedInThisProcess), - mPersist(aOther.mPersist) {} + mPersist(aOther.mPersist), + mOnionUrlbarRewritesAllowed(aOther.mOnionUrlbarRewritesAllowed) {} nsSHEntry::~nsSHEntry() { // Null out the mParent pointers on all our kids. @@ -824,6 +826,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(); @@ -873,6 +887,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->SetLoadFlags(flags); diff --git a/docshell/shistory/nsSHEntry.h b/docshell/shistory/nsSHEntry.h index 351f6f200a33..6bc9de521437 100644 --- a/docshell/shistory/nsSHEntry.h +++ b/docshell/shistory/nsSHEntry.h @@ -63,6 +63,7 @@ class nsSHEntry : public nsISHEntry { bool mScrollRestorationIsManual; bool mLoadedInThisProcess; bool mPersist; + bool mOnionUrlbarRewritesAllowed; }; #endif /* nsSHEntry_h */ diff --git a/dom/interfaces/base/nsIBrowser.idl b/dom/interfaces/base/nsIBrowser.idl index 300b09e13824..a9ff00e964b7 100644 --- a/dom/interfaces/base/nsIBrowser.idl +++ b/dom/interfaces/base/nsIBrowser.idl @@ -158,7 +158,8 @@ interface nsIBrowser : nsISupports in uint64_t aInnerWindowID, in boolean aHasRequestContextID, in uint64_t aRequestContextID, - in AString aContentType); + in AString aContentType, + in boolean aOnionUrlbarRewritesAllowed); /** * Called by Gecko when it wants to change the process which is currently diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 2b17323d8a93..f72aa8faa11d 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -3645,6 +3645,8 @@ NS_IMETHODIMP BrowserChild::OnLocationChange(nsIWebProgress* aWebProgress, docShell->GetMayEnableCharacterEncodingMenu(); locationChangeData->charsetAutodetected() = docShell->GetCharsetAutodetected(); + locationChangeData->onionUrlbarRewritesAllowed() = + docShell->GetOnionUrlbarRewritesAllowed(); locationChangeData->contentPrincipal() = document->NodePrincipal(); locationChangeData->contentStoragePrincipal() = diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 7749792cafb4..086ecd95e9e7 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -2698,7 +2698,8 @@ mozilla::ipc::IPCResult BrowserParent::RecvOnLocationChange( aWebProgressData->innerDOMWindowID(), aLocationChangeData->requestContextID().isSome(), aLocationChangeData->requestContextID().valueOr(0), - aLocationChangeData->contentType()); + aLocationChangeData->contentType(), + aLocationChangeData->onionUrlbarRewritesAllowed()); } Unused << managerAsListener->OnLocationChange(webProgress, request, aLocation, diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index f36e2c6db353..15756e7fef4f 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -142,6 +142,7 @@ struct WebProgressLocationChangeData bool isSyntheticDocument; bool mayEnableCharacterEncodingMenu; bool charsetAutodetected; + bool onionUrlbarRewritesAllowed; nsString contentType; nsString title; nsString charset; diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index eb05986e017c..2b91fa8ee708 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -1077,6 +1077,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 d8830866b354..6bb120e4babc 100644 --- a/netwerk/dns/effective_tld_names.dat +++ b/netwerk/dns/effective_tld_names.dat @@ -5519,6 +5519,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 901ce11b57ca..988ec94a82d4 100644 --- a/netwerk/ipc/DocumentLoadListener.cpp +++ b/netwerk/ipc/DocumentLoadListener.cpp @@ -1856,6 +1856,16 @@ DocumentLoadListener::AsyncOnChannelRedirect( mLoadStateLoadType, nsIWebNavigation::LOAD_FLAGS_ALLOW_MIXED_CONTENT)); } + // 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)) { + mLoadStateLoadFlags |= + 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 62a1ab1d6796..67fa1e5645c0 100644 --- a/toolkit/content/widgets/browser-custom-element.js +++ b/toolkit/content/widgets/browser-custom-element.js @@ -261,6 +261,8 @@ this._mayEnableCharacterEncodingMenu = null; + this._onionUrlbarRewritesAllowed = false; + this._charsetAutodetected = false; this._contentPrincipal = null; @@ -686,6 +688,12 @@ } } + get onionUrlbarRewritesAllowed() { + return this.isRemoteBrowser + ? this._onionUrlbarRewritesAllowed + : this.docShell.onionUrlbarRewritesAllowed; + } + get charsetAutodetected() { return this.isRemoteBrowser ? this._charsetAutodetected @@ -1292,7 +1300,8 @@ aInnerWindowID, aHaveRequestContextID, aRequestContextID, - aContentType + aContentType, + aOnionUrlbarRewritesAllowed ) { if (this.isRemoteBrowser && this.messageManager) { if (aCharset != null) { @@ -1316,6 +1325,7 @@ this._contentRequestContextID = aHaveRequestContextID ? aRequestContextID : null; + this._onionUrlbarRewritesAllowed = aOnionUrlbarRewritesAllowed; } } @@ -1708,6 +1718,7 @@ "_contentStoragePrincipal", "_isSyntheticDocument", "_innerWindowID", + "_onionUrlbarRewritesAllowed", ] ); } diff --git a/toolkit/modules/sessionstore/SessionHistory.jsm b/toolkit/modules/sessionstore/SessionHistory.jsm index 6c16ac331659..cc47f437824f 100644 --- a/toolkit/modules/sessionstore/SessionHistory.jsm +++ b/toolkit/modules/sessionstore/SessionHistory.jsm @@ -318,6 +318,7 @@ var SessionHistoryInternal = { } entry.persist = shEntry.persist; + entry.onionUrlbarRewritesAllowed = shEntry.onionUrlbarRewritesAllowed; return entry; }, @@ -601,6 +602,10 @@ var SessionHistoryInternal = { } } + if (entry.onionUrlbarRewritesAllowed) { + shEntry.onionUrlbarRewritesAllowed = entry.onionUrlbarRewritesAllowed; + } + return shEntry; },
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 40193: Add `AT_EMPTY_PATH` definition
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 45724d03d79e8bf801600799ad83688b78d0d8ab Author: Georg Koppen <gk(a)torproject.org> Date: Fri Oct 16 08:47:05 2020 +0000 Bug 40193: Add `AT_EMPTY_PATH` definition `AT_EMPTY_PATH` comes with glibc 2.14. However, Debian Wheezy, which we still use for building our stable Linux bundles, comes only with glibc 2.13 resulting in an "undeclared-identifier"-error during build time. This problem got introduced by fixing https://bugzilla.mozilla.org/show_bug.cgi?id=1660901. --- security/sandbox/linux/SandboxFilter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp index dc5dd51a5e3e..2ca3fbb45027 100644 --- a/security/sandbox/linux/SandboxFilter.cpp +++ b/security/sandbox/linux/SandboxFilter.cpp @@ -73,6 +73,10 @@ using namespace sandbox::bpf_dsl; // Not part of UAPI, but userspace sees it in F_GETFL; see bug 1650751. #define FMODE_NONOTIFY 0x4000000 +#ifndef AT_EMPTY_PATH +# define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */ +#endif + #ifndef F_LINUX_SPECIFIC_BASE # define F_LINUX_SPECIFIC_BASE 1024 #else
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 28125 - Prevent non-Necko network connections
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit 5b8f828c25f6b499530f0da67bc026e223341696 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, 3 insertions(+), 93 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 3ba59bfd6776..eb57b1013642 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 @@ -488,54 +488,7 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm { @Override protected Void doInBackground(final Void... params) { - HttpURLConnection urlConnection = null; - BufferedReader in = null; - try { - 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(); - - int responseCode = urlConnection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { - in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StringUtils.UTF_8)); - String inputLine; - StringBuffer response = new StringBuffer(); - - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - in.close(); - mResponseBody = String.valueOf(response).getBytes(StringUtils.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 (IOException e) { - Log.e(LOGTAG, "Got exception during posting provisioning request ...", e); - } catch (URISyntaxException e) { - Log.e(LOGTAG, "Got exception during creating uri ...", e); - } finally { - if (urlConnection != null) { - urlConnection.disconnect(); - } - try { - if (in != null) { - in.close(); - } - } catch (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 fdb44cc2ea1f..7a2044721d2d 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 @@ -395,51 +395,8 @@ public class DefaultHttpDataSource implements HttpDataSource { */ private HttpURLConnection makeConnection(URL url, byte[] postBody, long position, long length, boolean allowGzip, boolean followRedirects) 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); - if (defaultRequestProperties != null) { - for (Map.Entry<String, String> property : defaultRequestProperties.getSnapshot().entrySet()) { - connection.setRequestProperty(property.getKey(), property.getValue()); - } - } - for (Map.Entry<String, String> property : requestProperties.getSnapshot().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); - if (!allowGzip) { - connection.setRequestProperty("Accept-Encoding", "identity"); - } - connection.setInstanceFollowRedirects(followRedirects); - connection.setDoOutput(postBody != null); - if (postBody != null) { - connection.setRequestMethod("POST"); - if (postBody.length == 0) { - connection.connect(); - } else { - connection.setFixedLengthStreamingMode(postBody.length); - connection.connect(); - OutputStream os = connection.getOutputStream(); - os.write(postBody); - os.close(); - } - } else { - connection.connect(); - } - return connection; + Log.i(TAG, "This is Tor Browser. Skipping."); + throw new IOException(); } /**
1 0
0 0
[tor-browser/tor-browser-78.9.0esr-10.0-1] Bug 21952: Implement Onion-Location
by sysrqb@torproject.org 17 Mar '21

17 Mar '21
commit c4b3103e320362d14db28f2e9dbee75580a684f7 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/browser.xhtml | 3 + browser/components/BrowserGlue.jsm | 9 ++ .../onionservices/OnionLocationChild.jsm | 43 ++++++ .../onionservices/OnionLocationParent.jsm | 161 +++++++++++++++++++++ .../content/onionlocation-notification-icons.css | 5 + .../onionservices/content/onionlocation-urlbar.css | 27 ++++ .../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 | 9 ++ modules/libpref/init/StaticPrefList.yaml | 5 + xpcom/ds/StaticAtoms.py | 1 + 22 files changed, 392 insertions(+), 1 deletion(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 70d3f43bf0ef..5662f05c4394 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -44,6 +44,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", @@ -5422,6 +5423,7 @@ var XULBrowserWindow = { Services.obs.notifyObservers(null, "touchbar-location-change", location); UpdateBackForwardCommands(gBrowser.webNavigation); ReaderParent.updateReaderButton(gBrowser.selectedBrowser); + OnionLocationParent.updateOnionLocationBadge(gBrowser.selectedBrowser); if (!gMultiProcessBrowser) { // Bug 1108553 - Cannot rotate images with e10s @@ -5964,6 +5966,16 @@ const AccessibilityRefreshBlocker = { 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/browser.xhtml b/browser/base/content/browser.xhtml index 4bbc85706798..fc3ddf5d650f 100644 --- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -1077,6 +1077,9 @@ onclick="FullZoom.reset();" tooltip="dynamic-shortcut-tooltip" hidden="true"/> + +#include ../../components/onionservices/content/onionlocation-urlbar.inc.xhtml + <box id="pageActionSeparator" class="urlbar-page-action"/> <image id="pageActionButton" class="urlbar-icon urlbar-page-action" diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index d30abff54562..e08e461a27ff 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -539,6 +539,13 @@ let LEGACY_ACTORS = { observers: ["keyword-uri-fixup"], }, }, + OnionLocation: { + child: { + module: "resource:///modules/OnionLocationChild.jsm", + events: { pageshow: {} }, + messages: ["OnionLocation:Refresh"], + }, + }, }; if (AppConstants.TOR_BROWSER_UPDATE) { @@ -713,6 +720,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { XPCOMUtils.defineLazyModuleGetters(this, { AboutLoginsParent: "resource:///modules/AboutLoginsParent.jsm", AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm", + OnionLocationParent: "resource:///modules/OnionLocationParent.jsm", PluginManager: "resource:///actors/PluginParent.jsm", ReaderParent: "resource:///modules/ReaderParent.jsm", }); @@ -816,6 +824,7 @@ const listeners = { "AboutLogins:VulnerableLogins": ["AboutLoginsParent"], "Reader:FaviconRequest": ["ReaderParent"], "Reader:UpdateReaderButton": ["ReaderParent"], + "OnionLocation:Set": ["OnionLocationParent"], }, observe(subject, topic, data) { diff --git a/browser/components/onionservices/OnionLocationChild.jsm b/browser/components/onionservices/OnionLocationChild.jsm new file mode 100644 index 000000000000..1059eb7d5925 --- /dev/null +++ b/browser/components/onionservices/OnionLocationChild.jsm @@ -0,0 +1,43 @@ +// Copyright (c) 2020, The Tor Project, Inc. + +"use strict"; + +var EXPORTED_SYMBOLS = ["OnionLocationChild"]; + +const { ActorChild } = ChromeUtils.import( + "resource://gre/modules/ActorChild.jsm" +); + +class OnionLocationChild extends ActorChild { + handleEvent(event) { + this.onPageShow(event); + } + + onPageShow(event) { + if (event.target != this.content.document) { + return; + } + const onionLocationURI = this.content.document.onionLocationURI; + if (onionLocationURI) { + this.mm.sendAsyncMessage("OnionLocation:Set"); + } + } + + receiveMessage(aMessage) { + if (aMessage.name == "OnionLocation:Refresh") { + const doc = this.content.document; + const docShell = this.mm.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..1c79fc07d215 --- /dev/null +++ b/browser/components/onionservices/OnionLocationParent.jsm @@ -0,0 +1,161 @@ +// 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; + +var OnionLocationParent = { + // Listeners are added in BrowserGlue.jsm + receiveMessage(aMsg) { + switch (aMsg.name) { + case "OnionLocation:Set": + this.setOnionLocation(aMsg.target); + break; + } + }, + + buttonClick(event) { + if (event.button != 0) { + return; + } + const win = event.target.ownerGlobal; + const browser = win.gBrowser.selectedBrowser; + this.redirect(browser); + }, + + redirect(browser) { + browser.messageManager.sendAsyncMessage("OnionLocation:Refresh"); + this.setDisabled(browser); + }, + + onStateChange(browser) { + delete browser._onionLocation; + this.hideNotification(browser); + }, + + setOnionLocation(browser) { + const win = browser.ownerGlobal; + browser._onionLocation = true; + if (browser === win.gBrowser.selectedBrowser) { + this.updateOnionLocationBadge(browser); + } + }, + + hideNotification(browser) { + const win = browser.ownerGlobal; + if (browser._onionLocationPrompt) { + win.PopupNotifications.remove(browser._onionLocationPrompt); + } + }, + + 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 + ); + }, + + 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"); + }, + + setDisabled(browser) { + const win = browser.ownerGlobal; + const elem = win.document.getElementById(ONIONLOCATION_BOX_ID); + elem.setAttribute("hidden", true); + }, + + updateOnionLocationBadge(browser) { + if (browser._onionLocation) { + this.setEnabled(browser); + this.showNotification(browser); + } else { + this.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..91cad5f178d1 --- /dev/null +++ b/browser/components/onionservices/content/onionlocation-urlbar.css @@ -0,0 +1,27 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ + +#onion-location-button { + list-style-image: url(chrome://browser/content/onionservices/onionlocation.svg); +} + +#onion-location-box { + border-radius: 3px; + background-color: #6200A4; + padding-left: 5px; + padding-right: 5px; + color: white; + -moz-context-properties: fill; + fill: white; +} + +#onion-location-box:hover { + background-color: #0060DF !important; +} + +toolbar[brighttext] #onion-location-box { + background-color: #9400ff; +} + +toolbar[brighttext] #onion-location-box:hover { + background-color: #0060DF !important; +} 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 e4b6d73f8f40..dfd664df434e 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 eb7587afa0e1..6e05405079bf 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 15957b416a67..7f24a0a659f5 100644 --- a/browser/components/preferences/privacy.js +++ b/browser/components/preferences/privacy.js @@ -90,6 +90,12 @@ XPCOMUtils.defineLazyScriptGetter( "chrome://browser/content/securitylevel/securityLevel.js" ); +XPCOMUtils.defineLazyScriptGetter( + this, + ["OnionLocationPreferences"], + "chrome://browser/content/onionservices/onionlocationPreferences.js" +); + XPCOMUtils.defineLazyServiceGetter( this, "listManager", @@ -158,6 +164,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" }, @@ -300,6 +309,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. */ @@ -442,6 +458,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 979ae9482244..7aa92d51f4d6 100644 --- a/browser/themes/shared/notification-icons.inc.css +++ b/browser/themes/shared/notification-icons.inc.css @@ -415,3 +415,5 @@ html|*#webRTC-previewVideo { background: #FFE900 url(chrome://browser/skin/notification-icons/update.svg) no-repeat center; border-radius: 50%; } + +%include ../../components/onionservices/content/onionlocation-notification-icons.css \ No newline at end of file diff --git a/browser/themes/shared/urlbar-searchbar.inc.css b/browser/themes/shared/urlbar-searchbar.inc.css index 0b1f69342995..d3cc6bf7f024 100644 --- a/browser/themes/shared/urlbar-searchbar.inc.css +++ b/browser/themes/shared/urlbar-searchbar.inc.css @@ -824,3 +824,5 @@ .searchbar-search-button:hover:not([addengines=true]) > .searchbar-search-icon-overlay:-moz-locale-dir(rtl) { margin-inline: -26px 20px; } + +%include ../../components/onionservices/content/onionlocation-urlbar.css diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 132c0ecbfdac..afc872569519 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -2542,6 +2542,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 @@ -6026,6 +6027,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, NS_LITERAL_CSTRING(".onion")) && + ((NS_SUCCEEDED(aOnionLocationURI->SchemeIs("http", &isHttpish)) && + isHttpish) || + (NS_SUCCEEDED(aOnionLocationURI->SchemeIs("https", &isHttpish)) && + isHttpish)) && + NS_SUCCEEDED(aOnionLocationURI->GetAsciiHost(host)) && + StringEndsWith(host, NS_LITERAL_CSTRING(".onion")); +} + void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) { if (!aHeaderField) { NS_ERROR("null headerField"); @@ -6101,6 +6118,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, @@ -10141,7 +10173,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 6d06a8c2a8cd..6e80306e94b5 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -3322,6 +3322,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) { @@ -4194,6 +4195,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 8130db018f47..df3a18eaf266 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -676,3 +676,12 @@ partial interface Document { [ChromeOnly, Pure] readonly attribute nsIPermissionDelegateHandler permDelegateHandler; }; + + +/** + * 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 2b91fa8ee708..2d09ef687bbd 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -8446,6 +8446,11 @@ value: @IS_NIGHTLY_BUILD@ mirror: always +- 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 ab5f662575e4..23a5d6f9bb95 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -811,6 +811,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
0 0
  • ← Newer
  • 1
  • ...
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • ...
  • 35
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.