Bug 1589189 - Update noreferrer code. r=frg a=frg CLOSED TREE
authorIan Neal <iann_cvs@blueyonder.co.uk>
Tue, 19 Nov 2019 17:50:29 +0100
changeset 32368 78af4c1422a872a7c3c53ca7fcdec32f0719fe2c
parent 32367 c36e650c405d6193471f8f7a3ca338f67afda8a8
child 32369 e0970057c304ce9c645f1107cb224c297202cd98
push id230
push userfrgrahl@gmx.net
push dateTue, 19 Nov 2019 16:51:10 +0000
treeherdercomm-esr60@232db96d678a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrg, frg
bugs1589189
Bug 1589189 - Update noreferrer code. r=frg a=frg CLOSED TREE
suite/base/content/contentAreaClick.js
suite/base/content/contentAreaContextOverlay.xul
suite/base/content/nsContextMenu.js
suite/base/content/utilityOverlay.js
suite/browser/navigator.js
suite/browser/tabbrowser.xml
suite/mailnews/content/mailWindowOverlay.xul
--- a/suite/base/content/contentAreaClick.js
+++ b/suite/base/content/contentAreaClick.js
@@ -121,20 +121,32 @@ function handleLinkClick(event, href, li
       var targetURI = makeURI(href);
       sm.checkSameOriginURI(referrerURI, targetURI, false);
       persistAllowMixedContentInChildTab = true;
     }
     catch (e) { }
   }
 
   urlSecurityCheck(href, doc.nodePrincipal);
-  openLinkIn(href, where, { referrerURI: referrerURI,
-                            charset: doc.characterSet,
-                            private: gPrivate ? true : false,
-                            allowMixedContent: persistAllowMixedContentInChildTab });
+  let params = {
+    charset: doc.characterSet,
+    private: gPrivate ? true : false,
+    allowMixedContent: persistAllowMixedContentInChildTab,
+    referrerURI: referrerURI,
+    noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
+    originPrincipal: doc.nodePrincipal,
+    triggeringPrincipal: doc.nodePrincipal,
+  };
+
+  // The new tab/window must use the same userContextId
+  if (doc.nodePrincipal.originAttributes.userContextId) {
+    params.userContextId = doc.nodePrincipal.originAttributes.userContextId;
+  }
+
+  openLinkIn(href, where, params);
   event.preventDefault();
   return true;
 }
 
   var gURIFixup = null;
 
   function middleMousePaste(event) {
 
--- a/suite/base/content/contentAreaContextOverlay.xul
+++ b/suite/base/content/contentAreaContextOverlay.xul
@@ -54,16 +54,17 @@
       <menuitem id="spell-ignore-word"
                 label="&spellIgnoreWord.label;"
                 accesskey="&spellIgnoreWord.accesskey;"
                 oncommand="InlineSpellCheckerUI.ignoreWord();"/>
       <menuseparator id="spell-suggestions-separator"/>
       <menuitem id="context-openlinkintab"
                 label="&openLinkCmdInTab.label;"
                 accesskey="&openLinkCmdInTab.accesskey;"
+                usercontextid="0"
                 oncommand="gContextMenu.openLinkInTab(event);"/>
       <menuitem id="context-openlink"
                 label="&openLinkCmd.label;"
                 accesskey="&openLinkCmd.accesskey;"
                 oncommand="gContextMenu.openLinkInWindow();"/>
       <menuitem id="context-openlinkinprivatewindow"
                 label="&openLinkCmdInPrivateWindow.label;"
                 accesskey="&openLinkCmdInPrivateWindow.accesskey;"
--- a/suite/base/content/nsContextMenu.js
+++ b/suite/base/content/nsContextMenu.js
@@ -8,16 +8,17 @@
 |   context menu.                                                              |
 |                                                                              |
 |   For usage, see references to this class in navigator.xul.                  |
 |                                                                              |
 |   Currently, this code is relatively useless for any other purpose.  In the  |
 |   longer term, this code will be restructured to make it more reusable.      |
 ------------------------------------------------------------------------------*/
 
+ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm");
 ChromeUtils.import("resource://gre/modules/LoginManagerContextMenu.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "InlineSpellCheckerUI", function() {
   return new InlineSpellChecker();
 });
