Page action assigns site to a container and simultaneously reopens site in container.

This commit is contained in:
Kendall Werts 2020-02-14 00:08:37 -06:00
parent fbba6beee2
commit 1870ce08bc
9 changed files with 235 additions and 117 deletions

View file

@ -46,7 +46,7 @@ module.exports = {
"error", "error",
{ {
"escape": { "escape": {
"taggedTemplates": ["escaped"] "taggedTemplates": ["escaped", "Utils.escaped"]
} }
} }
], ],

View file

@ -8,6 +8,7 @@ module.exports = {
"backgroundLogic": true, "backgroundLogic": true,
"identityState": true, "identityState": true,
"messageHandler": true, "messageHandler": true,
"sync": true "sync": true,
"Utils": true
} }
}; };

View file

@ -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

View file

@ -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
View 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();

View file

@ -30,41 +30,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 = { "&": "&amp;", "\"": "&quot;", "'": "&apos;", "<": "&lt;", ">": "&gt;", "/": "&#x2F;" };
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");
@ -201,31 +166,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;
@ -338,7 +278,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() {
@ -374,16 +314,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 = [];
@ -423,7 +353,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);
}); });
@ -447,7 +377,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);
}); });
@ -471,7 +401,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);
}); });
@ -493,7 +423,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);
}); });
@ -514,7 +444,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);
}); });
@ -535,7 +465,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({
@ -543,7 +473,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({
@ -568,14 +498,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);
}); });
@ -594,21 +524,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("#manage-containers-link"), (e) => { Utils.addEnterHandler(document.querySelector("#manage-containers-link"), (e) => {
if (!e.target.classList.contains("disable-edit-containers")) { if (!e.target.classList.contains("disable-edit-containers")) {
Logic.showPanel(P_CONTAINER_PICKER, null, MANAGE_CONTAINERS_PICKER); Logic.showPanel(P_CONTAINER_PICKER, null, MANAGE_CONTAINERS_PICKER);
} }
}); });
Logic.addEnterHandler(document.querySelector("#open-new-tab-in"), () => { Utils.addEnterHandler(document.querySelector("#open-new-tab-in"), () => {
Logic.showPanel(P_CONTAINER_PICKER, null, OPEN_NEW_CONTAINER_PICKER); Logic.showPanel(P_CONTAINER_PICKER, null, OPEN_NEW_CONTAINER_PICKER);
}); });
Logic.addEnterHandler(document.querySelector("#reopen-site-in"), () => { Utils.addEnterHandler(document.querySelector("#reopen-site-in"), () => {
Logic.showPanel(P_CONTAINER_PICKER, null, REOPEN_IN_CONTAINER); Logic.showPanel(P_CONTAINER_PICKER, null, REOPEN_IN_CONTAINER);
}); });
Logic.addEnterHandler(document.querySelector("#always-open-in"), () => { Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
Logic.showPanel(P_CONTAINER_PICKER, null, ALWAYS_OPEN_IN_PICKER); Logic.showPanel(P_CONTAINER_PICKER, null, ALWAYS_OPEN_IN_PICKER);
}); });
Logic.addEnterHandler(document.querySelector("#sort-containers-link"), async () => { Utils.addEnterHandler(document.querySelector("#sort-containers-link"), async () => {
try { try {
await browser.runtime.sendMessage({ await browser.runtime.sendMessage({
method: "sortTabs" method: "sortTabs"
@ -685,7 +615,7 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
const td = document.createElement("td"); const td = document.createElement("td");
const openTabs = identity.numberOfOpenTabs || "" ; const openTabs = identity.numberOfOpenTabs || "" ;
td.innerHTML = escaped` td.innerHTML = Utils.escaped`
<div class="menu-icon"> <div class="menu-icon">
<div class="usercontext-icon" <div class="usercontext-icon"
data-identity-icon="${identity.icon}" data-identity-icon="${identity.icon}"
@ -704,7 +634,7 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
tr.appendChild(td); tr.appendChild(td);
Logic.addEnterHandler(tr, () => { Utils.addEnterHandler(tr, () => {
Logic.showPanel(P_CONTAINER_INFO, identity); Logic.showPanel(P_CONTAINER_INFO, identity);
}); });
}); });
@ -744,12 +674,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({
@ -782,7 +712,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,
@ -836,7 +766,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));
@ -863,14 +793,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();
}); });
@ -888,7 +818,7 @@ Logic.registerPanel(P_CONTAINER_PICKER, {
// 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);
// }); // });
}, },
@ -930,13 +860,12 @@ Logic.registerPanel(P_CONTAINER_PICKER, {
case ALWAYS_OPEN_IN_PICKER: case ALWAYS_OPEN_IN_PICKER:
default: default:
pickedFunction = async function (identity) { pickedFunction = async function (identity) {
const currentTab = await Logic.currentTab(); const userContextId = Utils.userContextId(identity.cookieStoreId);
console.log(identity.cookieStoreId) const currentTab = await Utils.currentTab();
console.log(identity) Utils.setOrRemoveAssignment(
Logic.setOrRemoveAssignment(
currentTab.id, currentTab.id,
currentTab.url, currentTab.url,
identity.cookieStoreId, userContextId,
false false
); );
window.close(); window.close();
@ -949,7 +878,7 @@ Logic.registerPanel(P_CONTAINER_PICKER, {
tr.classList.add("menu-item"); tr.classList.add("menu-item");
const td = document.createElement("td"); const td = document.createElement("td");
td.innerHTML = escaped` td.innerHTML = Utils.escaped`
<div class="menu-icon"> <div class="menu-icon">
<div class="usercontext-icon" <div class="usercontext-icon"
data-identity-icon="${identity.icon}" data-identity-icon="${identity.icon}"
@ -962,7 +891,7 @@ Logic.registerPanel(P_CONTAINER_PICKER, {
tr.appendChild(td); tr.appendChild(td);
Logic.addEnterHandler(tr, () => { Utils.addEnterHandler(tr, () => {
pickedFunction(identity); pickedFunction(identity);
}); });
}); });
@ -986,7 +915,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();
@ -995,13 +924,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", () => {
@ -1053,7 +982,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}
@ -1065,12 +994,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);
}); });
@ -1082,7 +1011,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"];
@ -1096,7 +1025,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"];
@ -1146,18 +1075,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) {
@ -1198,7 +1127,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);
}); });

View file

@ -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 = { "&": "&amp;", "\"": "&quot;", "'": "&apos;", "<": "&lt;", ">": "&gt;", "/": "&#x2F;" };
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;

View file

@ -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
View 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>