Commit 4992b8e8 authored by Alex Catarineu's avatar Alex Catarineu Committed by Matthew Finkel
Browse files

Bug 28005: Implement .onion alias urlbar rewrites

A custom HTTPS Everywhere update channel is installed,
which provides rules for locally redirecting some memorable
.tor.onion URLs to non-memorable .onion URLs.

When these redirects occur, we also rewrite the URL in the urlbar
to display the human-memorable hostname instead of the actual
.onion.
parent 69c5e72a
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -156,6 +156,26 @@ class ClickHandlerChild extends ActorChild {
      json.originPrincipal = ownerDoc.nodePrincipal;
      json.triggeringPrincipal = ownerDoc.nodePrincipal;

      // Check if the link needs to be opened with .tor.onion urlbar rewrites
      // allowed. Only when the owner doc has allowOnionUrlbarRewrites = true
      // and the same origin we should allow this.
      json.allowOnionUrlbarRewrites = false;
      if (this.mm.docShell.allowOnionUrlbarRewrites) {
        const sm = Services.scriptSecurityManager;
        try {
          let targetURI = Services.io.newURI(href);
          let isPrivateWin =
            ownerDoc.nodePrincipal.originAttributes.privateBrowsingId > 0;
          sm.checkSameOriginURI(
            docshell.currentDocumentChannel.URI,
            targetURI,
            false,
            isPrivateWin
          );
          json.allowOnionUrlbarRewrites = true;
        } catch (e) {}
      }

      // If a link element is clicked with middle button, user wants to open
      // the link somewhere rather than pasting clipboard content.  Therefore,
      // when it's clicked with middle button, we should prevent multiple
+4 −0
Original line number Diff line number Diff line
@@ -543,6 +543,9 @@ class ContextMenuChild extends ActorChild {
    // The same-origin check will be done in nsContextMenu.openLinkInTab.
    let parentAllowsMixedContent = !!this.docShell.mixedContentChannel;

    let parentAllowsOnionUrlbarRewrites = this.docShell
      .allowOnionUrlbarRewrites;

    // Get referrer attribute from clicked link and parse it
    let referrerAttrValue = Services.netUtils.parseAttributePolicyString(
      aEvent.composedTarget.getAttribute("referrerpolicy")
@@ -659,6 +662,7 @@ class ContextMenuChild extends ActorChild {
      popupNodeSelectors,
      disableSetDesktopBg,
      parentAllowsMixedContent,
      parentAllowsOnionUrlbarRewrites,
    };

    Services.obs.notifyObservers(
+8 −4
Original line number Diff line number Diff line
@@ -440,7 +440,8 @@ var PlacesCommandHook = {
   */
  async bookmarkPage() {
    let browser = gBrowser.selectedBrowser;
    let url = new URL(browser.currentURI.spec);
    const uri = browser.currentOnionAliasURI || browser.currentURI;
    let url = new URL(uri.spec);
    let info = await PlacesUtils.bookmarks.fetch({ url });
    let isNewBookmark = !info;
    let showEditUI = !isNewBookmark || StarUI.showForNewBookmarks;
@@ -544,7 +545,7 @@ var PlacesCommandHook = {

    tabs.forEach(tab => {
      let browser = tab.linkedBrowser;
      let uri = browser.currentURI;
      let uri = browser.currentOnionAliasURI || browser.currentURI;
      let title = browser.contentTitle || tab.label;
      let spec = uri.spec;
      if (!(spec in uniquePages)) {
@@ -1618,14 +1619,17 @@ var BookmarkingUI = {
  },

  onLocationChange: function BUI_onLocationChange() {
    if (this._uri && gBrowser.currentURI.equals(this._uri)) {
    const uri =
      gBrowser.selectedBrowser.currentOnionAliasURI || gBrowser.currentURI;
    if (this._uri && uri.equals(this._uri)) {
      return;
    }
    this.updateStarState();
  },

  updateStarState: function BUI_updateStarState() {
    this._uri = gBrowser.currentURI;
    this._uri =
      gBrowser.selectedBrowser.currentOnionAliasURI || gBrowser.currentURI;
    this._itemGuids.clear();
    let guids = new Set();

+35 −2
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
  TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
  TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
  Translation: "resource:///modules/translation/Translation.jsm",
  OnionAliasStore: "resource:///modules/OnionAliasStore.jsm",
  UITour: "resource:///modules/UITour.jsm",
  UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
  UrlbarInput: "resource:///modules/UrlbarInput.jsm",
@@ -3336,7 +3337,10 @@ function URLBarSetURI(aURI, updatePopupNotifications) {
  // bar if the user has deleted the URL and we'd just put the same URL
  // back. See bug 304198.
  if (value === null) {
    let uri = aURI || gBrowser.currentURI;
    let uri =
      aURI ||
      gBrowser.selectedBrowser.currentOnionAliasURI ||
      gBrowser.currentURI;
    // Strip off usernames and passwords for the location bar
    try {
      uri = Services.uriFixup.createExposableURI(uri);
@@ -5898,11 +5902,24 @@ var XULBrowserWindow = {
        this.reloadCommand.removeAttribute("disabled");
      }

      // The onion memorable alias needs to be used in URLBarSetURI, but also in
      // other parts of the code (like the bookmarks UI), so we save it.
      if (gBrowser.selectedBrowser.allowOnionUrlbarRewrites) {
        gBrowser.selectedBrowser.currentOnionAliasURI = OnionAliasStore.getShortURI(
          aLocationURI
        );
      } else {
        gBrowser.selectedBrowser.currentOnionAliasURI = null;
      }

      // We want to update the popup visibility if we received this notification
      // via simulated locationchange events such as switching between tabs, however
      // if this is a document navigation then PopupNotifications will be updated
      // via TabsProgressListener.onLocationChange and we do not want it called twice
      URLBarSetURI(aLocationURI, aIsSimulated);
      URLBarSetURI(
        gBrowser.selectedBrowser.currentOnionAliasURI || aLocationURI,
        aIsSimulated
      );

      BookmarkingUI.onLocationChange();

@@ -7639,6 +7656,21 @@ function handleLinkClick(event, href, linkNode) {
    } catch (e) {}
  }

  // Check if the link needs to be opened with .tor.onion urlbar rewrites
  // allowed. Only when the owner doc has allowOnionUrlbarRewrites = true
  // and the same origin we should allow this.
  let persistAllowOnionUrlbarRewritesInChildTab = false;
  if (where == "tab" && gBrowser.docShell.allowOnionUrlbarRewrites) {
    const sm = Services.scriptSecurityManager;
    try {
      let tURI = makeURI(href);
      let isPrivateWin =
        doc.nodePrincipal.originAttributes.privateBrowsingId > 0;
      sm.checkSameOriginURI(referrerURI, tURI, false, isPrivateWin);
      persistAllowOnionUrlbarRewritesInChildTab = true;
    } catch (e) {}
  }

  // first get document wide referrer policy, then
  // get referrer attribute from clicked link and parse it and
  // allow per element referrer to overrule the document wide referrer if enabled
@@ -7671,6 +7703,7 @@ function handleLinkClick(event, href, linkNode) {
    triggeringPrincipal: doc.nodePrincipal,
    csp,
    frameOuterWindowID,
    allowOnionUrlbarRewrites: persistAllowOnionUrlbarRewritesInChildTab,
  };

  // The new tab/window must use the same userContextId
+18 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ function openContextMenu(aMessage) {
    disableSetDesktopBackground: data.disableSetDesktopBg,
    loginFillInfo: data.loginFillInfo,
    parentAllowsMixedContent: data.parentAllowsMixedContent,
    parentAllowsOnionUrlbarRewrites: data.parentAllowsOnionUrlbarRewrites,
    userContextId: data.userContextId,
    webExtContextData: data.webExtContextData,
  };
@@ -1122,9 +1123,26 @@ nsContextMenu.prototype = {
      } catch (e) {}
    }

    // Check if the link needs to be opened with .tor.onion urlbar rewrites
    // allowed. Only when parent has allowOnionUrlbarRewrites = true
    // and the same origin we should allow this.
    let persistAllowOnionUrlbarRewrites = false;

    if (gContextMenuContentData.parentAllowsOnionUrlbarRewrites) {
      const sm = Services.scriptSecurityManager;
      try {
        let targetURI = this.linkURI;
        let isPrivateWin =
          this.browser.contentPrincipal.originAttributes.privateBrowsingId > 0;
        sm.checkSameOriginURI(referrerURI, targetURI, false, isPrivateWin);
        persistAllowOnionUrlbarRewrites = true;
      } catch (e) {}
    }

    let params = {
      allowMixedContent: persistAllowMixedContentInChildTab,
      userContextId: parseInt(event.target.getAttribute("data-usercontextid")),
      allowOnionUrlbarRewrites: persistAllowOnionUrlbarRewrites,
    };

    openLinkIn(this.linkURL, "tab", this._openLinkInParameters(params));
Loading