Bug 1276880 - 'Open link in a new <container_name> Tab' in the context menu, r=gijs
authorAndrea Marchesini <amarchesini@mozilla.com>
Sun, 12 Jun 2016 16:23:10 +0200
changeset 377808 6a6aa8a42eb1f71c70bf0bbab7cf3e38e8bb9c90
parent 377807 5834abf0aebb53d59c2ab4bea68448bd62a03d74
child 377809 559f31d84a6e05ffcc1231f114cdc26c39afa72c
push id20857
push userbmo:james@hoppipolla.co.uk
push dateSun, 12 Jun 2016 16:59:39 +0000
reviewersgijs
bugs1276880
milestone50.0a1
Bug 1276880 - 'Open link in a new <container_name> Tab' in the context menu, r=gijs
browser/base/content/browser-context.inc
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_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/utilityOverlay.js
browser/locales/en-US/chrome/browser/browser.properties
browser/themes/linux/browser.css
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -49,28 +49,32 @@
                 label="&spellUndoAddToDictionary.label;"
                 accesskey="&spellUndoAddToDictionary.accesskey;"
                 oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
       <menuseparator id="spell-suggestions-separator"/>
       <menuitem id="context-openlinkincurrent"
                 label="&openLinkCmdInCurrent.label;"
                 accesskey="&openLinkCmdInCurrent.accesskey;"
                 oncommand="gContextMenu.openLinkInCurrent();"/>
+# label and usercontextid are dynamically set.
+      <menuitem id="context-openlinkincontainertab"
+                accesskey="&openLinkCmdInTab.accesskey;"
+                oncommand="gContextMenu.openLinkInTab(event);"/>
       <menuitem id="context-openlinkintab"
                 label="&openLinkCmdInTab.label;"
                 accesskey="&openLinkCmdInTab.accesskey;"
                 usercontextid="0"
                 oncommand="gContextMenu.openLinkInTab(event);"/>
 
       <menu id="context-openlinkinusercontext-menu"
             label="&openLinkCmdInContainerTab.label;"
             accesskey="&openLinkCmdInContainerTab.accesskey;"
             hidden="true">
         <menupopup oncommand="gContextMenu.openLinkInTab(event);"
-                   onpopupshowing="return createUserContextMenu(event, false);" />
+                   onpopupshowing="return gContextMenu.createContainerMenu(event);" />
       </menu>
 
       <menuitem id="context-openlink"
                 label="&openLinkCmd.label;"
                 accesskey="&openLinkCmd.accesskey;"
                 oncommand="gContextMenu.openLink();"/>
       <menuitem id="context-openlinkprivate"
                 label="&openLinkInPrivateWindowCmd.label;"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -6,16 +6,18 @@
 
 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
 Components.utils.import("resource://gre/modules/LoginManagerContextMenu.jsm");
 Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
+                                  "resource:///modules/ContextualIdentityService.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
   "resource://gre/modules/LoginHelper.jsm");
 
 var gContextMenuContentData = null;
 
 function nsContextMenu(aXulMenu, aIsShift) {
   this.shouldDisplay = true;
@@ -138,22 +140,37 @@ nsContextMenu.prototype = {
       try {
         this.linkURI = makeURI(this.linkURL);
       } catch (ex) {}
 
       this.linkTextStr = this.selectionInfo.linkText;
       this.onPlainTextLink = true;
     }
 
+    var inContainer = false;
+    var userContextId = this.browser.contentPrincipal.originAttributes.userContextId;
+    if (userContextId) {
+      inContainer = true;
+      var item = document.getElementById("context-openlinkincontainertab");
+
+      item.setAttribute("usercontextid", userContextId);
+
+      var label = ContextualIdentityService.getUserContextLabel(userContextId);
+      item.setAttribute("label",
+         gBrowserBundle.formatStringFromName("userContextOpenLink.label",
+                                             [label], 1));
+    }
+
     var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
     var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
     var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
     this.showItem("context-openlink", shouldShow && !isWindowPrivate);
     this.showItem("context-openlinkprivate", shouldShow);
-    this.showItem("context-openlinkintab", shouldShow);
+    this.showItem("context-openlinkintab", shouldShow && !inContainer);
+    this.showItem("context-openlinkincontainertab", shouldShow && inContainer);
     this.showItem("context-openlinkinusercontext-menu", shouldShow && !isWindowPrivate && showContainers);
     this.showItem("context-openlinkincurrent", this.onPlainTextLink);
     this.showItem("context-sep-open", shouldShow);
   },
 
   initNavigationItems: function CM_initNavigationItems() {
     var shouldShow = !(this.isContentSelected || this.onLink || this.onImage ||
                        this.onCanvas || this.onVideo || this.onAudio ||
@@ -1855,9 +1872,14 @@ nsContextMenu.prototype = {
   },
 
   _checkTelemetryForMenu: function(aXulMenu) {
     this._telemetryClickID = null;
     this._telemetryPageContext = this._getTelemetryPageContextInfo();
     this._telemetryHadCustomItems = this.hasPageMenu;
     this._getTelemetryClickInfo(aXulMenu);
   },
+
+  createContainerMenu: function(aEvent) {
+    var userContextId = this.browser.contentPrincipal.originAttributes.userContextId;
+    return createUserContextMenu(aEvent, false, userContextId);
+  },
 };
--- a/browser/base/content/test/referrer/browser.ini
+++ b/browser/base/content/test/referrer/browser.ini
@@ -6,18 +6,16 @@ support-files =
   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]
--- a/browser/base/content/test/referrer/browser_referrer_middle_click_in_container.js
+++ b/browser/base/content/test/referrer/browser_referrer_middle_click_in_container.js
@@ -10,11 +10,17 @@ function startMiddleClickTestCase(aTestN
                                   startMiddleClickTestCase,
                                   { userContextId: 3 });
   });
 
   clickTheLink(gTestWindow, "testlink", {button: 1});
 }
 
 function test() {
-  requestLongerTimeout(10);  // slowwww shutdown on e10s
-  startReferrerTest(startMiddleClickTestCase, { userContextId: 3 });
+  waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv(
+    {set: [["privacy.userContext.enabled", true]]},
+    function() {
+      requestLongerTimeout(10);  // slowwww shutdown on e10s
+      startReferrerTest(startMiddleClickTestCase, { userContextId: 3 });
+    });
 }
--- a/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab2.js
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab2.js
@@ -10,38 +10,17 @@ function startNewTabTestCase(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();
+    doContextMenuCommand(gTestWindow, aContextMenu, "context-openlinkincontainertab");
   });
 }
 
 function test() {
   waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv(
     {set: [["privacy.userContext.enabled", true]]},
--- a/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab3.js
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab3.js
@@ -32,17 +32,17 @@ function startNewTabTestCase(aTestNumber
       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.");
+      is("0", firstContext.getAttribute("usercontextid"), "We have the right usercontextid value.");
 
       aContextMenu.addEventListener("popuphidden", function onPopupHidden() {
         aContextMenu.removeEventListener("popuphidden", onPopupHidden);
         firstContext.doCommand();
       });
 
       aContextMenu.hidePopup();
     });
deleted file mode 100644
--- a/browser/base/content/test/referrer/browser_referrer_open_link_in_tab_in_container.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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 });
-}
--- a/browser/base/content/test/referrer/browser_referrer_open_link_in_window_in_container.js
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_window_in_container.js
@@ -16,11 +16,17 @@ function startNewWindowTestCase(aTestNum
       });
     });
 
     doContextMenuCommand(gTestWindow, aContextMenu, "context-openlink");
   });
 }
 
 function test() {
-  requestLongerTimeout(10);  // slowwww shutdown on e10s
-  startReferrerTest(startNewWindowTestCase, { userContextId: 1 });
+  waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv(
+    {set: [["privacy.userContext.enabled", true]]},
+    function() {
+      requestLongerTimeout(10);  // slowwww shutdown on e10s
+      startReferrerTest(startNewWindowTestCase, { userContextId: 1 });
+    });
 }
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -410,25 +410,46 @@ function checkForMiddleClick(node, event
     // (Menus close automatically with left-click but not with middle-click.)
     closeMenus(event.target);
   }
 }
 
 // Populate a menu with user-context menu items. This method should be called
 // by onpopupshowing passing the event as first argument. addCommandAttribute
 // param is used to set the 'command' attribute in the new menuitem elements.
