diff --git a/webextension/css/popup.css b/webextension/css/popup.css
index af78306..d9899da 100644
--- a/webextension/css/popup.css
+++ b/webextension/css/popup.css
@@ -450,7 +450,7 @@ manage things like container crud */
margin-inline-start: var(--inline-item-space-size);
}
-#container-panel #sort-containers-link {
+#container-panel .action-link {
align-items: center;
block-size: var(--block-url-label-size);
border: 1px solid #d8d8d8;
@@ -460,11 +460,12 @@ manage things like container crud */
font-size: var(--small-text-size);
inline-size: var(--inline-button-size);
justify-content: center;
+ margin-left: var(--block-line-space-size);
text-decoration: none;
}
-#container-panel #sort-containers-link:hover,
-#container-panel #sort-containers-link:focus {
+#container-panel .action-link:hover,
+#container-panel .action-link:focus {
background: #f2f2f2;
}
diff --git a/webextension/js/background/backgroundLogic.js b/webextension/js/background/backgroundLogic.js
index 36bcb47..c16dfc1 100644
--- a/webextension/js/background/backgroundLogic.js
+++ b/webextension/js/background/backgroundLogic.js
@@ -14,6 +14,25 @@ const backgroundLogic = {
return extensionInfo;
},
+ async getContainers(windowId) {
+ const [identities, state] = await Promise.all([
+ browser.contextualIdentities.query({}),
+ backgroundLogic.queryIdentitiesState(windowId)
+ ]);
+
+ return identities
+ .filter(identity => {
+ return identity.cookieStoreId in state;
+ })
+ .map(identity => {
+ const stateObject = state[identity.cookieStoreId];
+ identity.hasOpenTabs = stateObject.hasOpenTabs;
+ identity.hasHiddenTabs = stateObject.hasHiddenTabs;
+
+ return identity;
+ });
+ },
+
getUserContextIdFromCookieStoreId(cookieStoreId) {
if (!cookieStoreId) {
return false;
@@ -299,8 +318,40 @@ const backgroundLogic = {
return await identityState.storageArea.set(options.cookieStoreId, containerState);
},
+ async unhideAllTabs(windowId) {
+ const promises = [];
+ (await backgroundLogic.getContainers(windowId))
+ .filter(identity => identity.hasHiddenTabs)
+ .map(identity => identity.cookieStoreId)
+ .forEach(cookieStoreId => {
+ // We need to call unhideContainer in messageHandler to prevent it from
+ // unhiding multiple times
+ promises.push(messageHandler.unhideContainer(cookieStoreId));
+ });
+ return Promise.all(promises);
+ },
+
+ async showOnly(options) {
+ if (!("windowId" in options) || !("cookieStoreId" in options)) {
+ return Promise.reject("showOnly needs both a windowId and a cookieStoreId");
+ }
+
+ const promises = [];
+ (await backgroundLogic.getContainers(options.windowId))
+ .filter(identity => identity.cookieStoreId !== options.cookieStoreId && identity.hasOpenTabs)
+ .map(identity => identity.cookieStoreId)
+ .forEach(cookieStoreId => {
+ promises.push(backgroundLogic.hideTabs({cookieStoreId, windowId: options.windowId}));
+ });
+
+ // We need to call unhideContainer in messageHandler to prevent it from
+ // unhiding multiple times
+ promises.push(messageHandler.unhideContainer(options.cookieStoreId));
+
+ return Promise.all(promises);
+ },
+
cookieStoreId(userContextId) {
return `firefox-container-${userContextId}`;
}
};
-
diff --git a/webextension/js/background/messageHandler.js b/webextension/js/background/messageHandler.js
index 1971953..96e3628 100644
--- a/webextension/js/background/messageHandler.js
+++ b/webextension/js/background/messageHandler.js
@@ -68,6 +68,15 @@ const messageHandler = {
case "exemptContainerAssignment":
response = assignManager._exemptTab(m);
break;
+ case "unhideAllTabs":
+ response = backgroundLogic.unhideAllTabs(m.message.windowId);
+ break;
+ case "showOnly":
+ response = backgroundLogic.showOnly({
+ windowId: m.windowId,
+ cookieStoreId: m.cookieStoreId
+ });
+ break;
}
return response;
});
diff --git a/webextension/js/popup.js b/webextension/js/popup.js
index a644322..dbd4104 100644
--- a/webextension/js/popup.js
+++ b/webextension/js/popup.js
@@ -500,6 +500,20 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
}
});
+ Logic.addEnterHandler(document.querySelector("#unhide-all-containers-link"), async function () {
+ try {
+ await browser.runtime.sendMessage({
+ method: "unhideAllTabs",
+ message: {
+ windowId: browser.windows.WINDOW_ID_CURRENT
+ }
+ });
+ window.close();
+ } catch (e) {
+ window.close();
+ }
+ });
+
document.addEventListener("keydown", (e) => {
const selectables = [...document.querySelectorAll("[tabindex='0'], [tabindex='-1']")];
const element = document.activeElement;
@@ -603,6 +617,7 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
const hasTabs = (identity.hasHiddenTabs || identity.hasOpenTabs);
const tr = document.createElement("tr");
const context = document.createElement("td");
+ const hide = document.createElement("td");
const manage = document.createElement("td");
tr.classList.add("container-panel-row");
@@ -623,11 +638,24 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
context.querySelector(".container-name").textContent = identity.name;
manage.innerHTML = "";
+ hide.classList.add("hide-or-show-tabs", "pop-button");
+ const img = document.createElement("img");
+ img.classList.add("hide-or-show-tabs", "pop-button-image-small");
+ if (identity.hasHiddenTabs) {
+ hide.title = escaped`Show ${identity.name} container`;
+ img.setAttribute("src", CONTAINER_HIDE_SRC);
+ } else {
+ hide.title = escaped`Hide ${identity.name} container`;
+ img.setAttribute("src", CONTAINER_UNHIDE_SRC);
+ }
+ hide.appendChild(img);
+
fragment.appendChild(tr);
tr.appendChild(context);
if (hasTabs) {
+ tr.appendChild(hide);
tr.appendChild(manage);
}
@@ -635,16 +663,19 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
if (e.target.matches(".open-newtab")
|| e.target.parentNode.matches(".open-newtab")
|| e.type === "keydown") {
- try {
- browser.tabs.create({
- cookieStoreId: identity.cookieStoreId
- });
- window.close();
- } catch (e) {
- window.close();
- }
- } else if (hasTabs) {
+ browser.tabs.create({
+ cookieStoreId: identity.cookieStoreId
+ }).then(window.close).catch(window.close);
+ } else if (e.target.matches(".show-tabs")
+ || e.target.parentNode.matches(".show-tabs")) {
Logic.showPanel(P_CONTAINER_INFO, identity);
+ } else if (e.target.matches(".hide-or-show-tabs")
+ || e.target.parentNode.matches(".hide-or-show-tabs")) {
+ browser.runtime.sendMessage({
+ method: identity.hasHiddenTabs ? "showTabs" : "hideTabs",
+ windowId: browser.windows.WINDOW_ID_CURRENT,
+ cookieStoreId: identity.cookieStoreId
+ }).then(window.close).catch(window.close);
}
});
});
@@ -696,6 +727,19 @@ Logic.registerPanel(P_CONTAINER_INFO, {
}
});
+ Logic.addEnterHandler(document.querySelector("#container-info-hideothers"), async function () {
+ try {
+ browser.runtime.sendMessage({
+ method: "showOnly",
+ windowId: browser.windows.WINDOW_ID_CURRENT,
+ cookieStoreId: Logic.currentCookieStoreId()
+ });
+ window.close();
+ } catch (e) {
+ window.close();
+ }
+ });
+
// Check if the user has incompatible add-ons installed
try {
const incompatible = await browser.runtime.sendMessage({
@@ -752,6 +796,9 @@ Logic.registerPanel(P_CONTAINER_INFO, {
const hideShowLabel = document.getElementById("container-info-hideorshow-label");
hideShowLabel.textContent = identity.hasHiddenTabs ? "Show this container" : "Hide this container";
+ const hideOthersLabel = document.getElementById("container-info-hideothers");
+ hideOthersLabel.textContent = identity.hasHiddenTabs ? "Show only this container" : "Hide other containers";
+
// Let's remove all the previous tabs.
const table = document.getElementById("container-info-table");
while (table.firstChild) {
diff --git a/webextension/popup.html b/webextension/popup.html
index 8cde498..1634f4b 100644
--- a/webextension/popup.html
+++ b/webextension/popup.html
@@ -1,3 +1,4 @@
+