[tor-commits] [tor-browser] 68/68: Bug 21952: Implement Onion-Location

gitolite role git at cupani.torproject.org
Tue May 3 17:13:32 UTC 2022


This is an automated email from the git hooks/post-receive script.

richard pushed a commit to branch tor-browser-91.9.0esr-11.5-1
in repository tor-browser.

commit b1bfda557c9c34ace91ef92b18f7ddb9c9ba078e
Author: Alex Catarineu <acat at 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          | 168 +++++++++++++++++++++
 .../content/onionlocation-notification-icons.css   |   5 +
 .../onionservices/content/onionlocation-urlbar.css |  60 ++++++++
 .../content/onionlocation-urlbar.inc.xhtml         |  10 ++
 .../onionservices/content/onionlocation.svg        |   3 +
 .../content/onionlocationPreferences.inc.xhtml     |  11 ++
 .../content/onionlocationPreferences.js            |  31 ++++
 browser/components/onionservices/jar.mn            |   2 +
 browser/components/onionservices/moz.build         |   2 +
 browser/components/preferences/privacy.inc.xhtml   |   2 +
 browser/components/preferences/privacy.js          |  17 +++
 browser/themes/shared/notification-icons.inc.css   |   2 +
 browser/themes/shared/urlbar-searchbar.inc.css     |   1 +
 dom/base/Document.cpp                              |  34 ++++-
 dom/base/Document.h                                |   2 +
 dom/webidl/Document.webidl                         |   8 +
 modules/libpref/init/StaticPrefList.yaml           |   5 +
 xpcom/ds/StaticAtoms.py                            |   1 +
 22 files changed, 439 insertions(+), 1 deletion(-)

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index a5a2020b28513..16123f02ff49b 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",
@@ -5319,6 +5320,7 @@ var XULBrowserWindow = {
     CFRPageActions.updatePageActions(gBrowser.selectedBrowser);
 
     AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
+    OnionLocationParent.updateOnionLocationBadge(gBrowser.selectedBrowser);
 
     if (!gMultiProcessBrowser) {
       // Bug 1108553 - Cannot rotate images with e10s
@@ -5809,6 +5811,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 810a77e57766d..b881492864ae9 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -359,6 +359,9 @@
                      onclick="FullZoom.reset(); FullZoom.resetScalingZoom();"
                      tooltip="dynamic-shortcut-tooltip"
                      hidden="true"/>
+
+#include ../../components/onionservices/content/onionlocation-urlbar.inc.xhtml
+
               <hbox id="pageActionButton"
                     class="urlbar-page-action urlbar-icon-wrapper"
                     role="button"
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index 51e027aa9e2d5..e2824bffdf070 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -570,6 +570,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 0000000000000..23e1823f5a09a
--- /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 0000000000000..f6250e5548625
--- /dev/null
+++ b/browser/components/onionservices/OnionLocationParent.jsm
@@ -0,0 +1,168 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["OnionLocationParent"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
+// Prefs
+const NOTIFICATION_PREF = "privacy.prioritizeonions.showNotification";
+const PRIORITIZE_ONIONS_PREF = "privacy.prioritizeonions.enabled";
+
+// Element IDs
+const ONIONLOCATION_BOX_ID = "onion-location-box";
+const ONIONLOCATION_BUTTON_ID = "onion-location-button";
+const ONIONLOCATION_LABEL_ID = "onion-label";
+
+// Notification IDs
+const NOTIFICATION_ID = "onion-location";
+const NOTIFICATION_ANCHOR_ID = "onionlocation";
+
+// Strings
+const STRING_ONION_AVAILABLE = TorStrings.onionLocation.onionAvailable;
+const NOTIFICATION_CANCEL_LABEL = TorStrings.onionLocation.notNow;
+const NOTIFICATION_CANCEL_ACCESSKEY = TorStrings.onionLocation.notNowAccessKey;
+const NOTIFICATION_OK_LABEL = TorStrings.onionLocation.alwaysPrioritize;
+const NOTIFICATION_OK_ACCESSKEY =
+  TorStrings.onionLocation.alwaysPrioritizeAccessKey;
+const NOTIFICATION_TITLE = TorStrings.onionLocation.tryThis;
+const NOTIFICATION_DESCRIPTION = TorStrings.onionLocation.description;
+const NOTIFICATION_LEARN_MORE_URL = TorStrings.onionLocation.learnMoreURL;
+
+class OnionLocationParent extends JSWindowActorParent {
+  // Listeners are added in BrowserGlue.jsm
+  receiveMessage(aMsg) {
+    switch (aMsg.name) {
+      case "OnionLocation:Set":
+        let browser = this.browsingContext.embedderElement;
+        OnionLocationParent.setOnionLocation(browser);
+        break;
+    }
+  }
+
+  static buttonClick(event) {
+    if (event.button !== 0) {
+      return;
+    }
+    const win = event.target.ownerGlobal;
+    if (win.gBrowser) {
+      const browser = win.gBrowser.selectedBrowser;
+      OnionLocationParent.redirect(browser);
+    }
+  }
+
+  static redirect(browser) {
+    let windowGlobal = browser.browsingContext.currentWindowGlobal;
+    let actor = windowGlobal.getActor("OnionLocation");
+    if (actor) {
+      actor.sendAsyncMessage("OnionLocation:Refresh", {});
+      OnionLocationParent.setDisabled(browser);
+    }
+  }
+
+  static onStateChange(browser) {
+    delete browser._onionLocation;
+    OnionLocationParent.hideNotification(browser);
+  }
+
+  static setOnionLocation(browser) {
+    browser._onionLocation = true;
+    let tabBrowser = browser.getTabBrowser();
+    if (tabBrowser && browser === tabBrowser.selectedBrowser) {
+      OnionLocationParent.updateOnionLocationBadge(browser);
+    }
+  }
+
+  static hideNotification(browser) {
+    const win = browser.ownerGlobal;
+    if (browser._onionLocationPrompt) {
+      win.PopupNotifications.remove(browser._onionLocationPrompt);
+    }
+  }
+
+  static showNotification(browser) {
+    const mustShow = Services.prefs.getBoolPref(NOTIFICATION_PREF, true);
+    if (!mustShow) {
+      return;
+    }
+
+    const win = browser.ownerGlobal;
+    Services.prefs.setBoolPref(NOTIFICATION_PREF, false);
+
+    const mainAction = {
+      label: NOTIFICATION_OK_LABEL,
+      accessKey: NOTIFICATION_OK_ACCESSKEY,
+      callback() {
+        Services.prefs.setBoolPref(PRIORITIZE_ONIONS_PREF, true);
+        OnionLocationParent.redirect(browser);
+        win.openPreferences("privacy-onionservices");
+      },
+    };
+
+    const cancelAction = {
+      label: NOTIFICATION_CANCEL_LABEL,
+      accessKey: NOTIFICATION_CANCEL_ACCESSKEY,
+      callback: () => {},
+    };
+
+    const options = {
+      autofocus: true,
+      persistent: true,
+      removeOnDismissal: false,
+      eventCallback(aTopic) {
+        if (aTopic === "removed") {
+          delete browser._onionLocationPrompt;
+          delete browser.onionpopupnotificationanchor;
+        }
+      },
+      learnMoreURL: NOTIFICATION_LEARN_MORE_URL,
+      displayURI: {
+        hostPort: NOTIFICATION_TITLE, // This is hacky, but allows us to have a title without extra markup/css.
+      },
+      hideClose: true,
+      popupIconClass: "onionlocation-notification-icon",
+    };
+
+    // A hacky way of setting the popup anchor outside the usual url bar icon box
+    // onionlocationpopupnotificationanchor comes from `${ANCHOR_ID}popupnotificationanchor`
+    // From https://searchfox.org/mozilla-esr68/rev/080f9ed47742644d2ff84f7aa0b10aea5c44301a/browser/components/newtab/lib/CFRPageActions.jsm#488
+    browser.onionlocationpopupnotificationanchor = win.document.getElementById(
+      ONIONLOCATION_BUTTON_ID
+    );
+
+    browser._onionLocationPrompt = win.PopupNotifications.show(
+      browser,
+      NOTIFICATION_ID,
+      NOTIFICATION_DESCRIPTION,
+      NOTIFICATION_ANCHOR_ID,
+      mainAction,
+      [cancelAction],
+      options
+    );
+  }
+
+  static setEnabled(browser) {
+    const win = browser.ownerGlobal;
+    const label = win.document.getElementById(ONIONLOCATION_LABEL_ID);
+    label.textContent = STRING_ONION_AVAILABLE;
+    const elem = win.document.getElementById(ONIONLOCATION_BOX_ID);
+    elem.removeAttribute("hidden");
+  }
+
+  static setDisabled(browser) {
+    const win = browser.ownerGlobal;
+    const elem = win.document.getElementById(ONIONLOCATION_BOX_ID);
+    elem.setAttribute("hidden", true);
+  }
+
+  static updateOnionLocationBadge(browser) {
+    if (browser._onionLocation) {
+      OnionLocationParent.setEnabled(browser);
+      OnionLocationParent.showNotification(browser);
+    } else {
+      OnionLocationParent.setDisabled(browser);
+    }
+  }
+}
diff --git a/browser/components/onionservices/content/onionlocation-notification-icons.css b/browser/components/onionservices/content/onionlocation-notification-icons.css
new file mode 100644
index 0000000000000..7c8a6d892c6ff
--- /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 0000000000000..581175fdbf6be
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-urlbar.css
@@ -0,0 +1,60 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+
+#onion-location-box {
+  height: 28px;
+
+  background-color: var(--purple-60);
+  -moz-context-properties: fill;
+  fill: white;
+}
+
+#onion-location-box:hover {
+  background-color: var(--purple-70);
+}
+
+#onion-location-box:active {
+  background-color: var(--purple-80);
+}
+
+ at media (prefers-color-scheme: dark) {
+  #onion-location-box {
+    background-color: var(--purple-50);
+  }
+
+  #onion-location-box:hover {
+    background-color: var(--purple-60);
+  }
+
+  #onion-location-box:active {
+    background-color: var(--purple-70);
+  }
+}
+
+#onion-location-button {
+  list-style-image: url(chrome://browser/content/onionservices/onionlocation.svg);
+  padding-inline-start: 0.5em;
+}
+
+label#onion-label {
+  line-height: 28px;
+  margin: 0;
+  padding-block: 0;
+  padding-inline: 0.5em;
+  color: white;
+  font-weight: normal;
+}
+
+/* set appropriate sizes for the non-standard ui densities */
+:root[uidensity=compact] hbox.urlbar-page-action#onion-location-box {
+  height: 24px;
+}
+:root[uidensity=compact] label#onion-label {
+  line-height: 24px;
+}
+
+:root[uidensity=touch] hbox.urlbar-page-action#onion-location-box {
+  height: 30px;
+}
+:root[uidensity=touch] label#onion-label {
+  line-height: 30px;
+}
diff --git a/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml b/browser/components/onionservices/content/onionlocation-urlbar.inc.xhtml
new file mode 100644
index 0000000000000..b612a4236f3cf
--- /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 0000000000000..37f40ac1812f8
--- /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 0000000000000..c285f403f99bc
--- /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 0000000000000..aa569b54721cd
--- /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 9d6ce88d18416..f45b16dc5d293 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 8156853220244..8027233d65a68 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 18d01fee6b339..66d7ef724f831 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 932d4291e4862..1985b5489fc1c 100644
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -93,6 +93,12 @@ XPCOMUtils.defineLazyScriptGetter(
   "chrome://browser/content/securitylevel/securityLevel.js"
 );
 
