[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [tor-browser/tor-browser-83.0-10.0-1] Bug 21952: Implement Onion-Location



commit 2ebe7a2bbf9f8f0715d04c6a4ebbbfa46148f078
Author: Alex Catarineu <acat@xxxxxxxxxxxxxx>
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                 |  13 ++
 .../onionservices/OnionLocationChild.jsm           |  39 +++++
 .../onionservices/OnionLocationParent.jsm          | 168 +++++++++++++++++++++
 .../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, 399 insertions(+), 1 deletion(-)

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 3fd7f4b11adc..f090b2ff2ff5 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -45,6 +45,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",
@@ -5455,6 +5456,7 @@ var XULBrowserWindow = {
     Services.obs.notifyObservers(null, "touchbar-location-change", location);
     UpdateBackForwardCommands(gBrowser.webNavigation);
     AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
+    OnionLocationParent.updateOnionLocationBadge(gBrowser.selectedBrowser);
 
     if (!gMultiProcessBrowser) {
       // Bug 1108553 - Cannot rotate images with e10s
@@ -5947,6 +5949,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/browser.xhtml b/browser/base/content/browser.xhtml
index 3b533b9fb3d9..4ddf4053174e 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -1922,6 +1922,9 @@
                        onclick="FullZoom.reset(); FullZoom.resetScalingZoom();"
                        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 cf8c49152b1a..6b7939575c19 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -471,6 +471,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..9e00054ac56c
--- /dev/null
+++ b/browser/components/onionservices/OnionLocationChild.jsm
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, The Tor Project, Inc.
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["OnionLocationChild"];
+
+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;
+      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..f6250e554862
--- /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 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 017a69dc446e..940fbf9737ca 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 71ada9fff391..0f7d8856acdf 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",
@@ -161,6 +167,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" },
 
@@ -307,6 +316,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.
    */
@@ -520,6 +536,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 111732121d21..e152bc9ba85c 100644
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -419,3 +419,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 b9b3b9ddf768..40a06f50d155 100644
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -916,3 +916,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 3533135f59b2..41c29c88a585 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -2712,6 +2712,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
@@ -6203,6 +6204,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");
@@ -6277,6 +6294,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,
@@ -10358,7 +10390,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 4221cd1766df..558ed0ee4cc8 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3358,6 +3358,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) {
@@ -4251,6 +4252,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 fff35b32cf9a..147c46a4cd3b 100644
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -695,3 +695,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 e86bce743fc5..c637bbcda671 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -9148,6 +9148,11 @@
   value: false
   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 7d8130de7841..05540f079dcd 100644
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -816,6 +816,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"),



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits