Bug 1277765 - No referrer when a link is opened by middle-click or the context menu and containers are involved. r=mconley, a=sledru
☠☠ backed out by db8d264de7e3 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 09 Jun 2016 17:02:50 +0200
changeset 340066 cf366a3b54d26733dc13ffbd2a2c5b46e1c7df12
parent 340065 c2fbedd0b788bb91bf671f70b0a4b7f317f3c39d
child 340067 90964ed0b53027ef9ee5f07452130f45ea7d7e21
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley, sledru
bugs1277765
milestone49.0a2
Bug 1277765 - No referrer when a link is opened by middle-click or the context menu and containers are involved. r=mconley, a=sledru
browser/base/content/browser.js
browser/base/content/content.js
browser/base/content/nsContextMenu.js
browser/base/content/test/referrer/browser.ini
browser/base/content/test/referrer/browser_referrer_middle_click_in_container.js
browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab.js
browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab2.js
browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab3.js
browser/base/content/test/referrer/browser_referrer_open_link_in_tab_in_container.js
browser/base/content/test/referrer/browser_referrer_open_link_in_window_in_container.js
browser/base/content/test/referrer/head.js
browser/components/contextualidentity/test/browser/browser.ini
browser/components/contextualidentity/test/browser/browser_middleClick.js
browser/modules/ContentClick.jsm
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5545,16 +5545,22 @@ function handleLinkClick(event, href, li
   }
 
   urlSecurityCheck(href, doc.nodePrincipal);
   let params = { charset: doc.characterSet,
                  allowMixedContent: persistAllowMixedContentInChildTab,
                  referrerURI: referrerURI,
                  referrerPolicy: referrerPolicy,
                  noReferrer: BrowserUtils.linkHasNoReferrer(linkNode) };
+
+  // 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;
 }
 
 function middleMousePaste(event) {
   let clipboard = readFromClipboard();
   if (!clipboard)
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -449,17 +449,18 @@ var ClickEventHandler = {
       if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
         referrerPolicy = referrerAttrValue;
       }
     }
 
     let json = { button: event.button, shiftKey: event.shiftKey,
                  ctrlKey: event.ctrlKey, metaKey: event.metaKey,
                  altKey: event.altKey, href: null, title: null,
-                 bookmark: false, referrerPolicy: referrerPolicy };
+                 bookmark: false, referrerPolicy: referrerPolicy,
+                 originAttributes: principal ? principal.originAttributes : {} };
 
     if (href) {
       try {
         BrowserUtils.urlSecurityCheck(href, principal);
       } catch (e) {
         return;
       }
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -966,18 +966,27 @@ nsContextMenu.prototype = {
     return aNode.spellcheck;
   },
 
   _openLinkInParameters : function (extra) {
     let params = { charset: gContextMenuContentData.charSet,
                    referrerURI: gContextMenuContentData.documentURIObject,
                    referrerPolicy: gContextMenuContentData.referrerPolicy,
                    noReferrer: this.linkHasNoReferrer };
-    for (let p in extra)
+    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 window.
   openLink : function () {
     urlSecurityCheck(this.linkURL, this.principal);
     openLinkIn(this.linkURL, "window", this._openLinkInParameters());
   },
@@ -1009,20 +1018,16 @@ nsContextMenu.prototype = {
       catch (e) { }
     }
 
     let params = {
       allowMixedContent: persistAllowMixedContentInChildTab,
       userContextId: parseInt(event.target.getAttribute('usercontextid'))
     };
 
-    if (params.userContextId != this.principal.originAttributes.userContextId) {
-      params.noReferrer = true;
-    }
-
     openLinkIn(this.linkURL, "tab", this._openLinkInParameters(params));
   },
 
   // open URL in current tab
   openLinkInCurrent: function() {
     urlSecurityCheck(this.linkURL, this.principal);
     openLinkIn(this.linkURL, "current", this._openLinkInParameters());
   },
--- a/browser/base/content/test/referrer/browser.ini
+++ b/browser/base/content/test/referrer/browser.ini
@@ -1,17 +1,26 @@
 [DEFAULT]
 support-files =
   file_referrer_policyserver.sjs
   file_referrer_policyserver_attr.sjs
   file_referrer_testserver.sjs
   head.js
 
 [browser_referrer_middle_click.js]
+[browser_referrer_middle_click_in_container.js]
 [browser_referrer_open_link_in_private.js]
 skip-if = os == 'linux' # Bug 1145199
 [browser_referrer_open_link_in_tab.js]
 skip-if = os == 'linux' # Bug 1144816
+[browser_referrer_open_link_in_tab_in_container.js]
+skip-if = os == 'linux' # Bug 1144816
 [browser_referrer_open_link_in_window.js]
 skip-if = os == 'linux' # Bug 1145199
+[browser_referrer_open_link_in_window_in_container.js]
+skip-if = os == 'linux' # Bug 1145199
 [browser_referrer_simple_click.js]
 [browser_referrer_open_link_in_container_tab.js]
 skip-if = os == 'linux' # Bug 1144816
+[browser_referrer_open_link_in_container_tab2.js]
+skip-if = os == 'linux' # Bug 1144816
+[browser_referrer_open_link_in_container_tab3.js]
+skip-if = os == 'linux' # Bug 1144816
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/referrer/browser_referrer_middle_click_in_container.js
@@ -0,0 +1,20 @@
+// Tests referrer on middle-click navigation.
+// Middle-clicks on the link, which opens it in a new tab, same container.
+
+function startMiddleClickTestCase(aTestNumber) {
+  info("browser_referrer_middle_click: " +
+       getReferrerTestDescription(aTestNumber));
+  someTabLoaded(gTestWindow).then(function(aNewTab) {
+    gTestWindow.gBrowser.selectedTab = aNewTab;
+    checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
+                                  startMiddleClickTestCase,
+                                  { userContextId: 3 });
+  });
+
+  clickTheLink(gTestWindow, "testlink", {button: 1});
+}
+
+function test() {
+  requestLongerTimeout(10);  // slowwww shutdown on e10s
+  startReferrerTest(startMiddleClickTestCase, { userContextId: 3 });
+}
--- a/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab.js
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab.js
@@ -18,28 +18,37 @@ function startNewTabTestCase(aTestNumber
     someTabLoaded(gTestWindow).then(function(aNewTab) {
       gTestWindow.gBrowser.selectedTab = aNewTab;
 
       checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
                                     startNewTabTestCase);
     });
 
     let menu = gTestWindow.document.getElementById("context-openlinkinusercontext-menu");
-    let menupopup = menu.menupopup;
-    menupopup.showPopup();
 
-    is(menupopup.nodeType, Node.ELEMENT_NODE, "We have a menupopup.");
-    ok(menupopup.firstChild, "We have a first container entry.");
+    let menupopup = menu.menupopup;
+    menu.addEventListener("popupshown", function onPopupShown() {
+      menu.removeEventListener("popupshown", onPopupShown);
+
+      is(menupopup.nodeType, Node.ELEMENT_NODE, "We have a menupopup.");
+      ok(menupopup.firstChild, "We have a first container entry.");
 
-    let firstContext = menupopup.firstChild;
-    is(firstContext.nodeType, Node.ELEMENT_NODE, "We have a first container entry.");
-    ok(firstContext.hasAttribute('usercontextid'), "We have a usercontextid value.");
+      let firstContext = menupopup.firstChild;
+      is(firstContext.nodeType, Node.ELEMENT_NODE, "We have a first container entry.");
+      ok(firstContext.hasAttribute("usercontextid"), "We have a usercontextid value.");
 
-    firstContext.doCommand();
-    aContextMenu.hidePopup();
+      aContextMenu.addEventListener("popuphidden", function onPopupHidden() {
+        aContextMenu.removeEventListener("popuphidden", onPopupHidden);
+        firstContext.doCommand();
+      });
+
+      aContextMenu.hidePopup();
+    });
+
+    menupopup.showPopup();
   });
 }
 
 function test() {
   waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv(
     {set: [["privacy.userContext.enabled", true]]},
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab2.js
@@ -0,0 +1,52 @@
+// Tests referrer on context menu navigation - open link in new container tab.
+// Selects "open link in new container tab" from the context menu.
+
+// The test runs from a container ID 1.
+// Output: we have the correct referrer policy applied.
+
+function startNewTabTestCase(aTestNumber) {
+  info("browser_referrer_open_link_in_container_tab: " +
+       getReferrerTestDescription(aTestNumber));
+  contextMenuOpened(gTestWindow, "testlink").then(function(aContextMenu) {
+    someTabLoaded(gTestWindow).then(function(aNewTab) {
+      gTestWindow.gBrowser.selectedTab = aNewTab;
+
+      checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
+                                    startNewTabTestCase, { userContextId: 1 });
+    });
+
+    let menu = gTestWindow.document.getElementById("context-openlinkinusercontext-menu");
+    let menupopup = menu.menupopup;
+    menu.addEventListener("popupshown", function onPopupShown() {
+      menu.removeEventListener("popupshown", onPopupShown);
+
+      is(menupopup.nodeType, Node.ELEMENT_NODE, "We have a menupopup.");
+      ok(menupopup.firstChild, "We have a first container entry.");
+
+      let firstContext = menupopup.firstChild;
+      is(firstContext.nodeType, Node.ELEMENT_NODE, "We have a first container entry.");
+      ok(firstContext.hasAttribute("usercontextid"), "We have a usercontextid value.");
+      is("1", firstContext.getAttribute("usercontextid"), "We have the right usercontextid value.");
+
+      aContextMenu.addEventListener("popuphidden", function onPopupHidden() {
+        aContextMenu.removeEventListener("popuphidden", onPopupHidden);
+        firstContext.doCommand();
+      });
+
+      aContextMenu.hidePopup();
+    });
+
+    menupopup.showPopup();
+  });
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv(
+    {set: [["privacy.userContext.enabled", true]]},
+    function() {
+      requestLongerTimeout(10);  // slowwww shutdown on e10s
+      startReferrerTest(startNewTabTestCase, { userContextId: 1 });
+    });
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab3.js
@@ -0,0 +1,63 @@
+// Tests referrer on context menu navigation - open link in new container tab.
+// Selects "open link in new container tab" from the context menu.
+
+// The test runs from a container ID 2.
+// Output: we have no referrer.
+
+function getReferrerTest(aTestNumber) {
+  let test = _referrerTests[aTestNumber];
+  if (test) {
+    // We want all the referrer tests to fail!
+    test.result = "";
+  }
+
+  return test;
+}
+
+function startNewTabTestCase(aTestNumber) {
+  info("browser_referrer_open_link_in_container_tab: " +
+       getReferrerTestDescription(aTestNumber));
+  contextMenuOpened(gTestWindow, "testlink").then(function(aContextMenu) {
+    someTabLoaded(gTestWindow).then(function(aNewTab) {
+      gTestWindow.gBrowser.selectedTab = aNewTab;
+
+      checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
+                                    startNewTabTestCase, { userContextId: 2 });
+    });
+
+    let menu = gTestWindow.document.getElementById("context-openlinkinusercontext-menu");
+
+    let menupopup = menu.menupopup;
+    menu.addEventListener("popupshown", function onPopupShown() {
+      menu.removeEventListener("popupshown", onPopupShown);
+
+      is(menupopup.nodeType, Node.ELEMENT_NODE, "We have a menupopup.");
+      ok(menupopup.firstChild, "We have a first container entry.");
+
+      let firstContext = menupopup.firstChild;
+      is(firstContext.nodeType, Node.ELEMENT_NODE, "We have a first container entry.");
+      ok(firstContext.hasAttribute("usercontextid"), "We have a usercontextid value.");
+      is("1", firstContext.getAttribute("usercontextid"), "We have the right usercontextid value.");
+
+      aContextMenu.addEventListener("popuphidden", function onPopupHidden() {
+        aContextMenu.removeEventListener("popuphidden", onPopupHidden);
+        firstContext.doCommand();
+      });
+
+      aContextMenu.hidePopup();
+    });
+
+    menupopup.showPopup();
+  });
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv(
+    {set: [["privacy.userContext.enabled", true]]},
+    function() {
+      requestLongerTimeout(10);  // slowwww shutdown on e10s
+      startReferrerTest(startNewTabTestCase, { userContextId: 2 });
+    });
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_tab_in_container.js
@@ -0,0 +1,34 @@
+// Tests referrer on context menu navigation - open link in new tab.
+// Selects "open link in new tab" from the context menu.
+
+// This test starts from a container tab. We don't want to propagate the
+// referrer.
+function getReferrerTest(aTestNumber) {
+  let test = _referrerTests[aTestNumber];
+  if (test) {
+    // We want all the referrer tests to fail!
+    test.result = "";
+  }
+
+  return test;
+}
+
+function startNewTabTestCase(aTestNumber) {
+  info("browser_referrer_open_link_in_tab: " +
+       getReferrerTestDescription(aTestNumber));
+  contextMenuOpened(gTestWindow, "testlink").then(function(aContextMenu) {
+    someTabLoaded(gTestWindow).then(function(aNewTab) {
+      gTestWindow.gBrowser.selectedTab = aNewTab;
+      checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
+                                    startNewTabTestCase,
+                                    { userContextId: 4 });
+    });
+
+    doContextMenuCommand(gTestWindow, aContextMenu, "context-openlinkintab");
+  });
+}
+
+function test() {
+  requestLongerTimeout(10);  // slowwww shutdown on e10s
+  startReferrerTest(startNewTabTestCase, { userContextId: 4 });
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_window_in_container.js
@@ -0,0 +1,26 @@
+// Tests referrer on context menu navigation - open link in new window.
+// Selects "open link in new window" from the context menu.
+
+// This test runs from a container tab. The new tab/window will be loaded in
+// the same container.
+
+function startNewWindowTestCase(aTestNumber) {
+  info("browser_referrer_open_link_in_window: " +
+       getReferrerTestDescription(aTestNumber));
+  contextMenuOpened(gTestWindow, "testlink").then(function(aContextMenu) {
+    newWindowOpened().then(function(aNewWindow) {
+      someTabLoaded(aNewWindow).then(function() {
+        checkReferrerAndStartNextTest(aTestNumber, aNewWindow, null,
+                                      startNewWindowTestCase,
+                                      { userContextId: 1 });
+      });
+    });
+
+    doContextMenuCommand(gTestWindow, aContextMenu, "context-openlink");
+  });
+}
+
+function test() {
+  requestLongerTimeout(10);  // slowwww shutdown on e10s
+  startReferrerTest(startNewWindowTestCase, { userContextId: 1 });
+}
--- a/browser/base/content/test/referrer/head.js
+++ b/browser/base/content/test/referrer/head.js
@@ -190,82 +190,82 @@ function doContextMenuCommand(aWindow, a
 }
 
 /**
  * Loads a single test case, i.e., a source url into gTestWindow.
  * @param aTestNumber The test case number - 0, 1, 2...
  * @return {Promise}
  * @resolves When the source url for this test case is loaded.
  */
-function referrerTestCaseLoaded(aTestNumber) {
+function referrerTestCaseLoaded(aTestNumber, aParams) {
   let test = getReferrerTest(aTestNumber);
   let server = rounds == 0 ? REFERRER_POLICYSERVER_URL :
                              REFERRER_POLICYSERVER_URL_ATTRIBUTE;
   let url = test.fromScheme + server +
             "?scheme=" + escape(test.toScheme) +
             "&policy=" + escape(test.policy || "") +
             "&rel=" + escape(test.rel || "");
   var browser = gTestWindow.gBrowser;
-  browser.selectedTab = browser.addTab(url);
+  browser.selectedTab = browser.addTab(url, aParams);
   return BrowserTestUtils.browserLoaded(browser.selectedBrowser);
 }
 
 /**
  * Checks the result of the referrer test, and moves on to the next test.
  * @param aTestNumber The test number - 0, 1, 2, ...
  * @param aNewWindow The new window where the referrer target opened, or null.
  * @param aNewTab The new tab where the referrer target opened, or null.
  * @param aStartTestCase The callback to start the next test, aTestNumber + 1.
  */
 function checkReferrerAndStartNextTest(aTestNumber, aNewWindow, aNewTab,
-                                       aStartTestCase) {
+                                       aStartTestCase, aParams = {}) {
   referrerResultExtracted(aNewWindow || gTestWindow).then(function(result) {
     // Compare the actual result against the expected one.
     let test = getReferrerTest(aTestNumber);
     let desc = getReferrerTestDescription(aTestNumber);
     is(result, test.result, desc);
 
     // Clean up - close new tab / window, and then the source tab.
     aNewTab && (aNewWindow || gTestWindow).gBrowser.removeTab(aNewTab);
     aNewWindow && aNewWindow.close();
     is(gTestWindow.gBrowser.tabs.length, 2, "two tabs open");
     gTestWindow.gBrowser.removeTab(gTestWindow.gBrowser.tabs[1]);
 
     // Move on to the next test.  Or finish if we're done.
     var nextTestNumber = aTestNumber + 1;
     if (getReferrerTest(nextTestNumber)) {
-      referrerTestCaseLoaded(nextTestNumber).then(function() {
+      referrerTestCaseLoaded(nextTestNumber, aParams).then(function() {
         aStartTestCase(nextTestNumber);
       });
     } else if (rounds == 0) {
       nextTestNumber = 0;
       rounds = 1;
-      referrerTestCaseLoaded(nextTestNumber).then(function() {
+      referrerTestCaseLoaded(nextTestNumber, aParams).then(function() {
         aStartTestCase(nextTestNumber);
       });
     } else {
       finish();
     }
   });
 }
 
 /**
  * Fires up the complete referrer test.
  * @param aStartTestCase The callback to start a single test case, called with
  * the test number - 0, 1, 2... Needs to trigger the navigation from the source
  * page, and call checkReferrerAndStartNextTest() when the target is loaded.
  */
-function startReferrerTest(aStartTestCase) {
+function startReferrerTest(aStartTestCase, params = {}) {
   waitForExplicitFinish();
 
   // Open the window where we'll load the source URLs.
   gTestWindow = openDialog(location, "", "chrome,all,dialog=no", "about:blank");
   registerCleanupFunction(function() {
     gTestWindow && gTestWindow.close();
   });
 
   // Load and start the first test.
   delayedStartupFinished(gTestWindow).then(function() {
-    referrerTestCaseLoaded(0).then(function() {
+    referrerTestCaseLoaded(0, params).then(function() {
       aStartTestCase(0);
     });
   });
 }
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -12,8 +12,9 @@ support-files =
 skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276
 [browser_windowName.js]
 tags = openwindow
 [browser_windowOpen.js]
 tags = openwindow
 [browser_serviceworkers.js]
 [browser_broadcastchannel.js]
 [browser_blobUrl.js]
+[browser_middleClick.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_middleClick.js
@@ -0,0 +1,41 @@
+"use strict";
+
+const BASE_ORIGIN = "http://example.com";
+const URI = BASE_ORIGIN +
+  "/browser/browser/components/contextualidentity/test/browser/empty_file.html";
+
+add_task(function* () {
+  info("Opening a new container tab...");
+
+  let tab = gBrowser.addTab(URI, { userContextId: 1 });
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+
+  info("Create a HTMLAnchorElement...");
+  let position = yield ContentTask.spawn(browser, URI,
+    function(URI) {
+      let anchor = content.document.createElement("a");
+      anchor.setAttribute('id', 'clickMe');
+      anchor.setAttribute("href", URI);
+      anchor.appendChild(content.document.createTextNode("click me!"));
+      content.document.body.appendChild(anchor);
+    }
+  );
+
+  info("Synthesize a mouse click and wait for a new tab...");
+  let newTab = yield new Promise((resolve, reject) => {
+    gBrowser.tabContainer.addEventListener("TabOpen", function onTabOpen(openEvent) {
+      gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen);
+      resolve(openEvent.target);
+    })
+
+    BrowserTestUtils.synthesizeMouseAtCenter("#clickMe", { button: 1 }, browser);
+  });
+
+  is(newTab.getAttribute("usercontextid"), 1, "Correct UserContextId?");
+
+  yield BrowserTestUtils.removeTab(tab);
+  yield BrowserTestUtils.removeTab(newTab);
+});
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -78,11 +78,16 @@ var ContentClick = {
     // Todo(903022): code for where == save
 
     let params = { charset: browser.characterSet,
                    referrerURI: browser.documentURI,
                    referrerPolicy: json.referrerPolicy,
                    noReferrer: json.noReferrer,
                    allowMixedContent: json.allowMixedContent };
 
+    // The new tab/window must use the same userContextId.
+    if (json.originAttributes.userContextId) {
+      params.userContextId = json.originAttributes.userContextId;
+    }
+
     window.openLinkIn(json.href, where, params);
   }
 };