This is an automated email from the git hooks/post-receive script.
pierov pushed a commit to branch tor-browser-102.5.0esr-12.5-1 in repository tor-browser.
commit 91f015b892fa95d299a313785a228a9e7fe9eb96 Author: Alex Catarineu acat@torproject.org AuthorDate: Thu Mar 5 22:16:39 2020 +0100
Bug 21952: Implement Onion-Location
Whenever a valid Onion-Location HTTP header (or corresponding HTML <meta> http-equiv attribute) is found in a document load, we either redirect to it (if the user opted-in via preference) or notify the presence of an onionsite alternative with a badge in the urlbar. --- browser/base/content/browser.js | 12 ++ browser/base/content/navigator-toolbox.inc.xhtml | 3 + browser/components/BrowserGlue.jsm | 13 ++ .../onionservices/OnionLocationChild.jsm | 48 ++++++ .../onionservices/OnionLocationParent.jsm | 169 +++++++++++++++++++++ .../content/onionlocation-urlbar.inc.xhtml | 10 ++ .../onionservices/content/onionlocation.css | 46 ++++++ .../onionservices/content/onionlocation.svg | 3 + .../content/onionlocationPreferences.inc.xhtml | 11 ++ .../content/onionlocationPreferences.js | 34 +++++ browser/components/onionservices/jar.mn | 3 + browser/components/onionservices/moz.build | 5 + browser/components/preferences/privacy.inc.xhtml | 2 + browser/components/preferences/privacy.js | 17 +++ browser/themes/shared/browser-shared.css | 1 + dom/base/Document.cpp | 69 ++++++++- dom/base/Document.h | 2 + dom/webidl/Document.webidl | 8 + mobile/android/geckoview/api.txt | 3 + .../mozilla/geckoview/GeckoRuntimeSettings.java | 33 ++++ modules/libpref/init/StaticPrefList.yaml | 5 + xpcom/ds/StaticAtoms.py | 1 + 22 files changed, 497 insertions(+), 1 deletion(-)
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 66ac2d0f1d7c..24c5939750ee 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -48,6 +48,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { NetUtil: "resource://gre/modules/NetUtil.jsm", NewTabUtils: "resource://gre/modules/NewTabUtils.jsm", OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm", + OnionLocationParent: "resource:///modules/OnionLocationParent.jsm", PageActions: "resource:///modules/PageActions.jsm", PageThumbs: "resource://gre/modules/PageThumbs.jsm", PanelMultiView: "resource:///modules/PanelMultiView.jsm", @@ -5520,6 +5521,7 @@ var XULBrowserWindow = { CFRPageActions.updatePageActions(gBrowser.selectedBrowser);
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser); + OnionLocationParent.updateOnionLocationBadge(gBrowser.selectedBrowser);
if (!gMultiProcessBrowser) { // Bug 1108553 - Cannot rotate images with e10s @@ -6039,6 +6041,16 @@ var CombinedStopReload = {
var TabsProgressListener = { onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { + // Clear OnionLocation UI + if ( + aStateFlags & Ci.nsIWebProgressListener.STATE_START && + aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK && + aRequest && + aWebProgress.isTopLevel + ) { + OnionLocationParent.onStateChange(aBrowser); + } + // Collect telemetry data about tab load times. if ( aWebProgress.isTopLevel && diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml index 1cd54285c9a9..8e199e658a41 100644 --- a/browser/base/content/navigator-toolbox.inc.xhtml +++ b/browser/base/content/navigator-toolbox.inc.xhtml @@ -352,6 +352,9 @@ onclick="FullZoom.reset(); FullZoom.resetScalingZoom();" tooltip="dynamic-shortcut-tooltip" hidden="true"/> + +#include ../../components/onionservices/content/onionlocation-urlbar.inc.xhtml + <hbox id="pageActionButton" class="urlbar-page-action" role="button" diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm index fe46dec2c42d..5b60b15029c0 100644 --- a/browser/components/BrowserGlue.jsm +++ b/browser/components/BrowserGlue.jsm @@ -582,6 +582,19 @@ let JSWINDOWACTORS = { allFrames: true, },
+ OnionLocation: { + parent: { + moduleURI: "resource:///modules/OnionLocationParent.jsm", + }, + child: { + moduleURI: "resource:///modules/OnionLocationChild.jsm", + events: { + pageshow: { mozSystemGroup: true }, + }, + }, + messageManagerGroups: ["browsers"], + }, + PageInfo: { child: { moduleURI: "resource:///actors/PageInfoChild.jsm", diff --git a/browser/components/onionservices/OnionLocationChild.jsm b/browser/components/onionservices/OnionLocationChild.jsm new file mode 100644 index 000000000000..23e1823f5a09 --- /dev/null +++ b/browser/components/onionservices/OnionLocationChild.jsm @@ -0,0 +1,48 @@ +// Copyright (c) 2020, The Tor Project, Inc. + +"use strict"; + +var EXPORTED_SYMBOLS = ["OnionLocationChild"]; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +class OnionLocationChild extends JSWindowActorChild { + handleEvent(event) { + this.onPageShow(event); + } + + onPageShow(event) { + if (event.target != this.document) { + return; + } + const onionLocationURI = this.document.onionLocationURI; + if (onionLocationURI) { + this.sendAsyncMessage("OnionLocation:Set"); + } + } + + receiveMessage(aMessage) { + if (aMessage.name == "OnionLocation:Refresh") { + const doc = this.document; + const docShell = this.docShell; + let onionLocationURI = doc.onionLocationURI; + const refreshURI = docShell.QueryInterface(Ci.nsIRefreshURI); + if (onionLocationURI && refreshURI) { + const docUrl = new URL(doc.URL); + let onionUrl = new URL(onionLocationURI.asciiSpec); + // Keep consistent with Location + if (!onionUrl.hash && docUrl.hash) { + onionUrl.hash = docUrl.hash; + onionLocationURI = Services.io.newURI(onionUrl.toString()); + } + 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..4ac4a5d0775e --- /dev/null +++ b/browser/components/onionservices/OnionLocationParent.jsm @@ -0,0 +1,169 @@ +// 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.learnMoreURLNotification; + +class OnionLocationParent extends JSWindowActorParent { + // Listeners are added in BrowserGlue.jsm + receiveMessage(aMsg) { + switch (aMsg.name) { + case "OnionLocation:Set": + let browser = this.browsingContext.embedderElement; + OnionLocationParent.setOnionLocation(browser); + break; + } + } + + static buttonClick(event) { + if (event.button !== 0) { + return; + } + const win = event.target.ownerGlobal; + if (win.gBrowser) { + const browser = win.gBrowser.selectedBrowser; + OnionLocationParent.redirect(browser); + } + } + + static redirect(browser) { + let windowGlobal = browser.browsingContext.currentWindowGlobal; + let actor = windowGlobal.getActor("OnionLocation"); + if (actor) { + actor.sendAsyncMessage("OnionLocation:Refresh", {}); + OnionLocationParent.setDisabled(browser); + } + } + + static onStateChange(browser) { + delete browser._onionLocation; + OnionLocationParent.hideNotification(browser); + } + + static setOnionLocation(browser) { + browser._onionLocation = true; + let tabBrowser = browser.getTabBrowser(); + if (tabBrowser && browser === tabBrowser.selectedBrowser) { + OnionLocationParent.updateOnionLocationBadge(browser); + } + } + + static hideNotification(browser) { + const win = browser.ownerGlobal; + if (browser._onionLocationPrompt) { + win.PopupNotifications.remove(browser._onionLocationPrompt); + } + } + + static showNotification(browser) { + const mustShow = Services.prefs.getBoolPref(NOTIFICATION_PREF, true); + if (!mustShow) { + return; + } + + const win = browser.ownerGlobal; + Services.prefs.setBoolPref(NOTIFICATION_PREF, false); + + const mainAction = { + label: NOTIFICATION_OK_LABEL, + accessKey: NOTIFICATION_OK_ACCESSKEY, + callback() { + Services.prefs.setBoolPref(PRIORITIZE_ONIONS_PREF, true); + OnionLocationParent.redirect(browser); + win.openPreferences("privacy-onionservices"); + }, + }; + + const cancelAction = { + label: NOTIFICATION_CANCEL_LABEL, + accessKey: NOTIFICATION_CANCEL_ACCESSKEY, + callback: () => {}, + }; + + const options = { + autofocus: true, + persistent: true, + removeOnDismissal: false, + eventCallback(aTopic) { + if (aTopic === "removed") { + delete browser._onionLocationPrompt; + delete browser.onionpopupnotificationanchor; + } + }, + learnMoreURL: NOTIFICATION_LEARN_MORE_URL, + displayURI: { + hostPort: NOTIFICATION_TITLE, // This is hacky, but allows us to have a title without extra markup/css. + }, + hideClose: true, + popupIconClass: "onionlocation-notification-icon", + }; + + // A hacky way of setting the popup anchor outside the usual url bar icon box + // onionlocationpopupnotificationanchor comes from `${ANCHOR_ID}popupnotificationanchor` + // From https://searchfox.org/mozilla-esr68/rev/080f9ed47742644d2ff84f7aa0b10aea5c44... + browser.onionlocationpopupnotificationanchor = win.document.getElementById( + ONIONLOCATION_BUTTON_ID + ); + + browser._onionLocationPrompt = win.PopupNotifications.show( + browser, + NOTIFICATION_ID, + NOTIFICATION_DESCRIPTION, + NOTIFICATION_ANCHOR_ID, + mainAction, + [cancelAction], + options + ); + } + + static setEnabled(browser) { + const win = browser.ownerGlobal; + const label = win.document.getElementById(ONIONLOCATION_LABEL_ID); + label.textContent = STRING_ONION_AVAILABLE; + const elem = win.document.getElementById(ONIONLOCATION_BOX_ID); + elem.removeAttribute("hidden"); + } + + static setDisabled(browser) { + const win = browser.ownerGlobal; + const elem = win.document.getElementById(ONIONLOCATION_BOX_ID); + elem.setAttribute("hidden", true); + } + + static updateOnionLocationBadge(browser) { + if (browser._onionLocation) { + OnionLocationParent.setEnabled(browser); + OnionLocationParent.showNotification(browser); + } else { + OnionLocationParent.setDisabled(browser); + } + } +} diff --git a/browser/components/onionservices/content/onionlocation-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.css b/browser/components/onionservices/content/onionlocation.css new file mode 100644 index 000000000000..b90b2b9b8d46 --- /dev/null +++ b/browser/components/onionservices/content/onionlocation.css @@ -0,0 +1,46 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ + +#onion-location-box { + background-color: var(--purple-60); + -moz-context-properties: fill; + fill: white; +} + +#onion-location-box:hover { + background-color: var(--purple-70); +} + +#onion-location-box:active { + background-color: var(--purple-80); +} + +@media (prefers-color-scheme: dark) { + #onion-location-box { + background-color: var(--purple-50); + } + + #onion-location-box:hover { + background-color: var(--purple-60); + } + + #onion-location-box:active { + background-color: var(--purple-70); + } +} + +#onion-location-button { + list-style-image: url(chrome://browser/content/onionservices/onionlocation.svg); + padding-inline-start: 0.5em; +} + +label#onion-label { + margin: 0; + padding-block: 0; + padding-inline: 0.5em; + color: white; + font-weight: normal; +} + +.onionlocation-notification-icon { + display: none; +} 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- [...] +</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..005e37d4a991 --- /dev/null +++ b/browser/components/onionservices/content/onionlocationPreferences.js @@ -0,0 +1,34 @@ +// 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; + if (TorStrings.onionLocation.learnMoreURL.startsWith("about:")) { + learnMore.setAttribute("useoriginprincipal", "true"); + } + 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..e778abd206b3 100644 --- a/browser/components/onionservices/jar.mn +++ b/browser/components/onionservices/jar.mn @@ -7,3 +7,6 @@ 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) + skin/classic/browser/onionlocation.css (content/onionlocation.css) diff --git a/browser/components/onionservices/moz.build b/browser/components/onionservices/moz.build index 2661ad7cb9f3..27f9d2da4a9e 100644 --- a/browser/components/onionservices/moz.build +++ b/browser/components/onionservices/moz.build @@ -1 +1,6 @@ JAR_MANIFESTS += ["jar.mn"] + +EXTRA_JS_MODULES += [ + "OnionLocationChild.jsm", + "OnionLocationParent.jsm", +] diff --git a/browser/components/preferences/privacy.inc.xhtml b/browser/components/preferences/privacy.inc.xhtml index 37f77e3b70da..d322527a9853 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 d584fb5e4624..d6a77393b155 100644 --- a/browser/components/preferences/privacy.js +++ b/browser/components/preferences/privacy.js @@ -61,6 +61,12 @@ XPCOMUtils.defineLazyScriptGetter( "chrome://browser/content/securitylevel/securityLevel.js" );
+XPCOMUtils.defineLazyScriptGetter( + this, + ["OnionLocationPreferences"], + "chrome://browser/content/onionservices/onionlocationPreferences.js" +); + XPCOMUtils.defineLazyPreferenceGetter( this, "OS_AUTH_ENABLED", @@ -138,6 +144,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" },
@@ -339,6 +348,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. */ @@ -536,6 +552,7 @@ var gPrivacyPane = { this._initTrackingProtectionExtensionControl(); OnionServicesAuthPreferences.init(); this._initSecurityLevel(); + this._initOnionLocation();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
diff --git a/browser/themes/shared/browser-shared.css b/browser/themes/shared/browser-shared.css index c8dac0afb49a..e771c46dcbf1 100644 --- a/browser/themes/shared/browser-shared.css +++ b/browser/themes/shared/browser-shared.css @@ -22,6 +22,7 @@ @import url("chrome://browser/skin/customizableui/customizeMode.css"); @import url("chrome://browser/skin/UITour.css"); @import url("chrome://browser/skin/torconnect-urlbar.css"); +@import url("chrome://browser/skin/onionlocation.css");
@namespace html url("http://www.w3.org/1999/xhtml");
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index b1be884fe828..9001556d0232 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -2925,6 +2925,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 @@ -6828,6 +6829,57 @@ void Document::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const { } }
+static bool IsValidOnionLocation(nsIURI* aDocumentURI, + nsIURI* aOnionLocationURI) { + if (!aDocumentURI || !aOnionLocationURI) { + return false; + } + + // Current URI + nsAutoCString host; + if (!aDocumentURI->SchemeIs("https")) { + return false; + } + NS_ENSURE_SUCCESS(aDocumentURI->GetAsciiHost(host), false); + if (StringEndsWith(host, ".onion"_ns)) { + // Already in the .onion site + return false; + } + + // Target URI + if (!aOnionLocationURI->SchemeIs("http") && + !aOnionLocationURI->SchemeIs("https")) { + return false; + } + nsCOMPtr<nsIEffectiveTLDService> eTLDService = + do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); + if (!eTLDService) { + NS_ENSURE_SUCCESS(aOnionLocationURI->GetAsciiHost(host), false); + // This should not happen, but in the unlikely case, still check if it is a + // .onion and in case allow it. + return StringEndsWith(host, ".onion"_ns); + } + NS_ENSURE_SUCCESS(eTLDService->GetBaseDomain(aOnionLocationURI, 0, host), + false); + if (!StringEndsWith(host, ".onion"_ns)) { + return false; + } + + // Ignore v2 + if (host.Length() == 22) { + const char* cur = host.BeginWriting(); + // We have already checked that it ends by ".onion" + const char* end = host.EndWriting() - 6; + bool base32 = true; + for (; cur < end && base32; ++cur) { + base32 = isalpha(*cur) || ('2' <= *cur && *cur <= '7'); + } + return !base32; + } + + return true; +} + void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) { if (!aHeaderField) { NS_ERROR("null headerField"); @@ -6903,6 +6955,21 @@ void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) { if (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); + } + } else { + mOnionLocationURI = onionURI; + } + } + } }
void Document::TryChannelCharset(nsIChannel* aChannel, int32_t& aCharsetSource, @@ -11036,7 +11103,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", "origin-trial", + "x-frame-options", "origin-trial", "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 2d9bbba59bce..91dfe3de8028 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -3401,6 +3401,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) { @@ -4413,6 +4414,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 3d089ddbe848..9c5f78784cb1 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -748,3 +748,11 @@ partial interface Document { [ChromeOnly] Wireframe? getWireframe(optional boolean aIncludeNodes = false); }; + +/** + * 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/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index af1b7796e6d2..707a65a7eebc 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -807,6 +807,7 @@ package org.mozilla.geckoview { method public boolean getLoginAutofillEnabled(); method public boolean getPauseForDebuggerEnabled(); method public int getPreferredColorScheme(); + method public boolean getPrioritizeOnions(); method public boolean getRemoteDebuggingEnabled(); method @Nullable public GeckoRuntime getRuntime(); method @Nullable public Rect getScreenSizeOverride(); @@ -832,6 +833,7 @@ package org.mozilla.geckoview { method public void setLocales(@Nullable String[]); method @NonNull public GeckoRuntimeSettings setLoginAutofillEnabled(boolean); method @NonNull public GeckoRuntimeSettings setPreferredColorScheme(int); + method @NonNull public GeckoRuntimeSettings setPrioritizeOnions(boolean); method @NonNull public GeckoRuntimeSettings setRemoteDebuggingEnabled(boolean); method @NonNull public GeckoRuntimeSettings setSpoofEnglish(boolean); method @NonNull public GeckoRuntimeSettings setTorSecurityLevel(int); @@ -871,6 +873,7 @@ package org.mozilla.geckoview { method @NonNull public GeckoRuntimeSettings.Builder locales(@Nullable String[]); method @NonNull public GeckoRuntimeSettings.Builder loginAutofillEnabled(boolean); method @NonNull public GeckoRuntimeSettings.Builder pauseForDebugger(boolean); + method @NonNull public GeckoRuntimeSettings.Builder prioritizeOnions(boolean); method @NonNull public GeckoRuntimeSettings.Builder preferredColorScheme(int); method @NonNull public GeckoRuntimeSettings.Builder remoteDebuggingEnabled(boolean); method @NonNull public GeckoRuntimeSettings.Builder screenSizeOverride(int, int); diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java index 5cc92aa81834..90df323d667d 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java @@ -475,6 +475,17 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { getSettings().mSecurityLevel.set(level); return this; } + + /** + * Sets whether the browser should prioritize .onion sites when available. + * + * @param flag True if we should prioritize .onion sites, false otherwise + * @return This Builder instance. + */ + public @NonNull Builder prioritizeOnions(final boolean flag) { + getSettings().mPrioritizeOnions.set(flag); + return this; + } }
private GeckoRuntime mRuntime; @@ -526,6 +537,8 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { /* package */ final Pref<Integer> mSpoofEnglish = new Pref<>("privacy.spoof_english", 0); /* package */ final Pref<Integer> mSecurityLevel = new Pref<>("browser.security_level.security_slider", 4); + /* package */ final Pref<Boolean> mPrioritizeOnions = + new Pref<>("privacy.prioritizeonions.enabled", false);
/* package */ int mPreferredColorScheme = COLOR_SCHEME_SYSTEM;
@@ -1310,6 +1323,26 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { return this; }
+ /** + * Gets whether we should prioritize .onion sites. + * + * @return Whether we should prioritize .onion sites. + */ + public boolean getPrioritizeOnions() { + return mPrioritizeOnions.get(); + } + + /** + * Sets whether we should prioritize .onion sites. + * + * @param flag Whether we should prioritize .onion sites. + * @return This GeckoRuntimeSettings instance. + */ + public @NonNull GeckoRuntimeSettings setPrioritizeOnions(final boolean flag) { + mPrioritizeOnions.commit(flag); + return this; + } + @Override // Parcelable public void writeToParcel(final Parcel out, final int flags) { super.writeToParcel(out, flags); diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 5df8f94e761e..23a237ee71b9 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -11850,6 +11850,11 @@ value: "" mirror: never
+- name: privacy.prioritizeonions.enabled + type: RelaxedAtomicBool + value: false + mirror: always + #--------------------------------------------------------------------------- # Prefs starting with "prompts." #--------------------------------------------------------------------------- diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py index 740b3f6e187d..503a62d5f17b 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -826,6 +826,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"),