[tbb-commits] [tor-browser/tor-browser-68.6.0esr-9.5-1] Bug 21952: Implement Onion-Location

sysrqb at torproject.org sysrqb at torproject.org
Fri Apr 3 02:22:07 UTC 2020


commit da5513527e50e7f13e3b1c3206ed75ff8fbd76db
Author: Alex Catarineu <acat at 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.xul                   |   3 +
 browser/components/BrowserGlue.jsm                 |  10 ++
 .../onionservices/OnionLocationChild.jsm           |  43 ++++++
 .../onionservices/OnionLocationParent.jsm          | 161 +++++++++++++++++++++
 .../content/onionlocation-notification-icons.css   |   5 +
 .../onionservices/content/onionlocation-urlbar.css |  27 ++++
 .../content/onionlocation-urlbar.inc.xul           |  10 ++
 .../onionservices/content/onionlocation.svg        |   3 +
 .../content/onionlocationPreferences.inc.xul       |  11 ++
 .../content/onionlocationPreferences.js            |  31 ++++
 browser/components/onionservices/jar.mn            |   2 +
 browser/components/onionservices/moz.build         |   5 +
 .../components/preferences/in-content/privacy.js   |  17 +++
 .../components/preferences/in-content/privacy.xul  |   2 +
 browser/modules/TorStrings.jsm                     |  39 +++++
 browser/themes/shared/notification-icons.inc.css   |   2 +
 browser/themes/shared/urlbar-searchbar.inc.css     |   2 +
 dom/base/Document.cpp                              |  32 +++-
 dom/base/Document.h                                |   2 +
 dom/webidl/Document.webidl                         |   9 ++
 modules/libpref/init/StaticPrefList.h              |   7 +
 xpcom/ds/StaticAtoms.py                            |   1 +
 23 files changed, 435 insertions(+), 1 deletion(-)

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 3f9bf006f562..410c8f62d0ef 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -43,6 +43,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",
@@ -5934,6 +5935,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
@@ -6581,6 +6583,16 @@ 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.xul b/browser/base/content/browser.xul
index d2f72eea8edb..4309a0b71228 100644
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -992,6 +992,9 @@
                        onclick="FullZoom.reset();"
                        tooltip="dynamic-shortcut-tooltip"
                        hidden="true"/>
+
+#include ../../components/onionservices/content/onionlocation-urlbar.inc.xul
+
                 <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 4e851289fbe9..fd2cbfe1d64f 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -216,6 +216,14 @@ let LEGACY_ACTORS = {
     },
   },
 
