cherry picked
This commit is contained in:
parent
44206f3400
commit
2ae3a4d9bf
9 changed files with 304 additions and 173 deletions
|
@ -46,7 +46,7 @@ module.exports = {
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"escape": {
|
"escape": {
|
||||||
"taggedTemplates": ["escaped"]
|
"taggedTemplates": ["escaped", "Utils.escaped"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -8,6 +8,7 @@ module.exports = {
|
||||||
"backgroundLogic": true,
|
"backgroundLogic": true,
|
||||||
"identityState": true,
|
"identityState": true,
|
||||||
"messageHandler": true,
|
"messageHandler": true,
|
||||||
"sync": true
|
"sync": true,
|
||||||
|
"Utils": true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -474,6 +474,7 @@ window.assignManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) {
|
async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) {
|
||||||
|
console.log(userContextId)
|
||||||
let actionName;
|
let actionName;
|
||||||
|
|
||||||
// https://github.com/mozilla/testpilot-containers/issues/626
|
// https://github.com/mozilla/testpilot-containers/issues/626
|
||||||
|
|
|
@ -73,6 +73,16 @@ const messageHandler = {
|
||||||
case "exemptContainerAssignment":
|
case "exemptContainerAssignment":
|
||||||
response = assignManager._exemptTab(m);
|
response = assignManager._exemptTab(m);
|
||||||
break;
|
break;
|
||||||
|
case "reloadInContainer":
|
||||||
|
response = assignManager.reloadPageInContainer(
|
||||||
|
m.url,
|
||||||
|
m.currentUserContextId,
|
||||||
|
m.newUserContextId,
|
||||||
|
m.tabIndex,
|
||||||
|
m.active,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
|
|
51
src/js/pageAction.js
Normal file
51
src/js/pageAction.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
async function init() {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
const identities = await browser.contextualIdentities.query({});
|
||||||
|
|
||||||
|
identities.forEach(identity => {
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
tr.classList.add("menu-item");
|
||||||
|
const td = document.createElement("td");
|
||||||
|
|
||||||
|
td.innerHTML = Utils.escaped`
|
||||||
|
<div class="menu-icon">
|
||||||
|
<div class="usercontext-icon"
|
||||||
|
data-identity-icon="${identity.icon}"
|
||||||
|
data-identity-color="${identity.color}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="menu-text">${identity.name}</span>`;
|
||||||
|
|
||||||
|
fragment.appendChild(tr);
|
||||||
|
|
||||||
|
tr.appendChild(td);
|
||||||
|
|
||||||
|
Utils.addEnterHandler(tr, async () => {
|
||||||
|
const currentTab = await Utils.currentTab();
|
||||||
|
const assignedUserContextId = Utils.userContextId(identity.cookieStoreId);
|
||||||
|
Utils.setOrRemoveAssignment(
|
||||||
|
currentTab.id,
|
||||||
|
currentTab.url,
|
||||||
|
assignedUserContextId,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
console.log(currentTab);
|
||||||
|
Utils.reloadInContainer(
|
||||||
|
currentTab.url,
|
||||||
|
false,
|
||||||
|
assignedUserContextId,
|
||||||
|
currentTab.index + 1,
|
||||||
|
currentTab.active
|
||||||
|
);
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const list = document.querySelector("#picker-identities-list");
|
||||||
|
|
||||||
|
list.innerHTML = "";
|
||||||
|
list.appendChild(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
282
src/js/popup.js
282
src/js/popup.js
|
@ -26,41 +26,6 @@ const P_CONTAINER_EDIT = "containerEdit";
|
||||||
const P_CONTAINER_DELETE = "containerDelete";
|
const P_CONTAINER_DELETE = "containerDelete";
|
||||||
const P_CONTAINERS_ACHIEVEMENT = "containersAchievement";
|
const P_CONTAINERS_ACHIEVEMENT = "containersAchievement";
|
||||||
|
|
||||||
/**
|
|
||||||
* Escapes any occurances of &, ", <, > or / with XML entities.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* The string to escape.
|
|
||||||
* @return {string} The escaped string.
|
|
||||||
*/
|
|
||||||
function escapeXML(str) {
|
|
||||||
const replacements = { "&": "&", "\"": """, "'": "'", "<": "<", ">": ">", "/": "/" };
|
|
||||||
return String(str).replace(/[&"'<>/]/g, m => replacements[m]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A tagged template function which escapes any XML metacharacters in
|
|
||||||
* interpolated values.
|
|
||||||
*
|
|
||||||
* @param {Array<string>} strings
|
|
||||||
* An array of literal strings extracted from the templates.
|
|
||||||
* @param {Array} values
|
|
||||||
* An array of interpolated values extracted from the template.
|
|
||||||
* @returns {string}
|
|
||||||
* The result of the escaped values interpolated with the literal
|
|
||||||
* strings.
|
|
||||||
*/
|
|
||||||
function escaped(strings, ...values) {
|
|
||||||
const result = [];
|
|
||||||
|
|
||||||
for (const [i, string] of strings.entries()) {
|
|
||||||
result.push(string);
|
|
||||||
if (i < values.length)
|
|
||||||
result.push(escapeXML(values[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getExtensionInfo() {
|
async function getExtensionInfo() {
|
||||||
const manifestPath = browser.extension.getURL("manifest.json");
|
const manifestPath = browser.extension.getURL("manifest.json");
|
||||||
|
@ -197,31 +162,6 @@ const Logic = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addEnterHandler(element, handler) {
|
|
||||||
element.addEventListener("click", (e) => {
|
|
||||||
handler(e);
|
|
||||||
});
|
|
||||||
element.addEventListener("keydown", (e) => {
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
e.preventDefault();
|
|
||||||
handler(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
userContextId(cookieStoreId = "") {
|
|
||||||
const userContextId = cookieStoreId.replace("firefox-container-", "");
|
|
||||||
return (userContextId !== cookieStoreId) ? Number(userContextId) : false;
|
|
||||||
},
|
|
||||||
|
|
||||||
async currentTab() {
|
|
||||||
const activeTabs = await browser.tabs.query({ active: true, windowId: browser.windows.WINDOW_ID_CURRENT });
|
|
||||||
if (activeTabs.length > 0) {
|
|
||||||
return activeTabs[0];
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
async numTabs() {
|
async numTabs() {
|
||||||
const activeTabs = await browser.tabs.query({ windowId: browser.windows.WINDOW_ID_CURRENT });
|
const activeTabs = await browser.tabs.query({ windowId: browser.windows.WINDOW_ID_CURRENT });
|
||||||
return activeTabs.length;
|
return activeTabs.length;
|
||||||
|
@ -333,7 +273,7 @@ const Logic = {
|
||||||
|
|
||||||
currentUserContextId() {
|
currentUserContextId() {
|
||||||
const identity = Logic.currentIdentity();
|
const identity = Logic.currentIdentity();
|
||||||
return Logic.userContextId(identity.cookieStoreId);
|
return Utils.userContextId(identity.cookieStoreId);
|
||||||
},
|
},
|
||||||
|
|
||||||
currentCookieStoreId() {
|
currentCookieStoreId() {
|
||||||
|
@ -369,16 +309,6 @@ const Logic = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setOrRemoveAssignment(tabId, url, userContextId, value) {
|
|
||||||
return browser.runtime.sendMessage({
|
|
||||||
method: "setOrRemoveAssignment",
|
|
||||||
tabId,
|
|
||||||
url,
|
|
||||||
userContextId,
|
|
||||||
value
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
generateIdentityName() {
|
generateIdentityName() {
|
||||||
const defaultName = "Container #";
|
const defaultName = "Container #";
|
||||||
const ids = [];
|
const ids = [];
|
||||||
|
@ -418,7 +348,7 @@ Logic.registerPanel(P_ONBOARDING_1, {
|
||||||
initialize() {
|
initialize() {
|
||||||
// Let's move to the next panel.
|
// Let's move to the next panel.
|
||||||
[...document.querySelectorAll(".onboarding-start-button")].forEach(startElement => {
|
[...document.querySelectorAll(".onboarding-start-button")].forEach(startElement => {
|
||||||
Logic.addEnterHandler(startElement, async () => {
|
Utils.addEnterHandler(startElement, async () => {
|
||||||
await Logic.setOnboardingStage(1);
|
await Logic.setOnboardingStage(1);
|
||||||
Logic.showPanel(P_ONBOARDING_2);
|
Logic.showPanel(P_ONBOARDING_2);
|
||||||
});
|
});
|
||||||
|
@ -442,7 +372,7 @@ Logic.registerPanel(P_ONBOARDING_2, {
|
||||||
initialize() {
|
initialize() {
|
||||||
// Let's move to the containers list panel.
|
// Let's move to the containers list panel.
|
||||||
[...document.querySelectorAll(".onboarding-next-button")].forEach(nextElement => {
|
[...document.querySelectorAll(".onboarding-next-button")].forEach(nextElement => {
|
||||||
Logic.addEnterHandler(nextElement, async () => {
|
Utils.addEnterHandler(nextElement, async () => {
|
||||||
await Logic.setOnboardingStage(2);
|
await Logic.setOnboardingStage(2);
|
||||||
Logic.showPanel(P_ONBOARDING_3);
|
Logic.showPanel(P_ONBOARDING_3);
|
||||||
});
|
});
|
||||||
|
@ -466,7 +396,7 @@ Logic.registerPanel(P_ONBOARDING_3, {
|
||||||
initialize() {
|
initialize() {
|
||||||
// Let's move to the containers list panel.
|
// Let's move to the containers list panel.
|
||||||
[...document.querySelectorAll(".onboarding-almost-done-button")].forEach(almostElement => {
|
[...document.querySelectorAll(".onboarding-almost-done-button")].forEach(almostElement => {
|
||||||
Logic.addEnterHandler(almostElement, async () => {
|
Utils.addEnterHandler(almostElement, async () => {
|
||||||
await Logic.setOnboardingStage(3);
|
await Logic.setOnboardingStage(3);
|
||||||
Logic.showPanel(P_ONBOARDING_4);
|
Logic.showPanel(P_ONBOARDING_4);
|
||||||
});
|
});
|
||||||
|
@ -488,7 +418,7 @@ Logic.registerPanel(P_ONBOARDING_4, {
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
// Let's move to the containers list panel.
|
// Let's move to the containers list panel.
|
||||||
Logic.addEnterHandler(document.querySelector("#onboarding-done-button"), async () => {
|
Utils.addEnterHandler(document.querySelector("#onboarding-done-button"), async () => {
|
||||||
await Logic.setOnboardingStage(4);
|
await Logic.setOnboardingStage(4);
|
||||||
Logic.showPanel(P_ONBOARDING_5);
|
Logic.showPanel(P_ONBOARDING_5);
|
||||||
});
|
});
|
||||||
|
@ -509,7 +439,7 @@ Logic.registerPanel(P_ONBOARDING_5, {
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
// Let's move to the containers list panel.
|
// Let's move to the containers list panel.
|
||||||
Logic.addEnterHandler(document.querySelector("#onboarding-longpress-button"), async () => {
|
Utils.addEnterHandler(document.querySelector("#onboarding-longpress-button"), async () => {
|
||||||
await Logic.setOnboardingStage(5);
|
await Logic.setOnboardingStage(5);
|
||||||
Logic.showPanel(P_ONBOARDING_6);
|
Logic.showPanel(P_ONBOARDING_6);
|
||||||
});
|
});
|
||||||
|
@ -530,7 +460,7 @@ Logic.registerPanel(P_ONBOARDING_6, {
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
// Let's move to the containers list panel.
|
// Let's move to the containers list panel.
|
||||||
Logic.addEnterHandler(document.querySelector("#start-sync-button"), async () => {
|
Utils.addEnterHandler(document.querySelector("#start-sync-button"), async () => {
|
||||||
await Logic.setOnboardingStage(6);
|
await Logic.setOnboardingStage(6);
|
||||||
await browser.storage.local.set({syncEnabled: true});
|
await browser.storage.local.set({syncEnabled: true});
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
|
@ -538,7 +468,7 @@ Logic.registerPanel(P_ONBOARDING_6, {
|
||||||
});
|
});
|
||||||
Logic.showPanel(P_ONBOARDING_7);
|
Logic.showPanel(P_ONBOARDING_7);
|
||||||
});
|
});
|
||||||
Logic.addEnterHandler(document.querySelector("#no-sync"), async () => {
|
Utils.addEnterHandler(document.querySelector("#no-sync"), async () => {
|
||||||
await Logic.setOnboardingStage(7);
|
await Logic.setOnboardingStage(7);
|
||||||
await browser.storage.local.set({syncEnabled: false});
|
await browser.storage.local.set({syncEnabled: false});
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
|
@ -563,14 +493,14 @@ Logic.registerPanel(P_ONBOARDING_7, {
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
// Let's move to the containers list panel.
|
// Let's move to the containers list panel.
|
||||||
Logic.addEnterHandler(document.querySelector("#sign-in"), async () => {
|
Utils.addEnterHandler(document.querySelector("#sign-in"), async () => {
|
||||||
browser.tabs.create({
|
browser.tabs.create({
|
||||||
url: "https://accounts.firefox.com/?service=sync&action=email&context=fx_desktop_v3&entrypoint=multi-account-containers&utm_source=addon&utm_medium=panel&utm_campaign=container-sync",
|
url: "https://accounts.firefox.com/?service=sync&action=email&context=fx_desktop_v3&entrypoint=multi-account-containers&utm_source=addon&utm_medium=panel&utm_campaign=container-sync",
|
||||||
});
|
});
|
||||||
await Logic.setOnboardingStage(7);
|
await Logic.setOnboardingStage(7);
|
||||||
Logic.showPanel(P_CONTAINERS_LIST);
|
Logic.showPanel(P_CONTAINERS_LIST);
|
||||||
});
|
});
|
||||||
Logic.addEnterHandler(document.querySelector("#no-sign-in"), async () => {
|
Utils.addEnterHandler(document.querySelector("#no-sign-in"), async () => {
|
||||||
await Logic.setOnboardingStage(7);
|
await Logic.setOnboardingStage(7);
|
||||||
Logic.showPanel(P_CONTAINERS_LIST);
|
Logic.showPanel(P_CONTAINERS_LIST);
|
||||||
});
|
});
|
||||||
|
@ -589,17 +519,21 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
|
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
async initialize() {
|
async initialize() {
|
||||||
Logic.addEnterHandler(document.querySelector("#container-add-link"), () => {
|
Utils.addEnterHandler(document.querySelector("#manage-containers-link"), (e) => {
|
||||||
Logic.showPanel(P_CONTAINER_EDIT, { name: Logic.generateIdentityName() });
|
|
||||||
});
|
|
||||||
|
|
||||||
Logic.addEnterHandler(document.querySelector("#edit-containers-link"), (e) => {
|
|
||||||
if (!e.target.classList.contains("disable-edit-containers")) {
|
if (!e.target.classList.contains("disable-edit-containers")) {
|
||||||
Logic.showPanel(P_CONTAINERS_EDIT);
|
Logic.showPanel(P_CONTAINERS_EDIT);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Utils.addEnterHandler(document.querySelector("#open-new-tab-in"), () => {
|
||||||
Logic.addEnterHandler(document.querySelector("#sort-containers-link"), async () => {
|
Logic.showPanel(P_CONTAINER_PICKER, null, OPEN_NEW_CONTAINER_PICKER);
|
||||||
|
});
|
||||||
|
Utils.addEnterHandler(document.querySelector("#reopen-site-in"), () => {
|
||||||
|
Logic.showPanel(P_CONTAINER_PICKER, null, REOPEN_IN_CONTAINER);
|
||||||
|
});
|
||||||
|
Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
|
||||||
|
Logic.showPanel(P_CONTAINER_PICKER, null, ALWAYS_OPEN_IN_PICKER);
|
||||||
|
});
|
||||||
|
Utils.addEnterHandler(document.querySelector("#sort-containers-link"), async () => {
|
||||||
try {
|
try {
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
method: "sortTabs"
|
method: "sortTabs"
|
||||||
|
@ -737,18 +671,12 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
Logic.identities().forEach(identity => {
|
Logic.identities().forEach(identity => {
|
||||||
const hasTabs = (identity.hasHiddenTabs || identity.hasOpenTabs);
|
const hasTabs = (identity.hasHiddenTabs || identity.hasOpenTabs);
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
const context = document.createElement("td");
|
tr.classList.add("menu-item");
|
||||||
const manage = document.createElement("td");
|
const td = document.createElement("td");
|
||||||
|
const openTabs = identity.numberOfOpenTabs || "" ;
|
||||||
|
|
||||||
tr.classList.add("container-panel-row");
|
td.innerHTML = Utils.escaped`
|
||||||
|
<div class="menu-icon">
|
||||||
context.classList.add("userContext-wrapper", "open-newtab", "clickable", "firstTabindex");
|
|
||||||
manage.classList.add("show-tabs", "pop-button");
|
|
||||||
manage.setAttribute("title", `View ${identity.name} container`);
|
|
||||||
context.setAttribute("tabindex", "0");
|
|
||||||
context.setAttribute("title", `Create ${identity.name} tab`);
|
|
||||||
context.innerHTML = escaped`
|
|
||||||
<div class="userContext-icon-wrapper open-newtab">
|
|
||||||
<div class="usercontext-icon"
|
<div class="usercontext-icon"
|
||||||
data-identity-icon="${identity.icon}"
|
data-identity-icon="${identity.icon}"
|
||||||
data-identity-color="${identity.color}">
|
data-identity-color="${identity.color}">
|
||||||
|
@ -762,25 +690,8 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
|
|
||||||
tr.appendChild(context);
|
tr.appendChild(context);
|
||||||
|
|
||||||
if (hasTabs) {
|
Utils.addEnterHandler(tr, () => {
|
||||||
tr.appendChild(manage);
|
Logic.showPanel(P_CONTAINER_INFO, identity);
|
||||||
}
|
|
||||||
|
|
||||||
Logic.addEnterHandler(tr, async (e) => {
|
|
||||||
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) {
|
|
||||||
Logic.showPanel(P_CONTAINER_INFO, identity);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -827,12 +738,12 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
const closeContEl = document.querySelector("#close-container-info-panel");
|
const closeContEl = document.querySelector("#close-container-info-panel");
|
||||||
closeContEl.setAttribute("tabindex", "0");
|
closeContEl.setAttribute("tabindex", "0");
|
||||||
closeContEl.classList.add("firstTabindex");
|
closeContEl.classList.add("firstTabindex");
|
||||||
Logic.addEnterHandler(closeContEl, () => {
|
Utils.addEnterHandler(closeContEl, () => {
|
||||||
Logic.showPreviousPanel();
|
Logic.showPreviousPanel();
|
||||||
});
|
});
|
||||||
const hideContEl = document.querySelector("#container-info-hideorshow");
|
const hideContEl = document.querySelector("#container-info-hideorshow");
|
||||||
hideContEl.setAttribute("tabindex", "0");
|
hideContEl.setAttribute("tabindex", "0");
|
||||||
Logic.addEnterHandler(hideContEl, async () => {
|
Utils.addEnterHandler(hideContEl, async () => {
|
||||||
const identity = Logic.currentIdentity();
|
const identity = Logic.currentIdentity();
|
||||||
try {
|
try {
|
||||||
browser.runtime.sendMessage({
|
browser.runtime.sendMessage({
|
||||||
|
@ -865,7 +776,7 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
Logic._disableMoveTabs("Cannot move a tab from a single-tab window.");
|
Logic._disableMoveTabs("Cannot move a tab from a single-tab window.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Logic.addEnterHandler(moveTabsEl, async () => {
|
Utils.addEnterHandler(moveTabsEl, async () => {
|
||||||
await browser.runtime.sendMessage({
|
await browser.runtime.sendMessage({
|
||||||
method: "moveTabsToWindow",
|
method: "moveTabsToWindow",
|
||||||
windowId: browser.windows.WINDOW_ID_CURRENT,
|
windowId: browser.windows.WINDOW_ID_CURRENT,
|
||||||
|
@ -919,7 +830,7 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
fragment.appendChild(tr);
|
fragment.appendChild(tr);
|
||||||
tr.classList.add("container-info-tab-row");
|
tr.classList.add("container-info-tab-row");
|
||||||
tr.innerHTML = escaped`
|
tr.innerHTML = Utils.escaped`
|
||||||
<td></td>
|
<td></td>
|
||||||
<td class="container-info-tab-title truncate-text" title="${tab.url}" ><div class="container-tab-title">${tab.title}</div></td>`;
|
<td class="container-info-tab-title truncate-text" title="${tab.url}" ><div class="container-tab-title">${tab.title}</div></td>`;
|
||||||
tr.querySelector("td").appendChild(Utils.createFavIconElement(tab.favIconUrl));
|
tr.querySelector("td").appendChild(Utils.createFavIconElement(tab.favIconUrl));
|
||||||
|
@ -946,14 +857,14 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
tr.addEventListener("mouseout", tabTitleHoverEvent);
|
tr.addEventListener("mouseout", tabTitleHoverEvent);
|
||||||
|
|
||||||
tr.classList.add("clickable");
|
tr.classList.add("clickable");
|
||||||
Logic.addEnterHandler(tr, async () => {
|
Utils.addEnterHandler(tr, async () => {
|
||||||
await browser.tabs.update(tab.id, { active: true });
|
await browser.tabs.update(tab.id, { active: true });
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
const closeTab = document.getElementById(tab.id);
|
const closeTab = document.getElementById(tab.id);
|
||||||
if (closeTab) {
|
if (closeTab) {
|
||||||
Logic.addEnterHandler(closeTab, async (e) => {
|
Utils.addEnterHandler(closeTab, async (e) => {
|
||||||
await browser.tabs.remove(Number(e.target.id));
|
await browser.tabs.remove(Number(e.target.id));
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
|
@ -971,50 +882,81 @@ Logic.registerPanel(P_CONTAINERS_EDIT, {
|
||||||
|
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
Logic.addEnterHandler(document.querySelector("#exit-edit-mode-link"), () => {
|
// Utils.addEnterHandler(document.querySelector("#exit-edit-mode-link"), () => {
|
||||||
Logic.showPanel(P_CONTAINERS_LIST);
|
// Logic.showPanel(P_CONTAINERS_LIST);
|
||||||
});
|
// });
|
||||||
},
|
},
|
||||||
|
|
||||||
// This method is called when the panel is shown.
|
// This method is called when the panel is shown.
|
||||||
prepare() {
|
prepare() {
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
|
let pickedFunction;
|
||||||
|
switch (Logic.pickerType) {
|
||||||
|
case OPEN_NEW_CONTAINER_PICKER:
|
||||||
|
pickedFunction = function (identity) {
|
||||||
|
try {
|
||||||
|
browser.tabs.create({
|
||||||
|
cookieStoreId: identity.cookieStoreId
|
||||||
|
});
|
||||||
|
window.close();
|
||||||
|
} catch (e) {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case MANAGE_CONTAINERS_PICKER:
|
||||||
|
pickedFunction = function (identity) {
|
||||||
|
Logic.showPanel(P_CONTAINER_EDIT, identity);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case REOPEN_IN_CONTAINER:
|
||||||
|
pickedFunction = function (identity) {
|
||||||
|
try {
|
||||||
|
browser.tabs.create({
|
||||||
|
cookieStoreId: identity.cookieStoreId
|
||||||
|
});
|
||||||
|
window.close();
|
||||||
|
} catch (e) {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case ALWAYS_OPEN_IN_PICKER:
|
||||||
|
default:
|
||||||
|
pickedFunction = async function (identity) {
|
||||||
|
const userContextId = Utils.userContextId(identity.cookieStoreId);
|
||||||
|
const currentTab = await Utils.currentTab();
|
||||||
|
Utils.setOrRemoveAssignment(
|
||||||
|
currentTab.id,
|
||||||
|
currentTab.url,
|
||||||
|
userContextId,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
window.close();
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Logic.identities().forEach(identity => {
|
Logic.identities().forEach(identity => {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
fragment.appendChild(tr);
|
tr.classList.add("menu-item");
|
||||||
tr.classList.add("container-panel-row");
|
const td = document.createElement("td");
|
||||||
tr.innerHTML = escaped`
|
|
||||||
<td class="userContext-wrapper">
|
td.innerHTML = Utils.escaped`
|
||||||
<div class="userContext-icon-wrapper">
|
<div class="menu-icon">
|
||||||
<div class="usercontext-icon"
|
<div class="usercontext-icon"
|
||||||
data-identity-icon="${identity.icon}"
|
data-identity-icon="${identity.icon}"
|
||||||
data-identity-color="${identity.color}">
|
data-identity-color="${identity.color}">
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container-name truncate-text"></div>
|
</div>
|
||||||
</td>
|
<span class="menu-text">${identity.name}</span>`;
|
||||||
<td class="edit-container pop-button edit-container-icon">
|
|
||||||
<img
|
|
||||||
src="/img/container-edit.svg"
|
|
||||||
class="pop-button-image" />
|
|
||||||
</td>
|
|
||||||
<td class="remove-container pop-button delete-container-icon">
|
|
||||||
<img
|
|
||||||
class="pop-button-image"
|
|
||||||
src="/img/container-delete.svg"
|
|
||||||
/>
|
|
||||||
</td>`;
|
|
||||||
tr.querySelector(".container-name").textContent = identity.name;
|
|
||||||
tr.querySelector(".edit-container").setAttribute("title", `Edit ${identity.name} container`);
|
|
||||||
tr.querySelector(".remove-container").setAttribute("title", `Remove ${identity.name} container`);
|
|
||||||
|
|
||||||
|
fragment.appendChild(tr);
|
||||||
|
|
||||||
Logic.addEnterHandler(tr, e => {
|
tr.appendChild(td);
|
||||||
if (e.target.matches(".edit-container-icon") || e.target.parentNode.matches(".edit-container-icon")) {
|
|
||||||
Logic.showPanel(P_CONTAINER_EDIT, identity);
|
Utils.addEnterHandler(tr, () => {
|
||||||
} else if (e.target.matches(".delete-container-icon") || e.target.parentNode.matches(".delete-container-icon")) {
|
pickedFunction(identity);
|
||||||
Logic.showPanel(P_CONTAINER_DELETE, identity);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1037,7 +979,7 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
initialize() {
|
initialize() {
|
||||||
this.initializeRadioButtons();
|
this.initializeRadioButtons();
|
||||||
|
|
||||||
Logic.addEnterHandler(document.querySelector("#edit-container-panel-back-arrow"), () => {
|
Utils.addEnterHandler(document.querySelector("#edit-container-panel-back-arrow"), () => {
|
||||||
const formValues = new FormData(this._editForm);
|
const formValues = new FormData(this._editForm);
|
||||||
if (formValues.get("container-id") !== NEW_CONTAINER_ID) {
|
if (formValues.get("container-id") !== NEW_CONTAINER_ID) {
|
||||||
this._submitForm();
|
this._submitForm();
|
||||||
|
@ -1046,13 +988,13 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Logic.addEnterHandler(document.querySelector("#edit-container-cancel-link"), () => {
|
Utils.addEnterHandler(document.querySelector("#edit-container-cancel-link"), () => {
|
||||||
Logic.showPreviousPanel();
|
Logic.showPreviousPanel();
|
||||||
});
|
});
|
||||||
|
|
||||||
this._editForm = document.getElementById("edit-container-panel-form");
|
this._editForm = document.getElementById("edit-container-panel-form");
|
||||||
const editLink = document.querySelector("#edit-container-ok-link");
|
const editLink = document.querySelector("#edit-container-ok-link");
|
||||||
Logic.addEnterHandler(editLink, () => {
|
Utils.addEnterHandler(editLink, () => {
|
||||||
this._submitForm();
|
this._submitForm();
|
||||||
});
|
});
|
||||||
editLink.addEventListener("submit", () => {
|
editLink.addEventListener("submit", () => {
|
||||||
|
@ -1104,7 +1046,7 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
/* As we don't have the full or correct path the best we can assume is the path is HTTPS and then replace with a broken icon later if it doesn't load.
|
/* As we don't have the full or correct path the best we can assume is the path is HTTPS and then replace with a broken icon later if it doesn't load.
|
||||||
This is pending a better solution for favicons from web extensions */
|
This is pending a better solution for favicons from web extensions */
|
||||||
const assumedUrl = `https://${site.hostname}/favicon.ico`;
|
const assumedUrl = `https://${site.hostname}/favicon.ico`;
|
||||||
trElement.innerHTML = escaped`
|
trElement.innerHTML = Utils.escaped`
|
||||||
<div class="favicon"></div>
|
<div class="favicon"></div>
|
||||||
<div title="${site.hostname}" class="truncate-text hostname">
|
<div title="${site.hostname}" class="truncate-text hostname">
|
||||||
${site.hostname}
|
${site.hostname}
|
||||||
|
@ -1116,12 +1058,12 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
trElement.getElementsByClassName("favicon")[0].appendChild(Utils.createFavIconElement(assumedUrl));
|
trElement.getElementsByClassName("favicon")[0].appendChild(Utils.createFavIconElement(assumedUrl));
|
||||||
const deleteButton = trElement.querySelector(".delete-assignment");
|
const deleteButton = trElement.querySelector(".delete-assignment");
|
||||||
const that = this;
|
const that = this;
|
||||||
Logic.addEnterHandler(deleteButton, async () => {
|
Utils.addEnterHandler(deleteButton, async () => {
|
||||||
const userContextId = Logic.currentUserContextId();
|
const userContextId = Logic.currentUserContextId();
|
||||||
// Lets show the message to the current tab
|
// Lets show the message to the current tab
|
||||||
// TODO remove then when firefox supports arrow fn async
|
// TODO remove then when firefox supports arrow fn async
|
||||||
const currentTab = await Logic.currentTab();
|
const currentTab = await Utils.currentTab();
|
||||||
Logic.setOrRemoveAssignment(currentTab.id, assumedUrl, userContextId, true);
|
Utils.setOrRemoveAssignment(currentTab.id, assumedUrl, userContextId, true);
|
||||||
delete assignments[siteKey];
|
delete assignments[siteKey];
|
||||||
that.showAssignedContainers(assignments);
|
that.showAssignedContainers(assignments);
|
||||||
});
|
});
|
||||||
|
@ -1133,7 +1075,7 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
|
|
||||||
initializeRadioButtons() {
|
initializeRadioButtons() {
|
||||||
const colorRadioTemplate = (containerColor) => {
|
const colorRadioTemplate = (containerColor) => {
|
||||||
return escaped`<input type="radio" value="${containerColor}" name="container-color" id="edit-container-panel-choose-color-${containerColor}" />
|
return Utils.escaped`<input type="radio" value="${containerColor}" name="container-color" id="edit-container-panel-choose-color-${containerColor}" />
|
||||||
<label for="edit-container-panel-choose-color-${containerColor}" class="usercontext-icon choose-color-icon" data-identity-icon="circle" data-identity-color="${containerColor}">`;
|
<label for="edit-container-panel-choose-color-${containerColor}" class="usercontext-icon choose-color-icon" data-identity-icon="circle" data-identity-color="${containerColor}">`;
|
||||||
};
|
};
|
||||||
const colors = ["blue", "turquoise", "green", "yellow", "orange", "red", "pink", "purple"];
|
const colors = ["blue", "turquoise", "green", "yellow", "orange", "red", "pink", "purple"];
|
||||||
|
@ -1147,7 +1089,7 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
});
|
});
|
||||||
|
|
||||||
const iconRadioTemplate = (containerIcon) => {
|
const iconRadioTemplate = (containerIcon) => {
|
||||||
return escaped`<input type="radio" value="${containerIcon}" name="container-icon" id="edit-container-panel-choose-icon-${containerIcon}" />
|
return Utils.escaped`<input type="radio" value="${containerIcon}" name="container-icon" id="edit-container-panel-choose-icon-${containerIcon}" />
|
||||||
<label for="edit-container-panel-choose-icon-${containerIcon}" class="usercontext-icon choose-color-icon" data-identity-color="grey" data-identity-icon="${containerIcon}">`;
|
<label for="edit-container-panel-choose-icon-${containerIcon}" class="usercontext-icon choose-color-icon" data-identity-color="grey" data-identity-icon="${containerIcon}">`;
|
||||||
};
|
};
|
||||||
const icons = ["fingerprint", "briefcase", "dollar", "cart", "vacation", "gift", "food", "fruit", "pet", "tree", "chill", "circle"];
|
const icons = ["fingerprint", "briefcase", "dollar", "cart", "vacation", "gift", "food", "fruit", "pet", "tree", "chill", "circle"];
|
||||||
|
@ -1197,18 +1139,18 @@ Logic.registerPanel(P_CONTAINER_DELETE, {
|
||||||
|
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
Logic.addEnterHandler(document.querySelector("#delete-container-cancel-link"), () => {
|
Utils.addEnterHandler(document.querySelector("#delete-container-cancel-link"), () => {
|
||||||
Logic.showPreviousPanel();
|
Logic.showPreviousPanel();
|
||||||
});
|
});
|
||||||
|
|
||||||
Logic.addEnterHandler(document.querySelector("#delete-container-ok-link"), async () => {
|
Utils.addEnterHandler(document.querySelector("#delete-container-ok-link"), async () => {
|
||||||
/* This promise wont resolve if the last tab was removed from the window.
|
/* This promise wont resolve if the last tab was removed from the window.
|
||||||
as the message async callback stops listening, this isn't an issue for us however it might be in future
|
as the message async callback stops listening, this isn't an issue for us however it might be in future
|
||||||
if you want to do anything post delete do it in the background script.
|
if you want to do anything post delete do it in the background script.
|
||||||
Browser console currently warns about not listening also.
|
Browser console currently warns about not listening also.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
await Logic.removeIdentity(Logic.userContextId(Logic.currentIdentity().cookieStoreId));
|
await Logic.removeIdentity(Utils.userContextId(Logic.currentIdentity().cookieStoreId));
|
||||||
await Logic.refreshIdentities();
|
await Logic.refreshIdentities();
|
||||||
Logic.showPreviousPanel();
|
Logic.showPreviousPanel();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1249,7 +1191,7 @@ Logic.registerPanel(P_CONTAINERS_ACHIEVEMENT, {
|
||||||
// This method is called when the object is registered.
|
// This method is called when the object is registered.
|
||||||
initialize() {
|
initialize() {
|
||||||
// Set done and move to the containers list panel.
|
// Set done and move to the containers list panel.
|
||||||
Logic.addEnterHandler(document.querySelector("#achievement-done-button"), async () => {
|
Utils.addEnterHandler(document.querySelector("#achievement-done-button"), async () => {
|
||||||
await Logic.setAchievementDone("manyContainersOpened");
|
await Logic.setAchievementDone("manyContainersOpened");
|
||||||
Logic.showPanel(P_CONTAINERS_LIST);
|
Logic.showPanel(P_CONTAINERS_LIST);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const DEFAULT_FAVICON = "/img/blank-favicon.svg";
|
const DEFAULT_FAVICON = "/img/blank-favicon.svg";
|
||||||
|
|
||||||
// TODO use export here instead of globals
|
// TODO use export here instead of globals
|
||||||
window.Utils = {
|
const Utils = {
|
||||||
|
|
||||||
createFavIconElement(url) {
|
createFavIconElement(url) {
|
||||||
const imageElement = document.createElement("img");
|
const imageElement = document.createElement("img");
|
||||||
|
@ -18,6 +18,90 @@ window.Utils = {
|
||||||
imageElement.addEventListener("error", errorListener);
|
imageElement.addEventListener("error", errorListener);
|
||||||
imageElement.addEventListener("load", loadListener);
|
imageElement.addEventListener("load", loadListener);
|
||||||
return imageElement;
|
return imageElement;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Escapes any occurances of &, ", <, > or / with XML entities.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* The string to escape.
|
||||||
|
* @return {string} The escaped string.
|
||||||
|
*/
|
||||||
|
escapeXML(str) {
|
||||||
|
const replacements = { "&": "&", "\"": """, "'": "'", "<": "<", ">": ">", "/": "/" };
|
||||||
|
return String(str).replace(/[&"'<>/]/g, m => replacements[m]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tagged template function which escapes any XML metacharacters in
|
||||||
|
* interpolated values.
|
||||||
|
*
|
||||||
|
* @param {Array<string>} strings
|
||||||
|
* An array of literal strings extracted from the templates.
|
||||||
|
* @param {Array} values
|
||||||
|
* An array of interpolated values extracted from the template.
|
||||||
|
* @returns {string}
|
||||||
|
* The result of the escaped values interpolated with the literal
|
||||||
|
* strings.
|
||||||
|
*/
|
||||||
|
escaped(strings, ...values) {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
for (const [i, string] of strings.entries()) {
|
||||||
|
result.push(string);
|
||||||
|
if (i < values.length)
|
||||||
|
result.push(this.escapeXML(values[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.join("");
|
||||||
|
},
|
||||||
|
|
||||||
|
async currentTab() {
|
||||||
|
const activeTabs = await browser.tabs.query({ active: true, windowId: browser.windows.WINDOW_ID_CURRENT });
|
||||||
|
if (activeTabs.length > 0) {
|
||||||
|
return activeTabs[0];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
addEnterHandler(element, handler) {
|
||||||
|
element.addEventListener("click", (e) => {
|
||||||
|
handler(e);
|
||||||
|
});
|
||||||
|
element.addEventListener("keydown", (e) => {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
e.preventDefault();
|
||||||
|
handler(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
userContextId(cookieStoreId = "") {
|
||||||
|
const userContextId = cookieStoreId.replace("firefox-container-", "");
|
||||||
|
return (userContextId !== cookieStoreId) ? Number(userContextId) : false;
|
||||||
|
},
|
||||||
|
|
||||||
|
setOrRemoveAssignment(tabId, url, userContextId, value) {
|
||||||
|
return browser.runtime.sendMessage({
|
||||||
|
method: "setOrRemoveAssignment",
|
||||||
|
tabId,
|
||||||
|
url,
|
||||||
|
userContextId,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
reloadInContainer(url, currentUserContextId, newUserContextId, tabIndex, active) {
|
||||||
|
return browser.runtime.sendMessage({
|
||||||
|
method: "reloadInContainer",
|
||||||
|
url,
|
||||||
|
currentUserContextId,
|
||||||
|
newUserContextId,
|
||||||
|
tabIndex,
|
||||||
|
active
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
window.Utils = Utils;
|
|
@ -66,6 +66,14 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"page_action": {
|
||||||
|
"browser_style": true,
|
||||||
|
"default_icon": "img/container-site.svg",
|
||||||
|
"default_title": "Always open this in a Container",
|
||||||
|
"default_popup": "pageActionPopup.html",
|
||||||
|
"pinned": false,
|
||||||
|
"show_matches": ["*://*/*"]
|
||||||
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"page": "js/background/index.html"
|
"page": "js/background/index.html"
|
||||||
},
|
},
|
||||||
|
|
34
src/pageActionPopup.html
Normal file
34
src/pageActionPopup.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
<title>Multi-Account Containers</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/popup.css">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="page-action-container-picker" id="container-picker-panel">
|
||||||
|
<h3 class="title">
|
||||||
|
Reopen and Always Open this Site in...
|
||||||
|
</h3>
|
||||||
|
<hr>
|
||||||
|
<div class="scrollable identities-list">
|
||||||
|
<table class="menu" id="picker-identities-list">
|
||||||
|
<tr class="menu-item">
|
||||||
|
<td>
|
||||||
|
<div class="menu-icon">
|
||||||
|
<div class="usercontext-icon"
|
||||||
|
data-identity-icon="pet"
|
||||||
|
data-identity-color="blue">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="menu-text">Default</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="js/utils.js"></script>
|
||||||
|
<script src="js/pageAction.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Reference in a new issue