+XPCOMUtils.defineLazyScriptGetter(
+  this,
+  ["OnionLocationPreferences"],
+  "chrome://browser/content/onionservices/onionlocationPreferences.js"
+);
+
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "listManager",
@@ -169,6 +175,9 @@ Preferences.addAll([
   // Do not track
   { id: "privacy.donottrackheader.enabled", type: "bool" },
 
+  // Onion Location
+  { id: "privacy.prioritizeonions.enabled", type: "bool" },
+
   // Media
   { id: "media.autoplay.default", type: "int" },
 
@@ -333,6 +342,13 @@ var gPrivacyPane = {
     window.addEventListener("unload", unload);
   },
 
+  /**
+   * Show the OnionLocation preferences UI
+   */
+  _initOnionLocation() {
+    OnionLocationPreferences.init();
+  },
+
   /**
    * Whether the prompt to restart Firefox should appear when changing the autostart pref.
    */
@@ -530,6 +546,7 @@ var gPrivacyPane = {
     this._initTrackingProtectionExtensionControl();
     OnionServicesAuthPreferences.init();
     this._initSecurityLevel();
+    this._initOnionLocation();
 
     Services.telemetry.setEventRecordingEnabled("pwmgr", true);
 
diff --git a/browser/themes/shared/notification-icons.inc.css b/browser/themes/shared/notification-icons.inc.css
index 67dd640baf162..83248f71c60d7 100644
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -449,3 +449,5 @@
   -moz-context-properties: fill;
   fill: var(--panel-banner-item-warning-icon-bgcolor);
 }
+
+%include ../../components/onionservices/content/onionlocation-notification-icons.css
diff --git a/browser/themes/shared/urlbar-searchbar.inc.css b/browser/themes/shared/urlbar-searchbar.inc.css
index f91278ce5ed3b..d7dc7df17f19d 100644
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -746,5 +746,6 @@ moz-input-box > menupopup .context-menu-add-engine > .menu-iconic-left::after {
   opacity: 0.69;
 }
 
+%include ../../components/onionservices/content/onionlocation-urlbar.css
 %include ../../components/torconnect/content/torconnect-urlbar.css
 
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index 5df0e24bec398..7bce87a3cbdb8 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -2779,6 +2779,7 @@ void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
   // mDocumentURI.
   mDocumentBaseURI = nullptr;
   mChromeXHRDocBaseURI = nullptr;
+  mOnionLocationURI = nullptr;
 
   // Check if the current document is the top-level DevTools document.
   // For inner DevTools frames, mIsDevToolsDocument will be set when
@@ -6511,6 +6512,22 @@ void Document::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const {
   }
 }
 
+static bool IsValidOnionLocation(nsIURI* aDocumentURI,
+                                 nsIURI* aOnionLocationURI) {
+  bool isHttpish;
+  nsAutoCString host;
+  return aDocumentURI && aOnionLocationURI &&
+         NS_SUCCEEDED(aDocumentURI->SchemeIs("https", &isHttpish)) &&
+         isHttpish && NS_SUCCEEDED(aDocumentURI->GetAsciiHost(host)) &&
+         !StringEndsWith(host, ".onion"_ns) &&
+         ((NS_SUCCEEDED(aOnionLocationURI->SchemeIs("http", &isHttpish)) &&
+           isHttpish) ||
+          (NS_SUCCEEDED(aOnionLocationURI->SchemeIs("https", &isHttpish)) &&
+           isHttpish)) &&
+         NS_SUCCEEDED(aOnionLocationURI->GetAsciiHost(host)) &&
+         StringEndsWith(host, ".onion"_ns);
+}
+
 void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
   if (!aHeaderField) {
     NS_ERROR("null headerField");
@@ -6585,6 +6602,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,
@@ -10722,7 +10754,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 69e59d09b9245..7ef73651d47fa 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3371,6 +3371,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) {
@@ -4354,6 +4355,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 a139ace11d4a2..d934cf8da0455 100644
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -711,3 +711,11 @@ partial interface Document {
   [ChromeOnly]
   void setNotifyFormOrPasswordRemoved(boolean aShouldNotify);
 };
+
+/**
+ * Extension to allows chrome JS to know whether the document has a valid
+ * Onion-Location that we could redirect to.
+ */
+partial interface Document {
+  [ChromeOnly] readonly attribute URI? onionLocationURI;
+};
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
index 96796d1be1859..8567b4e5a2272 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -10478,6 +10478,11 @@
   value: ""
   mirror: never
 
+- name: privacy.prioritizeonions.enabled
+  type: RelaxedAtomicBool
+  value: false
+  mirror: always
+
 #---------------------------------------------------------------------------
 # Prefs starting with "prompts."
 #---------------------------------------------------------------------------
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
index 2f5be143517ba..f620f57a12135 100644
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -821,6 +821,7 @@ STATIC_ATOMS = [
     Atom("oninputsourceschange", "oninputsourceschange"),
     Atom("oninstall", "oninstall"),
     Atom("oninvalid", "oninvalid"),
+    Atom("headerOnionLocation", "onion-location"),
     Atom("onkeydown", "onkeydown"),
     Atom("onkeypress", "onkeypress"),
     Atom("onkeyup", "onkeyup"),

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the tor-commits mailing list