+  OnionLocation: {
+    child: {
+      module: "resource:///modules/OnionLocationChild.jsm",
+      events: { pageshow: {} },
+      messages: ["OnionLocation:Refresh"],
+    },
+  },
+
   PageInfo: {
     child: {
       module: "resource:///actors/PageInfoChild.jsm",
@@ -507,6 +515,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
   ContentClick: "resource:///modules/ContentClick.jsm",
   FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
   LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
+  OnionLocationParent: "resource:///modules/OnionLocationParent.jsm",
   PictureInPicture: "resource://gre/modules/PictureInPicture.jsm",
   ReaderParent: "resource:///modules/ReaderParent.jsm",
   RemotePrompt: "resource:///modules/RemotePrompt.jsm",
@@ -605,6 +614,7 @@ const listeners = {
     ContentSearch: ["ContentSearch"],
     "FormValidation:ShowPopup": ["FormValidationHandler"],
     "FormValidation:HidePopup": ["FormValidationHandler"],
+    "OnionLocation:Set": ["OnionLocationParent"],
     "PictureInPicture:Request": ["PictureInPicture"],
     "PictureInPicture:Close": ["PictureInPicture"],
     "PictureInPicture:Playing": ["PictureInPicture"],
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/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
+    );
+  },
+
+  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.xul b/browser/components/onionservices/content/onionlocation-urlbar.inc.xul
new file mode 100644
index 000000000000..b612a4236f3c
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocation-urlbar.inc.xul
@@ -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.xul b/browser/components/onionservices/content/onionlocationPreferences.inc.xul
new file mode 100644
index 000000000000..c386316c98dc
--- /dev/null
+++ b/browser/components/onionservices/content/onionlocationPreferences.inc.xul
@@ -0,0 +1,11 @@
+# Copyright (c) 2020, The Tor Project, Inc.
+
+<groupbox id="onionServicesGroup" data-category="panePrivacy" data-subcategory="onionservices">
+  <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 583ab77bc6d8..d536e172721c 100644
--- a/browser/components/onionservices/jar.mn
+++ b/browser/components/onionservices/jar.mn
@@ -6,3 +6,5 @@ browser.jar:
     content/browser/onionservices/onionservices.css                (content/onionservices.css)
     content/browser/onionservices/savedKeysDialog.js               (content/savedKeysDialog.js)
     content/browser/onionservices/savedKeysDialog.xul              (content/savedKeysDialog.xul)
+    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 7e103239c8d6..bf276c4a3c4c 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/in-content/privacy.js b/browser/components/preferences/in-content/privacy.js
index ee86b4158d7c..015ee77932b7 100644
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -75,6 +75,12 @@ XPCOMUtils.defineLazyScriptGetter(
   "chrome://browser/content/securitylevel/securityLevel.js"
 );
 
+XPCOMUtils.defineLazyScriptGetter(
+  this,
+  ["OnionLocationPreferences"],
+  "chrome://browser/content/onionservices/onionlocationPreferences.js"
+);
+
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "listManager",
@@ -124,6 +130,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" },
 
@@ -251,6 +260,13 @@ var gPrivacyPane = {
   },
 
   /**
+   * Show the OnionLocation preferences UI
+   */
+  _initOnionLocation() {
+    OnionLocationPreferences.init();
+  },
+
+  /**
    * Whether the prompt to restart Firefox should appear when changing the autostart pref.
    */
   _shouldPromptForRestart: true,
@@ -377,6 +393,7 @@ var gPrivacyPane = {
     this._initTrackingProtectionExtensionControl();
     OnionServicesAuthPreferences.init();
     this._initSecurityLevel();
+    this._initOnionLocation();
 
     Services.telemetry.setEventRecordingEnabled("pwmgr", true);
 
diff --git a/browser/components/preferences/in-content/privacy.xul b/browser/components/preferences/in-content/privacy.xul
index e807ac69f1f1..05c4966a1e59 100644
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -15,6 +15,8 @@
   <html:h1 data-l10n-id="privacy-header"/>
 </hbox>
 
+#include ../../onionservices/content/onionlocationPreferences.inc.xul
+
 <!-- Tracking / Content Blocking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" aria-describedby="contentBlockingDescription">
   <label id="contentBlockingHeader"><html:h2 data-l10n-id="content-blocking-header"/></label>
diff --git a/browser/modules/TorStrings.jsm b/browser/modules/TorStrings.jsm
index e9a8b3969297..34b13f33a96a 100644
--- a/browser/modules/TorStrings.jsm
+++ b/browser/modules/TorStrings.jsm
@@ -359,6 +359,45 @@ var TorStrings = {
     return retval;
   })() /* Tor Onion Services Strings */,
 
+  /*
+    OnionLocation
+  */
+  onionLocation: (function() {
+    const tsb = new TorPropertyStringBundle(
+      ["chrome://torbutton/locale/torbutton.properties"],
+      "onionLocation."
+    );
+    const getString = function(key, fallback) {
+      return tsb.getString(key, fallback);
+    };
+
+    const retval = {
+      alwaysPrioritize: getString(
+        "alwaysPrioritize",
+        "Always Prioritize Onionsites"
+      ),
+      alwaysPrioritizeAccessKey: getString("alwaysPrioritizeAccessKey", "a"),
+      notNow: getString("notNow", "Not Now"),
+      notNowAccessKey: getString("notNowAccessKey", "n"),
+      description: getString(
+        "description",
+        "Website publishers can protect users by adding a security layer. This prevents eavesdroppers from knowing that you are the one visiting that website."
+      ),
+      tryThis: getString("tryThis", "Try this: Onionsite"),
+      onionAvailable: getString("onionAvailable", "Onionsite available"),
+      learnMore: getString("learnMore", "Learn more"),
+      learnMoreURL: `https://tb-manual.torproject.org/${getLocale()}`, // TODO: replace when manual page is available.
+      always: getString("always", "Always"),
+      askEverytime: getString("askEverytime", "Ask you every time"),
+      prioritizeOnionsDescription: getString(
+        "prioritizeOnionsDescription",
+        "Prioritize onionsites when they are available."
+      ),
+      onionServicesTitle: getString("onionServicesTitle", "Onion Services"),
+    };
+
+    return retval;
+  })() /* OnionLocation */,
 
   /*
     Tor Deamon Configuration Key Strings
diff --git a/browser/themes/shared/notification-icons.inc.css b/browser/themes/shared/notification-icons.inc.css
index d35f292a0860..afe67decceaf 100644
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -393,3 +393,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 a13d402f17e3..40d42921db30 100644
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -789,3 +789,5 @@
 #urlbar.hidden-focus[focused="true"] {
   caret-color: transparent;
 }
+
+%include ../../components/onionservices/content/onionlocation-urlbar.css
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index 95b95c9fd69e..994f52659265 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -2078,6 +2078,7 @@ void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
   // mDocumentURI.
   mDocumentBaseURI = nullptr;
   mChromeXHRDocBaseURI = nullptr;
+  mOnionLocationURI = nullptr;
 
   if (aLoadGroup) {
     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
@@ -3731,6 +3732,21 @@ void Document::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const {
   }
 }
 
+static bool IsValidOnionLocation(nsIURI* aDocumentURI,
+                                 nsIURI* aOnionLocationURI) {
+  bool isHttpish;
+  nsAutoCString onionHost;
+  return aDocumentURI && aOnionLocationURI &&
+         NS_SUCCEEDED(aDocumentURI->SchemeIs("https", &isHttpish)) &&
+         isHttpish &&
+         ((NS_SUCCEEDED(aOnionLocationURI->SchemeIs("http", &isHttpish)) &&
+           isHttpish) ||
+          (NS_SUCCEEDED(aOnionLocationURI->SchemeIs("https", &isHttpish)) &&
+           isHttpish)) &&
+         NS_SUCCEEDED(aOnionLocationURI->GetAsciiHost(onionHost)) &&
+         StringEndsWith(onionHost, NS_LITERAL_CSTRING(".onion"));
+}
+
 void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
   if (!aHeaderField) {
     NS_ERROR("null headerField");
@@ -3834,6 +3850,20 @@ void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
       mReferrerPolicySet = true;
     }
   }
+  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()) {
+        nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
+        if (refresher) {
+          refresher->RefreshURI(onionURI, NodePrincipal(), 0, false, true);
+        }
+      } else {
+        mOnionLocationURI = onionURI;
+      }
+    }
+  }
 }
 void Document::TryChannelCharset(nsIChannel* aChannel, int32_t& aCharsetSource,
                                  NotNull<const Encoding*>& aEncoding,
@@ -7468,7 +7498,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", "referrer-policy",
+        "x-frame-options", "referrer-policy", "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 58d09afba815..43f8517fcd65 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3291,6 +3291,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
   bool FullscreenEnabled(CallerType aCallerType);
   Element* FullscreenStackTop();
@@ -3998,6 +3999,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 0170a17fbf96..a281b04a0960 100644
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -589,3 +589,12 @@ partial interface Document {
   [ChromeOnly]
   void setRDMPaneOrientation(OrientationType type, float rotationAngle);
 };
+
+
+/**
+ * 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.h b/modules/libpref/init/StaticPrefList.h
index 47f4626f7e6b..158952041d3d 100644
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -2555,6 +2555,13 @@ VARCACHE_PREF(
 )
 #undef PREF_VALUE
 
+// Automatically redirect to Onion-Location header, disabled by default
+VARCACHE_PREF(
+  "privacy.prioritizeonions.enabled",
+   privacy_prioritizeOnions,
+  RelaxedAtomicBool, false
+)
+
 // Anti-fingerprinting, disabled by default
 VARCACHE_PREF(
   "privacy.resistFingerprinting",
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
index 9a182d3bf6d0..71fffc55f1b8 100644
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -803,6 +803,7 @@ STATIC_ATOMS = [
     Atom("oninput", "oninput"),
     Atom("oninstall", "oninstall"),
     Atom("oninvalid", "oninvalid"),
+    Atom("headerOnionLocation", "onion-location"),
     Atom("onkeydown", "onkeydown"),
     Atom("onkeypress", "onkeypress"),
     Atom("onkeyup", "onkeyup"),





More information about the tbb-commits mailing list