-function createUserContextMenu(event, addCommandAttribute = true) {
+function createUserContextMenu(event, addCommandAttribute = true, excludeUserContextId = 0) {
   while (event.target.hasChildNodes()) {
     event.target.removeChild(event.target.firstChild);
   }
 
   let bundle = document.getElementById("bundle_browser");
   let docfrag = document.createDocumentFragment();
 
+  // If we are excluding a userContextId, we want to add a 'no-container' item.
+  if (excludeUserContextId) {
+    let menuitem = document.createElement("menuitem");
+    menuitem.setAttribute("usercontextid", "0");
+    menuitem.setAttribute("label", bundle.getString("userContextNone.label"));
+    menuitem.setAttribute("accesskey", bundle.getString("userContextNone.accesskey"));
+
+    // We don't set an oncommand/command attribute attribute because if we have
+    // to exclude a userContextId we are generating the contextMenu and
+    // addCommandAttribute will be false.
+
+    docfrag.appendChild(menuitem);
+
+    let menuseparator = document.createElement("menuseparator");
+    docfrag.appendChild(menuseparator);
+  }
+
   ContextualIdentityService.getIdentities().forEach(identity => {
+    if (identity.userContextId == excludeUserContextId) {
+      return;
+    }
+
     let menuitem = document.createElement("menuitem");
     menuitem.setAttribute("usercontextid", identity.userContextId);
     menuitem.setAttribute("label", bundle.getString(identity.label));
     menuitem.setAttribute("accesskey", bundle.getString(identity.accessKey));
     menuitem.classList.add("menuitem-iconic");
 
     if (addCommandAttribute) {
       menuitem.setAttribute("command", "Browser:NewUserContextTab");
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -702,35 +702,40 @@ e10s.accessibilityNotice.mainMessage2 = 
 e10s.accessibilityNotice.acceptButton.label = OK
 e10s.accessibilityNotice.acceptButton.accesskey = O
 e10s.accessibilityNotice.enableAndRestart.label = Enable (Requires Restart)
 e10s.accessibilityNotice.enableAndRestart.accesskey = E
 
 # LOCALIZATION NOTE (userContextPersonal.label,
 #                    userContextWork.label,
 #                    userContextShopping.label,
-#                    userContextBanking.label):
+#                    userContextBanking.label,
+#                    userContextNone.label):
 # These strings specify the four predefined contexts included in support of the
 # Contextual Identity / Containers project. Each context is meant to represent
 # the context that the user is in when interacting with the site. Different
 # contexts will store cookies and other information from those sites in
 # different, isolated locations. You can enable the feature by typing
 # about:config in the URL bar and changing privacy.userContext.enabled to true.
 # Once enabled, you can open a new tab in a specific context by clicking
 # File > New Container Tab > (1 of 4 contexts). Once opened, you will see these
 # strings on the right-hand side of the URL bar.
 userContextPersonal.label = Personal
 userContextWork.label = Work
 userContextBanking.label = Banking
 userContextShopping.label = Shopping
+userContextNone.label = No Container
 
 userContextPersonal.accesskey = P
 userContextWork.accesskey = W
 userContextBanking.accesskey = B
 userContextShopping.accesskey = S
+userContextNone.accesskey = N
+
+userContextOpenLink.label = Open Link in New %S Tab
 
 muteTab.label = Mute Tab
 muteTab.accesskey = M
 unmuteTab.label = Unmute Tab
 unmuteTab.accesskey = M
 
 # LOCALIZATION NOTE (weakCryptoOverriding.message): %S is brandShortName
 weakCryptoOverriding.message = %S recommends that you don’t enter your password, credit card and other personal information on this website.
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -345,16 +345,17 @@ menuitem:not([type]):not(.menuitem-toolt
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 80px 16px 64px);
 }
 
 #placesContext_open\:newtab,
 #placesContext_openContainer\:tabs,
 #menu_newNavigatorTab,
 #context-openlinkintab,
+#context-openlinkincontainertab,
 #context-openframeintab {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 64px 16px 48px);
 }
 
 #menu_openFile {
   list-style-image: url("moz-icon://stock/gtk-open?size=menu");
 }