Container tab listing
This commit is contained in:
parent
8598c76e25
commit
bb0713d0d4
4 changed files with 289 additions and 116 deletions
209
index.js
209
index.js
|
@ -2,6 +2,7 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||||
|
|
||||||
const { attachTo } = require("sdk/content/mod");
|
const { attachTo } = require("sdk/content/mod");
|
||||||
const { ContextualIdentityService } = require("resource://gre/modules/ContextualIdentityService.jsm");
|
const { ContextualIdentityService } = require("resource://gre/modules/ContextualIdentityService.jsm");
|
||||||
|
const { getFavicon } = require("sdk/places/favicon");
|
||||||
const self = require("sdk/self");
|
const self = require("sdk/self");
|
||||||
const { Style } = require("sdk/stylesheet/style");
|
const { Style } = require("sdk/stylesheet/style");
|
||||||
const tabs = require("sdk/tabs");
|
const tabs = require("sdk/tabs");
|
||||||
|
@ -37,7 +38,10 @@ let ContainerService = {
|
||||||
"hideTabs",
|
"hideTabs",
|
||||||
"showTabs",
|
"showTabs",
|
||||||
"sortTabs",
|
"sortTabs",
|
||||||
|
"getTabs",
|
||||||
|
"showTab",
|
||||||
"openTab",
|
"openTab",
|
||||||
|
"moveTabsToWindow",
|
||||||
"queryIdentities",
|
"queryIdentities",
|
||||||
"getIdentity"
|
"getIdentity"
|
||||||
];
|
];
|
||||||
|
@ -51,25 +55,22 @@ let ContainerService = {
|
||||||
});
|
});
|
||||||
|
|
||||||
// It can happen that this jsm is loaded after the opening a container tab.
|
// It can happen that this jsm is loaded after the opening a container tab.
|
||||||
for (let tab of tabs) {
|
for (const tab of tabs) {
|
||||||
let xulTab = viewFor(tab);
|
const userContextId = this._getUserContextIdFromTab(tab);
|
||||||
let userContextId = parseInt(xulTab.getAttribute("usercontextid") || 0, 10);
|
|
||||||
if (userContextId) {
|
if (userContextId) {
|
||||||
++this._identitiesState[userContextId].openTabs;
|
++this._identitiesState[userContextId].openTabs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tabs.on("open", tab => {
|
tabs.on("open", tab => {
|
||||||
let xulTab = viewFor(tab);
|
const userContextId = this._getUserContextIdFromTab(tab);
|
||||||
let userContextId = parseInt(xulTab.getAttribute("usercontextid") || 0, 10);
|
|
||||||
if (userContextId) {
|
if (userContextId) {
|
||||||
++this._identitiesState[userContextId].openTabs;
|
++this._identitiesState[userContextId].openTabs;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.on("close", tab => {
|
tabs.on("close", tab => {
|
||||||
let xulTab = viewFor(tab);
|
const userContextId = this._getUserContextIdFromTab(tab);
|
||||||
let userContextId = parseInt(xulTab.getAttribute("usercontextid") || 0, 10);
|
|
||||||
if (userContextId && this._identitiesState[userContextId].openTabs) {
|
if (userContextId && this._identitiesState[userContextId].openTabs) {
|
||||||
--this._identitiesState[userContextId].openTabs;
|
--this._identitiesState[userContextId].openTabs;
|
||||||
}
|
}
|
||||||
|
@ -144,14 +145,28 @@ let ContainerService = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getUserContextIdFromTab(tab) {
|
||||||
|
return parseInt(viewFor(tab).getAttribute("usercontextid") || 0, 10);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getTabList(userContextId) {
|
||||||
|
let list = [];
|
||||||
|
for (const tab of tabs) {
|
||||||
|
if (userContextId === this._getUserContextIdFromTab(tab)) {
|
||||||
|
let object = { title: tab.title, url: tab.url, id: tab.id };
|
||||||
|
list.push(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
// Tabs management
|
// Tabs management
|
||||||
|
|
||||||
hideTabs(args) {
|
hideTabs(args) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
for (let tab of tabs) {
|
for (const tab of tabs) {
|
||||||
let xulTab = viewFor(tab);
|
const userContextId = this._getUserContextIdFromTab(tab);
|
||||||
let userContextId = parseInt(xulTab.getAttribute("usercontextid") || 0, 10);
|
|
||||||
|
|
||||||
if ("userContextId" in args && args.userContextId !== userContextId) {
|
if ("userContextId" in args && args.userContextId !== userContextId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -177,55 +192,145 @@ let ContainerService = {
|
||||||
},
|
},
|
||||||
|
|
||||||
sortTabs() {
|
sortTabs() {
|
||||||
function sortTabsInternal(window, pinnedTabs) {
|
|
||||||
// From model to XUL window.
|
|
||||||
const xulWindow = viewFor(window);
|
|
||||||
|
|
||||||
const tabs = tabsUtils.getTabs(xulWindow);
|
|
||||||
let pos = 0;
|
|
||||||
|
|
||||||
// Let's collect UCIs/tabs for this window.
|
|
||||||
let map = new Map;
|
|
||||||
for (const tab of tabs) {
|
|
||||||
if (pinnedTabs && !tabsUtils.isPinned(tab)) {
|
|
||||||
// We don't have, or we already handled all the pinned tabs.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pinnedTabs && tabsUtils.isPinned(tab)) {
|
|
||||||
// pinned tabs must be consider as taken positions.
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let userContextId = parseInt(tab.getAttribute("usercontextid") || 0, 10);
|
|
||||||
if (!map.has(userContextId)) {
|
|
||||||
map.set(userContextId, []);
|
|
||||||
}
|
|
||||||
map.get(userContextId).push(tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's sort the map.
|
|
||||||
const sortMap = new Map([...map.entries()].sort((a, b) => a[0] > b[0]));
|
|
||||||
|
|
||||||
// Let's move tabs.
|
|
||||||
sortMap.forEach(tabs => {
|
|
||||||
for (const tab of tabs) {
|
|
||||||
xulWindow.gBrowser.moveTabTo(tab, pos++);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
for (let window of windows.browserWindows) {
|
for (let window of windows.browserWindows) {
|
||||||
// First the pinned tabs, then the normal ones.
|
// First the pinned tabs, then the normal ones.
|
||||||
sortTabsInternal(window, true);
|
this._sortTabsInternal(window, true);
|
||||||
sortTabsInternal(window, false);
|
this._sortTabsInternal(window, false);
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(null);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_sortTabsInternal(window, pinnedTabs) {
|
||||||
|
// From model to XUL window.
|
||||||
|
const xulWindow = viewFor(window);
|
||||||
|
|
||||||
|
const tabs = tabsUtils.getTabs(xulWindow);
|
||||||
|
let pos = 0;
|
||||||
|
|
||||||
|
// Let's collect UCIs/tabs for this window.
|
||||||
|
let map = new Map;
|
||||||
|
for (const tab of tabs) {
|
||||||
|
if (pinnedTabs && !tabsUtils.isPinned(tab)) {
|
||||||
|
// We don't have, or we already handled all the pinned tabs.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pinnedTabs && tabsUtils.isPinned(tab)) {
|
||||||
|
// pinned tabs must be consider as taken positions.
|
||||||
|
++pos;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userContextId = this._getUserContextIdFromTab(tab);
|
||||||
|
if (!map.has(userContextId)) {
|
||||||
|
map.set(userContextId, []);
|
||||||
|
}
|
||||||
|
map.get(userContextId).push(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's sort the map.
|
||||||
|
const sortMap = new Map([...map.entries()].sort((a, b) => a[0] > b[0]));
|
||||||
|
|
||||||
|
// Let's move tabs.
|
||||||
|
sortMap.forEach(tabs => {
|
||||||
|
for (const tab of tabs) {
|
||||||
|
xulWindow.gBrowser.moveTabTo(tab, pos++);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getTabs(args) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!("userContextId" in args)) {
|
||||||
|
reject("getTabs must be called with userContextId argument.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = this._getTabList(args.userContextId);
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
for (let object of list) {
|
||||||
|
promises.push(getFavicon(object.url).then(url => {
|
||||||
|
object.favicon = url;
|
||||||
|
}, () => {
|
||||||
|
object.favicon = "";
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(promises).then(() => {
|
||||||
|
resolve(list);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showTab(args) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!("tabId" in args)) {
|
||||||
|
reject("showTab must be called with tabId argument.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tab of tabs) {
|
||||||
|
if (tab.id === args.tabId) {
|
||||||
|
tab.window.activate();
|
||||||
|
tab.activate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
moveTabsToWindow(args) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!("userContextId" in args)) {
|
||||||
|
reject("moveTabsToWindow must be called with userContextId argument.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let"s create a list of the tabs.
|
||||||
|
const list = this._getTabList(args.userContextId);
|
||||||
|
|
||||||
|
// Nothing to do
|
||||||
|
if (list.length === 0) {
|
||||||
|
resolve(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
windows.browserWindows.open({
|
||||||
|
url: "about:blank",
|
||||||
|
onOpen: window => {
|
||||||
|
const newBrowserWindow = viewFor(window);
|
||||||
|
|
||||||
|
// Let's move the tab to the new window.
|
||||||
|
for (const tab of list) {
|
||||||
|
const newTab = newBrowserWindow.gBrowser.addTab("about:blank");
|
||||||
|
newBrowserWindow.gBrowser.swapBrowsersAndCloseOther(newTab, tab);
|
||||||
|
// swapBrowsersAndCloseOther is an internal method of gBrowser
|
||||||
|
// an it's not supported by addon SDK. This means that we
|
||||||
|
// don't receive an 'open' event, but only the 'close' one.
|
||||||
|
// We have to force a +1 in our tab counter.
|
||||||
|
++this._identitiesState[args.userContextId].openTabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's close all the normal tab in the new window. In theory it
|
||||||
|
// should be only the first tab, but maybe there are addons doing
|
||||||
|
// crazy stuff.
|
||||||
|
for (const tab of window.tabs) {
|
||||||
|
const userContextId = this._getUserContextIdFromTab(tab);
|
||||||
|
if (args.userContextId !== userContextId) {
|
||||||
|
newBrowserWindow.gBrowser.removeTab(viewFor(tab));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
openTab(args) {
|
openTab(args) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
let browserWin = windowUtils.getMostRecentBrowserWindow();
|
let browserWin = windowUtils.getMostRecentBrowserWindow();
|
||||||
|
|
|
@ -45,7 +45,7 @@ table {
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.unstriped tbody tr {
|
.container-panel > table.unstriped tbody tr {
|
||||||
border-bottom: 1px solid #f1f1f1;
|
border-bottom: 1px solid #f1f1f1;
|
||||||
background-color: #fefefe;
|
background-color: #fefefe;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -62,6 +62,11 @@ table.unstriped tbody tr {
|
||||||
block-size: 32px;
|
block-size: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userContext-icon:hover {
|
||||||
|
background-image: url('/img/container-add.svg');
|
||||||
|
fill: 'gray';
|
||||||
|
}
|
||||||
|
|
||||||
.edit-identities {
|
.edit-identities {
|
||||||
background: #DCDBDC;
|
background: #DCDBDC;
|
||||||
}
|
}
|
||||||
|
@ -134,3 +139,7 @@ table.unstriped tbody tr {
|
||||||
[data-identity-icon="circle"] {
|
[data-identity-icon="circle"] {
|
||||||
--identity-icon: url("/img/usercontext.svg#circle");
|
--identity-icon: url("/img/usercontext.svg#circle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container-info-has-tabs {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
|
@ -2,33 +2,64 @@
|
||||||
const CONTAINER_HIDE_SRC = "/img/container-hide.svg";
|
const CONTAINER_HIDE_SRC = "/img/container-hide.svg";
|
||||||
const CONTAINER_UNHIDE_SRC = "/img/container-unhide.svg";
|
const CONTAINER_UNHIDE_SRC = "/img/container-unhide.svg";
|
||||||
|
|
||||||
function showOrHideContainerTabs(userContextId, hasHiddenTabs) {
|
function showContainerTabsPanel(identity) {
|
||||||
// Let"s show/hide the tabs
|
// Populating the panel: name and icon
|
||||||
return browser.runtime.sendMessage({
|
document.getElementById("container-info-name").innerText = identity.name;
|
||||||
method: hasHiddenTabs ? "showTabs" : "hideTabs",
|
|
||||||
userContextId: userContextId
|
let icon = document.getElementById("container-info-icon");
|
||||||
})
|
icon.setAttribute("data-identity-icon", identity.image);
|
||||||
// We need to retrieve the new identity configuration in order to choose the
|
icon.setAttribute("data-identity-color", identity.color);
|
||||||
// correct icon.
|
|
||||||
.then(() => {
|
// Show or not the has-tabs section.
|
||||||
return browser.runtime.sendMessage({
|
for (let trHasTabs of document.getElementsByClassName("container-info-has-tabs")) {
|
||||||
method: "getIdentity",
|
trHasTabs.hidden = !identity.hasHiddenTabs && !identity.hasOpenTabs;
|
||||||
userContextId: userContextId
|
trHasTabs.setAttribute("data-user-context-id", identity.userContextId);
|
||||||
});
|
}
|
||||||
})
|
|
||||||
// Let"s update the icon.
|
const hideShowIcon = document.getElementById("container-info-hideorshow-icon");
|
||||||
.then((identity) => {
|
hideShowIcon.src = identity.hasHiddenTabs ? CONTAINER_UNHIDE_SRC : CONTAINER_HIDE_SRC;
|
||||||
let hideorshowIcon = document.querySelector(`#uci-${identity.userContextId}-hideorshow-icon`);
|
|
||||||
if (!identity.hasHiddenTabs && !identity.hasOpenTabs) {
|
const hideShowLabel = document.getElementById("container-info-hideorshow-label");
|
||||||
hideorshowIcon.style.display = "none";
|
hideShowLabel.innerText = identity.hasHiddenTabs ? "Show these container tabs" : "Hide these container tabs";
|
||||||
} else {
|
|
||||||
hideorshowIcon.style.display = "";
|
// Let"s remove all the previous tabs.
|
||||||
|
for (const trTab of document.getElementsByClassName("container-info-tab")) {
|
||||||
|
trTab.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let"s retrieve the list of tabs.
|
||||||
|
browser.runtime.sendMessage({
|
||||||
|
method: "getTabs",
|
||||||
|
userContextId: identity.userContextId,
|
||||||
|
}).then(tabs => {
|
||||||
|
// For each one, let's create a new line.
|
||||||
|
let fragment = document.createDocumentFragment();
|
||||||
|
for (const tab of tabs) {
|
||||||
|
let tr = document.createElement("tr");
|
||||||
|
fragment.appendChild(tr);
|
||||||
|
tr.classList.add("container-info-tab");
|
||||||
|
tr.innerHTML = `
|
||||||
|
<td><img class="icon" src="${tab.favicon}" /></td>
|
||||||
|
<td>${tab.title}</td>`;
|
||||||
|
// On click, we activate this tab.
|
||||||
|
tr.addEventListener("click", () => {
|
||||||
|
browser.runtime.sendMessage({
|
||||||
|
method: "showTab",
|
||||||
|
tabId: tab.id,
|
||||||
|
}).then(() => {
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
hideorshowIcon.src = hasHiddenTabs ? CONTAINER_HIDE_SRC : CONTAINER_UNHIDE_SRC;
|
document.getElementById("container-info-table").appendChild(fragment);
|
||||||
|
})
|
||||||
|
|
||||||
// The new identity is returned.
|
// Finally we are ready to show the panel.
|
||||||
return identity;
|
.then(() => {
|
||||||
|
// FIXME: the animation...
|
||||||
|
document.getElementById("container-panel").classList.add("hide");
|
||||||
|
document.getElementById("container-info-panel").classList.remove("hide");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,59 +94,34 @@ browser.runtime.sendMessage({method: "queryIdentities"}).then(identities => {
|
||||||
let fragment = document.createDocumentFragment();
|
let fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
identities.forEach(identity => {
|
identities.forEach(identity => {
|
||||||
let hideOrShowIconSrc = CONTAINER_HIDE_SRC;
|
|
||||||
|
|
||||||
if (identity.hasHiddenTabs) {
|
|
||||||
hideOrShowIconSrc = CONTAINER_UNHIDE_SRC;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tr = document.createElement("tr");
|
let tr = document.createElement("tr");
|
||||||
fragment.appendChild(tr);
|
fragment.appendChild(tr);
|
||||||
tr.setAttribute("data-identity-cookie-store-id", identity.userContextId);
|
tr.setAttribute("data-identity-cookie-store-id", identity.userContextId);
|
||||||
tr.innerHTML = `
|
tr.innerHTML = `
|
||||||
<td>
|
<td>
|
||||||
<div class="userContext-icon"
|
<div class="userContext-icon open-newtab"
|
||||||
data-identity-icon="${identity.image}"
|
data-identity-icon="${identity.image}"
|
||||||
data-identity-color="${identity.color}">
|
data-identity-color="${identity.color}">
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>${identity.name}</td>
|
<td class="open-newtab">${identity.name}</td>
|
||||||
<td class="newtab">
|
<td class="info">></td>`;
|
||||||
<img
|
|
||||||
title="Open a new ${identity.name} container tab"
|
|
||||||
src="/img/container-add.svg"
|
|
||||||
class="icon newtab-icon" />
|
|
||||||
</td>
|
|
||||||
<td class="hideorshow" >
|
|
||||||
<img
|
|
||||||
title="Hide or show ${identity.name} container tabs"
|
|
||||||
data-identity-cookie-store-id="${identity.userContextId}"
|
|
||||||
id="uci-${identity.userContextId}-hideorshow-icon"
|
|
||||||
class="icon hideorshow-icon"
|
|
||||||
src="${hideOrShowIconSrc}"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>></td>`;
|
|
||||||
|
|
||||||
// No tabs, no icon.
|
|
||||||
if (!identity.hasHiddenTabs && !identity.hasOpenTabs) {
|
|
||||||
let hideorshowIcon = fragment.querySelector(`#uci-${identity.userContextId}-hideorshow-icon`);
|
|
||||||
hideorshowIcon.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.addEventListener("click", e => {
|
tr.addEventListener("click", e => {
|
||||||
if (e.target.matches(".hideorshow-icon")) {
|
if (e.target.matches(".open-newtab")) {
|
||||||
showOrHideContainerTabs(identity.userContextId,
|
browser.runtime.sendMessage({
|
||||||
identity.hasHiddenTabs).then(i => { identity = i; });
|
method: "showTabs",
|
||||||
} else if (e.target.matches(".newtab-icon")) {
|
userContextId: identity.userContextId
|
||||||
showOrHideContainerTabs(identity.userContextId, true).then(() => {
|
}).then(() => {
|
||||||
browser.runtime.sendMessage({
|
return browser.runtime.sendMessage({
|
||||||
method: "openTab",
|
method: "openTab",
|
||||||
userContextId: identity.userContextId
|
userContextId: identity.userContextId,
|
||||||
}).then(() => {
|
|
||||||
window.close();
|
|
||||||
});
|
});
|
||||||
|
}).then(() => {
|
||||||
|
window.close();
|
||||||
});
|
});
|
||||||
|
} else if (e.target.matches(".info")) {
|
||||||
|
showContainerTabsPanel(identity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -139,3 +145,33 @@ document.querySelector("#sort-containers-link").addEventListener("click", () =>
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelector("#close-container-info-panel").addEventListener("click", () => {
|
||||||
|
// TODO: animation
|
||||||
|
document.getElementById("container-info-panel").classList.add("hide");
|
||||||
|
document.getElementById("container-panel").classList.remove("hide");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector("#container-info-hideorshow").addEventListener("click", e => {
|
||||||
|
let userContextId = e.target.parentElement.getAttribute("data-user-context-id");
|
||||||
|
browser.runtime.sendMessage({
|
||||||
|
method: "getIdentity",
|
||||||
|
userContextId,
|
||||||
|
}).then(identity => {
|
||||||
|
return browser.runtime.sendMessage({
|
||||||
|
method: identity.hasHiddenTabs ? "showTabs" : "hideTabs",
|
||||||
|
userContextId: identity.userContextId
|
||||||
|
});
|
||||||
|
}).then(() => {
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector("#container-info-movetabs").addEventListener("click", e => {
|
||||||
|
return browser.runtime.sendMessage({
|
||||||
|
method: "moveTabsToWindow",
|
||||||
|
userContextId: e.target.parentElement.getAttribute("data-user-context-id"),
|
||||||
|
}).then(() => {
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -40,6 +40,29 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="hide panel container-info-panel" id="container-info-panel">
|
||||||
|
<div class="row popup-bumper">
|
||||||
|
<div class="small-1 columns" id="close-container-info-panel"><</div>
|
||||||
|
<div class="small-11 columns">
|
||||||
|
<table id="container-info-table">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="userContext-icon" id="container-info-icon"
|
||||||
|
data-identity-icon="" data-identity-color=""></div></td>
|
||||||
|
<td id="container-info-name"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="container-info-has-tabs" id="container-info-hideorshow">
|
||||||
|
<td>
|
||||||
|
<img class="icon" id="container-info-hideorshow-icon" src="" />
|
||||||
|
</td>
|
||||||
|
<td id="container-info-hideorshow-label"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="container-info-has-tabs" id="container-info-movetabs">
|
||||||
|
<td colspan="2">Open all tabs in new window</td>
|
||||||
|
</tr>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script src="js/popup.js"></script>
|
<script src="js/popup.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Reference in a new issue