@@ -614,16 +615,17 @@ nsContextMenu.prototype = {
     this.onLink                = false;
     this.onMailtoLink          = false;
     this.onSaveableLink        = false;
     this.inDirList             = false;
     this.link                  = null;
     this.linkURL               = "";
     this.linkURI               = null;
     this.linkProtocol          = "";
+    this.linkHasNoReferrer     = false;
     this.onMathML              = false;
     this.inFrame               = false;
     this.inSyntheticDoc        = false;
     this.hasBGImage            = false;
     this.bgImageURL            = "";
     this.popupPrincipal        = null;
     this.autoDownload          = false;
     this.isTextSelected        = false;
@@ -787,16 +789,17 @@ nsContextMenu.prototype = {
           // Remember corresponding element.
           this.link = elem;
           this.linkURL = this.getLinkURL();
           this.linkURI = this.getLinkURI();
           this.linkProtocol = this.getLinkProtocol();
           this.onMailtoLink = (this.linkProtocol == "mailto");
           // Remember if it is saveable.
           this.onSaveableLink = this.isLinkSaveable();
+          this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);
         }
 
         // Text input?
         if (!this.onTextInput) {
           // Clicked on a link.
           this.onTextInput = this.isTargetATextBox(elem);
         }
 
@@ -964,85 +967,99 @@ nsContextMenu.prototype = {
   toggleImageBlocking: function(aBlock) {
   const uri = Services.io.newURI(this.mediaURL);
   if (aBlock)
     Services.perms.add(uri, "image", Services.perms.DENY_ACTION);
   else
     Services.perms.remove(uri, "image");
   },
 
+  _openLinkInParameters : function (extra) {
+    let params = { charset: gContextMenuContentData.charSet,
+                   originPrincipal: this.principal,
+                   triggeringPrincipal: this.principal,
+                   referrerURI: gContextMenuContentData.documentURIObject,
+                   referrerPolicy: gContextMenuContentData.referrerPolicy,
+                   noReferrer: this.linkHasNoReferrer || this.onPlainTextLink };
+    for (let p in extra) {
+      params[p] = extra[p];
+    }
+
+    // If we want to change userContextId, we must be sure that we don't
+    // propagate the referrer.
+    if ("userContextId" in params &&
+        params.userContextId != this.principal.originAttributes.userContextId) {
+      params.noReferrer = true;
+    }
+
+    return params;
+  },
+
   // Open linked-to URL in a new tab.
   openLinkInTab: function(aEvent) {
-    var doc = this.target.ownerDocument;
-    urlSecurityCheck(this.linkURL, doc.nodePrincipal);
-    var referrerURI = doc.documentURIObject;
+    urlSecurityCheck(this.linkURL, this.principal);
+    let referrerURI = gContextMenuContentData.documentURIObject;
 
     // if the mixedContentChannel is present and the referring URI passes
     // a same origin check with the target URI, we can preserve the users
     // decision of disabling MCB on a page for it's child tabs.
-    var persistAllowMixedContentInChildTab = false;
+    let persistAllowMixedContentInChildTab = false;
 
     if (this.browser.docShell.mixedContentChannel) {
       const sm = Services.scriptSecurityManager;
       try {
-        var targetURI = this.linkURI;
+        let targetURI = this.linkURI;
         sm.checkSameOriginURI(referrerURI, targetURI, false);
         persistAllowMixedContentInChildTab = true;
       }
       catch (e) { }
     }
 
+    let params = {
+      allowMixedContent: persistAllowMixedContentInChildTab,
+      userContextId: parseInt(aEvent.target.getAttribute('usercontextid')),
+    };
+
     openLinkIn(this.linkURL,
                aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
-               { charset: doc.characterSet,
-                 referrerURI: referrerURI,
-                 allowMixedContent: persistAllowMixedContentInChildTab });
+               this._openLinkInParameters(params));
   },
 
   // Open linked-to URL in a new window.
   openLinkInWindow: function() {
-    var doc = this.target.ownerDocument;
-    urlSecurityCheck(this.linkURL, doc.nodePrincipal);
-    openLinkIn(this.linkURL, "window",
-               { charset: doc.characterSet,
-                 referrerURI: doc.documentURIObject });
+    urlSecurityCheck(this.linkURL, this.principal);
+    openLinkIn(this.linkURL, "window", this._openLinkInParameters());
   },
 
   // Open linked-to URL in a private window.
   openLinkInPrivateWindow: function() {
-    var doc = this.target.ownerDocument;
-    urlSecurityCheck(this.linkURL, doc.nodePrincipal);
+    urlSecurityCheck(this.linkURL, this.principal);
     openLinkIn(this.linkURL, "window",
-               { charset: doc.characterSet,
-                 referrerURI: doc.documentURIObject,
-                 private: true });
+               this._openLinkInParameters({ private: true }));
   },
 
   // Open frame in a new tab.
   openFrameInTab: function(aEvent) {
-    var doc = this.target.ownerDocument;
-    var frameURL = doc.location.href;
-    var referrer = doc.referrer;
-    openLinkIn(frameURL, aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
-               { charset: doc.characterSet,
+    let referrer = gContextMenuContentData.referrer;
+    openLinkIn(gContextMenuContentData.docLocation,
+               aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
+               { charset: gContextMenuContentData.charSet,
                  referrerURI: referrer ? makeURI(referrer) : null });
   },
 
   // Reload clicked-in frame.
   reloadFrame: function() {
     this.target.ownerDocument.location.reload();
   },
 
   // Open clicked-in frame in its own window.
   openFrame: function() {
-    var doc = this.target.ownerDocument;
-    var frameURL = doc.location.href;
-    var referrer = doc.referrer;
-    openLinkIn(frameURL, "window",
-               { charset: doc.characterSet,
+    let referrer = gContextMenuContentData.referrer;
+    openLinkIn(gContextMenuContentData.docLocation, "window",
+               { charset: gContextMenuContentData.charSet,
                  referrerURI: referrer ? makeURI(referrer) : null });
   },
 
   printFrame: function() {
     PrintUtils.printWindow(gContextMenuContentData.frameOuterWindowID,
                            this.browser);
   },
 
@@ -1174,17 +1191,17 @@ nsContextMenu.prototype = {
   // Save URL of clicked-on frame.
   saveFrame: function() {
     saveDocument(this.target.ownerDocument, true);
   },
 
   // Save URL of clicked-on link.
   saveLink: function() {
     var doc = this.target.ownerDocument;
-    urlSecurityCheck(this.linkURL, this.target.nodePrincipal);
+    urlSecurityCheck(this.linkURL, this.principal);
     this.saveHelper(this.linkURL, this.linkText(), null, true, doc);
   },
 
   // Helper function to wait for appropriate MIME-type headers and
   // then prompt the user with a file picker
   saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc) {
     // canonical def in nsURILoader.h
     const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;
@@ -1312,22 +1329,24 @@ nsContextMenu.prototype = {
     let referrerURI = doc.documentURIObject;
 
     if (this.onCanvas)
       // Bypass cache, since it's a data: URL.
       saveImageURL(this.target.toDataURL(), "canvas.png", "SaveImageTitle",
                    true, false, referrerURI, null, null, null,
                    (gPrivate ? true : false));
     else if (this.onImage) {
+      urlSecurityCheck(this.mediaURL, this.principal);
       saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
                    false, referrerURI, null, gContextMenuContentData.contentType,
                    gContextMenuContentData.contentDisposition,
                    (gPrivate ? true : false));
     }
     else if (this.onVideo || this.onAudio) {
+      urlSecurityCheck(this.mediaURL, this.principal);
       var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
       this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
     }
   },
 
   // Backwards-compatibility wrapper
   saveImage: function() {
     if (this.onCanvas || this.onImage)
--- a/suite/base/content/utilityOverlay.js
+++ b/suite/base/content/utilityOverlay.js
@@ -1004,42 +1004,45 @@ function openAsExternal(aURL) {
  *        in the background), or null.
  * @param aAllowThirdPartyFixup
  *        If true, then we allow the URL text to be sent to third party
  *        services (e.g., Google's I Feel Lucky) for interpretation. This
  *        parameter may be undefined in which case it is treated as false.
  * @param [optional] aReferrer
  *        If aDocument is null, then this will be used as the referrer.
  *        There will be no security check.
+ * @param [optional] aReferrerPolicy
+ *        Referrer policy - Ci.nsIHttpChannel.REFERRER_POLICY_*.
  */
 function openNewPrivateWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
-                            aReferrer) {
+                            aReferrer, aReferrerPolicy) {
   return openNewTabWindowOrExistingWith(aURL, "private", aDocument, null,
                                         aPostData, aAllowThirdPartyFixup,
-                                        aReferrer);
+                                        aReferrer, aReferrerPolicy);
 }
 
 function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
-                           aReferrer) {
+                           aReferrer, aReferrerPolicy) {
   return openNewTabWindowOrExistingWith(aURL, "window", aDocument, null,
                                         aPostData, aAllowThirdPartyFixup,
-                                        aReferrer);
+                                        aReferrer, aReferrerPolicy);
 }
 
 function openNewTabWith(aURL, aDocument, aPostData, aEvent,
-                        aAllowThirdPartyFixup, aReferrer) {
+                        aAllowThirdPartyFixup, aReferrer, aReferrerPolicy) {
   let where = aEvent && aEvent.shiftKey ? "tabshifted" : "tab";
   return openNewTabWindowOrExistingWith(aURL, where, aDocument, null,
                                         aPostData, aAllowThirdPartyFixup,
-                                        aReferrer);
+                                        aReferrer, aReferrerPolicy);
 }
 
 function openNewTabWindowOrExistingWith(aURL, aWhere, aDocument,
                                         aLoadInBackground, aPostData,
-                                        aAllowThirdPartyFixup, aReferrer) {
+                                        aAllowThirdPartyFixup, aReferrer,
+                                        aReferrerPolicy) {
   // Make sure we are allowed to open this url
   if (aDocument)
     urlSecurityCheck(aURL, aDocument.nodePrincipal);
 
   // Where appropriate we want to pass the charset of the
   // current document over to a new tab / window.
   var originCharset = null;
   if (aWhere != "current") {
@@ -1056,17 +1059,18 @@ function openNewTabWindowOrExistingWith(
   }
   var referrerURI = aDocument ? aDocument.documentURIObject : aReferrer;
   return openLinkIn(aURL, aWhere,
                     { charset: originCharset,
                       postData: aPostData,
                       inBackground: aLoadInBackground,
                       allowThirdPartyFixup: aAllowThirdPartyFixup,
                       referrerURI: referrerURI,
-                      private: isPrivate });
+                      referrerPolicy: aReferrerPolicy,
+                      private: isPrivate, });
 }
 
 /**
  * Handle command events bubbling up from error page content
  * called from oncommand by <browser>s that support error pages
  */
 function BrowserOnCommand(event)
 {
@@ -1334,33 +1338,34 @@ function togglePaneSplitter(aSplitterId)
  * accepted by openUILinkIn, plus "ignoreButton" and "ignoreSave".
  *
  * Note: Firefox uses aIgnoreAlt while SeaMonkey uses aIgnoreSave because in
  * SeaMonkey, Save can be Alt or Shift depending on ui.key.saveLink.shift.
  *
  * For API compatibility with Firefox the object version uses params.ignoreAlt
  * although for SeaMonkey it is effectively ignoreSave.
  */
-function openUILink(url, aEvent, aIgnoreButton, aIgnoreSave, aAllowThirdPartyFixup, aPostData, aReferrerURI)
-{
+function openUILink(url, aEvent, aIgnoreButton, aIgnoreSave,
+                    aAllowThirdPartyFixup, aPostData, aReferrerURI) {
   var params;
   if (aIgnoreButton && typeof aIgnoreButton == "object") {
     params = aIgnoreButton;
 
     // don't forward "ignoreButton" and "ignoreSave" to openUILinkIn.
     aIgnoreButton = params.ignoreButton;
     aIgnoreSave = params.ignoreAlt;
     delete params.ignoreButton;
     delete params.ignoreAlt;
   }
   else {
     params = {allowThirdPartyFixup: aAllowThirdPartyFixup,
               postData: aPostData,
               referrerURI: aReferrerURI,
-              initiatingDoc: aEvent ? aEvent.target.ownerDocument : document}
+              referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+              initiatingDoc: aEvent ? aEvent.target.ownerDocument : document,}
   }
 
   var where = whereToOpenLink(aEvent, aIgnoreButton, aIgnoreSave);
   return openUILinkIn(url, where, params);
 }
 
 /* whereToOpenLink() looks at an event to decide where to open a link.
  *
@@ -1483,22 +1488,26 @@ function openLinkIn(url, where, params)
   var aReferrerPolicy       = ("referrerPolicy" in params ?
         params.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
   var aRelatedToCurrent     = params.relatedToCurrent;
   var aAllowMixedContent    = params.allowMixedContent;
   var aInBackground         = params.inBackground;
   var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
   var aInitiatingDoc = params.initiatingDoc ? params.initiatingDoc : document;
   var aIsPrivate            = params.private;
+  var aNoReferrer           = params.noReferrer;
   var aUserContextId        = params.userContextId;
   var aPrincipal            = params.originPrincipal;
   var aTriggeringPrincipal  = params.triggeringPrincipal;
+  var aForceAboutBlankViewerInCurrent =
+        params.forceAboutBlankViewerInCurrent;
 
   if (where == "save") {
-    saveURL(url, null, null, true, true, aReferrerURI, aInitiatingDoc);
+    saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI,
+            aInitiatingDoc);
     return null;
   }
 
   // Establish which window we'll load the link in.
   var w = getTopWin();
   // We don't want to open tabs in popups, so try to find a non-popup window in
   // that case.
   if ((where == "tab" || where == "tabshifted") && w && !w.toolbar.visible) {
@@ -1520,19 +1529,21 @@ function openLinkIn(url, where, params)
     }
     return principal;
   }
   aPrincipal = useOAForPrincipal(aPrincipal);
   aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);
 
   if (!w || where == "window") {
     let features = "chrome,dialog=no,all";
-
     if (aIsPrivate) {
       features += ",private";
+      // To prevent regular browsing data from leaking to private browsing
+      // sites, strip the referrer when opening a new private window.
+      aNoReferrer = true;
     }
 
     // This propagates to window.arguments.
     var sa = Cc["@mozilla.org/array;1"].
              createInstance(Ci.nsIMutableArray);
 
     var wuri = Cc["@mozilla.org/supports-string;1"].
                createInstance(Ci.nsISupportsString);
@@ -1545,17 +1556,17 @@ function openLinkIn(url, where, params)
       charset.data = "charset=" + aCharset;
     }
 
     var allowThirdPartyFixupSupports = Cc["@mozilla.org/supports-PRBool;1"].
                                        createInstance(Ci.nsISupportsPRBool);
     allowThirdPartyFixupSupports.data = aAllowThirdPartyFixup;
 
     var referrerURISupports = null;
-    if (aReferrerURI) {
+    if (aReferrerURI && !aNoReferrer) {
       referrerURISupports = Cc["@mozilla.org/supports-string;1"].
                             createInstance(Ci.nsISupportsString);
       referrerURISupports.data = aReferrerURI.spec;
     }
 
     var referrerPolicySupports = Cc["@mozilla.org/supports-PRUint32;1"].
                                  createInstance(Ci.nsISupportsPRUint32);
     referrerPolicySupports.data = aReferrerPolicy;
@@ -1596,20 +1607,25 @@ function openLinkIn(url, where, params)
 
     if (aAllowThirdPartyFixup) {
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
     }
     if (aDisallowInheritPrincipal) {
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
     }
+
+    if (aForceAboutBlankViewerInCurrent) {
+      w.gBrowser.selectedBrowser.createAboutBlankContentViewer(aPrincipal);
+    }
+
     w.getBrowser().loadURIWithFlags(url, {
       triggeringPrincipal: aTriggeringPrincipal,
       flags,
-      referrerURI: aReferrerURI,
+      referrerURI: aNoReferrer ? null : aReferrerURI,
       referrerPolicy: aReferrerPolicy,
       postData: aPostData,
       userContextId: aUserContextId
     });
     w.content.focus();
     break;
 
   case "tabfocused":
@@ -1624,16 +1640,17 @@ function openLinkIn(url, where, params)
     var tab = browser.addTab(url, {
                 referrerURI: aReferrerURI,
                 referrerPolicy: aReferrerPolicy,
                 charset: aCharset,
                 postData: aPostData,
                 allowThirdPartyFixup: aAllowThirdPartyFixup,
                 relatedToCurrent: aRelatedToCurrent,
                 allowMixedContent: aAllowMixedContent,
+                noReferrer: aNoReferrer,
                 userContextId: aUserContextId,
                 originPrincipal: aPrincipal,
                 triggeringPrincipal: aTriggeringPrincipal,
               });
     if (!loadInBackground) {
       browser.selectedTab = tab;
       w.content.focus();
     }
--- a/suite/browser/navigator.js
+++ b/suite/browser/navigator.js
@@ -683,16 +683,19 @@ nsBrowserAccess.prototype = {
       else
         aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
     }
 
     let referrer = aOpener ? aOpener.QueryInterface(nsIInterfaceRequestor)
                                     .getInterface(nsIWebNavigation)
                                     .currentURI : null;
     let referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_UNSET;
+    if (aOpener && aOpener.document) {
+      referrerPolicy = aOpener.document.referrerPolicy;
+    }
     var uri = aURI ? aURI.spec : "about:blank";
 
     switch (aWhere) {
       case nsIBrowserDOMWindow.OPEN_NEWWINDOW:
         return window.openDialog(getBrowserURL(), "_blank", "all,dialog=no",
                                  uri, null, null);
       case nsIBrowserDOMWindow.OPEN_NEWTAB:
         var bgLoad = Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");
@@ -969,30 +972,21 @@ function Startup()
           referrerURI = null;
         }
       }
       let referrerPolicy = (window.arguments[5] != undefined ?
           window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
 
       let userContextId = (window.arguments[6] != undefined ?
           window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
-
-      try {
-        openLinkIn(uriToLoad, "current",
-                   { referrerURI,
-                     referrerPolicy,
-                     postData: window.arguments[3] || null,
-                     allowThirdPartyFixup: window.arguments[4] || false,
-                     userContextId,
-                     // pass the origin principal (if any) and force its use to create
-                     // an initial about:blank viewer if present:
-                     originPrincipal: window.arguments[7],
-                     triggeringPrincipal: window.arguments[8],
-                   });
-      } catch (e) {}
+      loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
+              window.arguments[4] || false, referrerPolicy, userContextId,
+              // pass the origin principal (if any) and force its use to create
+              // an initial about:blank viewer if present:
+              window.arguments[7], !!window.arguments[7], window.arguments[8]);
     } else {
       // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
       // Such callers expect that window.arguments[0] is handled as a single URI.
       loadOneOrMoreURIs(uriToLoad, Services.scriptSecurityManager.getSystemPrincipal());
     }
   }
 
   // Focus content area unless we're loading a blank or other initial
@@ -2085,26 +2079,29 @@ function BrowserCloseWindow()
   win.setAttribute( "x", x );
   win.setAttribute( "y", y );
   win.setAttribute( "height", h );
   win.setAttribute( "width", w );
 
   window.close();
 }
 
-// TODO align the function parameters with Firefox
-// function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
-//                  userContextId, originPrincipal, forceAboutBlankViewerInCurrent,
-//                  triggeringPrincipal)
-function loadURI(uri, referrer, postData, allowThirdPartyFixup) {
+function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
+                 userContextId, originPrincipal,
+                 forceAboutBlankViewerInCurrent, triggeringPrincipal) {
   try {
     openLinkIn(uri, "current",
                { referrerURI: referrer,
+                 referrerPolicy: referrerPolicy,
                  postData: postData,
-                 allowThirdPartyFixup: allowThirdPartyFixup });
+                 allowThirdPartyFixup: allowThirdPartyFixup,
+                 userContextId: userContextId,
+                 originPrincipal,
+                 triggeringPrincipal,
+                 forceAboutBlankViewerInCurrent, });
   } catch (e) {}
 }
 
 function loadOneOrMoreURIs(aURIString, aTriggeringPrincipal) {
   // we're not a browser window, pass the URI string to a new browser window
   if (window.location.href != getBrowserURL()) {
     window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
     return;
--- a/suite/browser/tabbrowser.xml
+++ b/suite/browser/tabbrowser.xml
@@ -1531,34 +1531,38 @@
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             var aTriggeringPrincipal;
             var aReferrerPolicy;
             var aFromExternal;
             var aRelatedToCurrent;
             var aAllowMixedContent;
+            var aNoReferrer;
             var aUserContextId;
+            var aOriginPrincipal;
             var aOpener;
             if (arguments.length == 2 &&
                 arguments[1] != null &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal  = params.triggeringPrincipal;
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aFocusNewTab          = params.focusNewTab;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aAllowMixedContent    = params.allowMixedContent;
+              aNoReferrer           = params.noReferrer;
               aUserContextId        = params.userContextId;
+              aOriginPrincipal      = params.originPrincipal;
               aOpener               = params.opener;
             }
 
             this._browsers = null; // invalidate cache
 
             var t = this.referenceTab.cloneNode(true);
 
             var blank = !aURI || aURI == "about:blank";
@@ -1638,17 +1642,17 @@
               if (aFromExternal)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
               if (aAllowMixedContent)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
               try {
                 b.loadURIWithFlags(aURI, {
                   flags,
                   triggeringPrincipal : aTriggeringPrincipal,
-                  referrerURI: aReferrerURI,
+                  referrerURI: aNoReferrer ? null : aReferrerURI,
                   charset: aCharset,
                   referrerPolicy: aReferrerPolicy,
                   postData: aPostData,
                 });
               }
               catch (ex) { }
             }
 
--- a/suite/mailnews/content/mailWindowOverlay.xul
+++ b/suite/mailnews/content/mailWindowOverlay.xul
@@ -485,16 +485,17 @@
 
   <menupopup id="mailContext"
          onpopupshowing="return event.target != this ||
                                 FillMailContextMenu(this, event);"
          onpopuphiding="if (event.target == this) MailContextOnPopupHiding(this);">
     <menuitem id="context-openlinkintab"
               label="&openLinkCmdInTab.label;"
               accesskey="&openLinkCmdInTab.accesskey;"
+              usercontextid="0"
               oncommand="gContextMenu.openLinkInTab(event);"/>
     <menuitem id="context-openlink"
               label="&openLinkCmd.label;"
               accesskey="&openLinkCmd.accesskey;"
               oncommand="gContextMenu.openLinkInWindow();"/>
     <menuseparator id="mailContext-sep-link"/>
     <menuitem id="context-selectall"/>
     <menuitem id="context-copy"/>