Merge 960a3a1241
into 50f5ebfcff
This commit is contained in:
commit
0ef10bb123
9 changed files with 407 additions and 68 deletions
|
@ -585,6 +585,11 @@ manage things like container crud */
|
||||||
padding-inline-start: 0;
|
padding-inline-start: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-allowed-sites-panel fieldset {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.edit-container-panel fieldset:last-of-type {
|
.edit-container-panel fieldset:last-of-type {
|
||||||
margin-block-end: 0;
|
margin-block-end: 0;
|
||||||
}
|
}
|
||||||
|
@ -729,6 +734,7 @@ h3.title {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Maintain 1:1 square ratio for Favicons of websites added to a specific container */
|
/* Maintain 1:1 square ratio for Favicons of websites added to a specific container */
|
||||||
|
.edit-allowed-sites-panel .menu-icon,
|
||||||
#edit-sites-assigned .menu-icon,
|
#edit-sites-assigned .menu-icon,
|
||||||
#container-info-table .menu-icon {
|
#container-info-table .menu-icon {
|
||||||
inline-size: 16px;
|
inline-size: 16px;
|
||||||
|
@ -952,6 +958,16 @@ tr:hover > td > .trash-button {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#add-allowed-site-form {
|
||||||
|
align-items: end;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
#add-allowed-site-form fieldset {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--title-text-color: #fff;
|
--title-text-color: #fff;
|
||||||
|
|
9
src/img/container-allowin-16.svg
Normal file
9
src/img/container-allowin-16.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 52.6 (67491) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>container-allowin-16</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<g id="container-allowin-16" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<path d="M8,15 C4.13400675,15 1,11.8659932 1,8 C1,4.13400675 4.13400675,1 8,1 C11.8659932,1 15,4.13400675 15,8 C15,11.8659932 11.8659932,15 8,15 Z M10.2928932,5.29289322 L7,8.58578644 L5.70710678,7.29289322 C5.31658249,6.90236893 4.68341751,6.90236893 4.29289322,7.29289322 C3.90236893,7.68341751 3.90236893,8.31658249 4.29289322,8.70710678 L6.29289322,10.7071068 C6.68341751,11.0976311 7.31658249,11.0976311 7.70710678,10.7071068 L11.7071068,6.70710678 C12.0976311,6.31658249 12.0976311,5.68341751 11.7071068,5.29289322 C11.3165825,4.90236893 10.6834175,4.90236893 10.2928932,5.29289322 Z" id="Combined-Shape" fill="#000000"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -210,6 +210,7 @@ window.assignManager = {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
||||||
|
const url = options.url;
|
||||||
|
|
||||||
// https://github.com/mozilla/multi-account-containers/issues/847
|
// https://github.com/mozilla/multi-account-containers/issues/847
|
||||||
//
|
//
|
||||||
|
@ -228,8 +229,11 @@ window.assignManager = {
|
||||||
// - the current tab's container is locked and only allows "www.google.com"
|
// - the current tab's container is locked and only allows "www.google.com"
|
||||||
// - the incoming request is for "www.amazon.com", which has no specific container assignment
|
// - the incoming request is for "www.amazon.com", which has no specific container assignment
|
||||||
// - in this case, we must re-open "www.amazon.com" in a new tab in the default container
|
// - in this case, we must re-open "www.amazon.com" in a new tab in the default container
|
||||||
const siteIsolatedReloadInDefault =
|
const siteIsolatedReloadInDefault = await this._maybeSiteIsolatedReloadInDefault(
|
||||||
await this._maybeSiteIsolatedReloadInDefault(siteSettings, tab);
|
siteSettings,
|
||||||
|
tab,
|
||||||
|
url
|
||||||
|
);
|
||||||
|
|
||||||
if (!siteIsolatedReloadInDefault) {
|
if (!siteIsolatedReloadInDefault) {
|
||||||
if (!siteSettings
|
if (!siteSettings
|
||||||
|
@ -338,7 +342,7 @@ window.assignManager = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async _maybeSiteIsolatedReloadInDefault(siteSettings, tab) {
|
async _maybeSiteIsolatedReloadInDefault(siteSettings, tab, url) {
|
||||||
// Tab doesn't support cookies, so containers not supported either.
|
// Tab doesn't support cookies, so containers not supported either.
|
||||||
if (!("cookieStoreId" in tab)) {
|
if (!("cookieStoreId" in tab)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -358,7 +362,21 @@ window.assignManager = {
|
||||||
// Requested page is not assigned to a specific container. If the current tab's container
|
// Requested page is not assigned to a specific container. If the current tab's container
|
||||||
// is locked, then the page must be reloaded in the default container.
|
// is locked, then the page must be reloaded in the default container.
|
||||||
const currentContainerState = await identityState.storageArea.get(tab.cookieStoreId);
|
const currentContainerState = await identityState.storageArea.get(tab.cookieStoreId);
|
||||||
return currentContainerState && currentContainerState.isIsolated;
|
|
||||||
|
// the container is not isolated, so any site can be opened
|
||||||
|
const isIsolated = currentContainerState && currentContainerState.isIsolated;
|
||||||
|
if (!isIsolated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the site is isolated, and it's *not* an assigned site, so check if it's in the allowed
|
||||||
|
// sites array. If it is we can open the site in the container, otherwise we should reload
|
||||||
|
// in the default container
|
||||||
|
const allowedSites =
|
||||||
|
(currentContainerState && currentContainerState.allowedSites) || [];
|
||||||
|
|
||||||
|
const allowedKey = Utils.getAllowedSiteKeyFor(url);
|
||||||
|
return !allowedSites.includes(allowedKey);
|
||||||
},
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
|
@ -151,6 +151,42 @@ const backgroundLogic = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async addRemoveAllowedSite(cookieStoreId, allowedSiteUrl, remove = false) {
|
||||||
|
try {
|
||||||
|
const containerState = await identityState.storageArea.get(cookieStoreId);
|
||||||
|
const allowedSiteKey = Utils.getAllowedSiteKeyFor(allowedSiteUrl);
|
||||||
|
const allowedSites = containerState.allowedSites || [];
|
||||||
|
const allowedSiteIdx = allowedSites.indexOf(allowedSiteKey);
|
||||||
|
|
||||||
|
if (!remove) {
|
||||||
|
if (allowedSiteIdx === -1) {
|
||||||
|
// only add the site if it's not already in the list.
|
||||||
|
allowedSites.push(allowedSiteKey);
|
||||||
|
containerState.allowedSites = allowedSites;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// remove
|
||||||
|
if (allowedSiteIdx >= 0) {
|
||||||
|
allowedSites.splice(allowedSiteIdx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
containerState.allowedSites = allowedSites;
|
||||||
|
return await identityState.storageArea.set(cookieStoreId, containerState);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`No container: ${cookieStoreId}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async clearAllowedSites(cookieStoreId) {
|
||||||
|
try {
|
||||||
|
const containerState = await identityState.storageArea.get(cookieStoreId);
|
||||||
|
containerState.allowedSites = [];
|
||||||
|
return await identityState.storageArea.set(cookieStoreId, containerState);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`No container: ${cookieStoreId}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async moveTabsToWindow(options) {
|
async moveTabsToWindow(options) {
|
||||||
const requiredArguments = ["cookieStoreId", "windowId"];
|
const requiredArguments = ["cookieStoreId", "windowId"];
|
||||||
this.checkArgs(requiredArguments, options, "moveTabsToWindow");
|
this.checkArgs(requiredArguments, options, "moveTabsToWindow");
|
||||||
|
@ -257,7 +293,8 @@ const backgroundLogic = {
|
||||||
hasOpenTabs: !!openTabs.length,
|
hasOpenTabs: !!openTabs.length,
|
||||||
numberOfHiddenTabs: containerState.hiddenTabs.length,
|
numberOfHiddenTabs: containerState.hiddenTabs.length,
|
||||||
numberOfOpenTabs: openTabs.length,
|
numberOfOpenTabs: openTabs.length,
|
||||||
isIsolated: !!containerState.isIsolated
|
isIsolated: !!containerState.isIsolated,
|
||||||
|
allowedSites: containerState.allowedSites || []
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,5 +19,6 @@
|
||||||
<script type="text/javascript" src="identityState.js"></script>
|
<script type="text/javascript" src="identityState.js"></script>
|
||||||
<script type="text/javascript" src="messageHandler.js"></script>
|
<script type="text/javascript" src="messageHandler.js"></script>
|
||||||
<script type="text/javascript" src="sync.js"></script>
|
<script type="text/javascript" src="sync.js"></script>
|
||||||
|
<script type="text/javascript" src="../utils.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -35,6 +35,16 @@ const messageHandler = {
|
||||||
case "addRemoveSiteIsolation":
|
case "addRemoveSiteIsolation":
|
||||||
response = backgroundLogic.addRemoveSiteIsolation(m.cookieStoreId);
|
response = backgroundLogic.addRemoveSiteIsolation(m.cookieStoreId);
|
||||||
break;
|
break;
|
||||||
|
case "addRemoveAllowedSite":
|
||||||
|
response = backgroundLogic.addRemoveAllowedSite(
|
||||||
|
m.cookieStoreId,
|
||||||
|
m.allowedSiteUrl,
|
||||||
|
m.remove
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "clearAllowedSites":
|
||||||
|
response = backgroundLogic.clearAllowedSites(m.cookieStoreId);
|
||||||
|
break;
|
||||||
case "getAssignment":
|
case "getAssignment":
|
||||||
response = browser.tabs.get(m.tabId).then((tab) => {
|
response = browser.tabs.get(m.tabId).then((tab) => {
|
||||||
return assignManager._getAssignment(tab);
|
return assignManager._getAssignment(tab);
|
||||||
|
|
167
src/js/popup.js
167
src/js/popup.js
|
@ -26,6 +26,7 @@ const OPEN_NEW_CONTAINER_PICKER = "new-tab";
|
||||||
const MANAGE_CONTAINERS_PICKER = "manage";
|
const MANAGE_CONTAINERS_PICKER = "manage";
|
||||||
const REOPEN_IN_CONTAINER_PICKER = "reopen-in";
|
const REOPEN_IN_CONTAINER_PICKER = "reopen-in";
|
||||||
const ALWAYS_OPEN_IN_PICKER = "always-open-in";
|
const ALWAYS_OPEN_IN_PICKER = "always-open-in";
|
||||||
|
const ALLOW_OPEN_IN_PICKER = "allow-open-in";
|
||||||
const P_CONTAINER_INFO = "containerInfo";
|
const P_CONTAINER_INFO = "containerInfo";
|
||||||
const P_CONTAINER_EDIT = "containerEdit";
|
const P_CONTAINER_EDIT = "containerEdit";
|
||||||
const P_CONTAINER_DELETE = "containerDelete";
|
const P_CONTAINER_DELETE = "containerDelete";
|
||||||
|
@ -40,6 +41,15 @@ function addRemoveSiteIsolation() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addRemoveAllowedSite(cookieStoreId, allowedSiteUrl, remove = false) {
|
||||||
|
return browser.runtime.sendMessage({
|
||||||
|
method: "addRemoveAllowedSite",
|
||||||
|
cookieStoreId: cookieStoreId,
|
||||||
|
allowedSiteUrl: allowedSiteUrl,
|
||||||
|
remove: remove
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function getExtensionInfo() {
|
async function getExtensionInfo() {
|
||||||
const manifestPath = browser.runtime.getURL("manifest.json");
|
const manifestPath = browser.runtime.getURL("manifest.json");
|
||||||
const response = await fetch(manifestPath);
|
const response = await fetch(manifestPath);
|
||||||
|
@ -225,6 +235,7 @@ const Logic = {
|
||||||
identity.numberOfHiddenTabs = stateObject.numberOfHiddenTabs;
|
identity.numberOfHiddenTabs = stateObject.numberOfHiddenTabs;
|
||||||
identity.numberOfOpenTabs = stateObject.numberOfOpenTabs;
|
identity.numberOfOpenTabs = stateObject.numberOfOpenTabs;
|
||||||
identity.isIsolated = stateObject.isIsolated;
|
identity.isIsolated = stateObject.isIsolated;
|
||||||
|
identity.allowedSites = stateObject.allowedSites;
|
||||||
}
|
}
|
||||||
if (containerOrder) {
|
if (containerOrder) {
|
||||||
identity.order = containerOrder[identity.cookieStoreId];
|
identity.order = containerOrder[identity.cookieStoreId];
|
||||||
|
@ -302,6 +313,14 @@ const Logic = {
|
||||||
return this._currentIdentity;
|
return this._currentIdentity;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async refreshCurrentIdentity() {
|
||||||
|
const current = this.currentIdentity();
|
||||||
|
await this.refreshIdentities();
|
||||||
|
this._currentIdentity = this.identities().find(
|
||||||
|
identity => identity.cookieStoreId === current.cookieStoreId
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
currentUserContextId() {
|
currentUserContextId() {
|
||||||
const identity = Logic.currentIdentity();
|
const identity = Logic.currentIdentity();
|
||||||
return Utils.userContextId(identity.cookieStoreId);
|
return Utils.userContextId(identity.cookieStoreId);
|
||||||
|
@ -645,6 +664,9 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
|
Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
|
||||||
Logic.showPanel(ALWAYS_OPEN_IN_PICKER);
|
Logic.showPanel(ALWAYS_OPEN_IN_PICKER);
|
||||||
});
|
});
|
||||||
|
Utils.addEnterHandler(document.querySelector("#allow-open-in"), () => {
|
||||||
|
Logic.showPanel(ALLOW_OPEN_IN_PICKER);
|
||||||
|
});
|
||||||
Utils.addEnterHandler(document.querySelector("#info-icon"), () => {
|
Utils.addEnterHandler(document.querySelector("#info-icon"), () => {
|
||||||
browser.runtime.openOptionsPage();
|
browser.runtime.openOptionsPage();
|
||||||
});
|
});
|
||||||
|
@ -668,6 +690,13 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
||||||
async prepare() {
|
async prepare() {
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
const anyIsolatedContainers = Logic.identities().some(
|
||||||
|
identity => identity.isIsolated
|
||||||
|
);
|
||||||
|
|
||||||
|
const allowOpenIn = document.querySelector("#allow-open-in");
|
||||||
|
allowOpenIn.hidden = !anyIsolatedContainers;
|
||||||
|
|
||||||
Logic.identities().forEach(identity => {
|
Logic.identities().forEach(identity => {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
tr.classList.add("menu-item", "hover-highlight", "keyboard-nav", "keyboard-right-arrow-override");
|
tr.classList.add("menu-item", "hover-highlight", "keyboard-nav", "keyboard-right-arrow-override");
|
||||||
|
@ -1236,6 +1265,65 @@ Logic.registerPanel(ALWAYS_OPEN_IN_PICKER, {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ALLOW_OPEN_IN_PICKER: Makes the list editable.
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Logic.registerPanel(ALLOW_OPEN_IN_PICKER, {
|
||||||
|
panelSelector: "#container-picker-panel",
|
||||||
|
|
||||||
|
// This method is called when the object is registered.
|
||||||
|
initialize() {},
|
||||||
|
|
||||||
|
// This method is called when the panel is shown.
|
||||||
|
prepare() {
|
||||||
|
Logic.listenToPickerBackButton();
|
||||||
|
document.getElementById("picker-title").textContent =
|
||||||
|
"Allow opening this site in";
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
document.getElementById("new-container-div").innerHTML = Utils.escaped`
|
||||||
|
<div class="sub-header">Only showing isolated containers</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
Logic.identities()
|
||||||
|
.filter(identity => identity.isIsolated)
|
||||||
|
.forEach(identity => {
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
tr.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
||||||
|
tr.setAttribute("tabindex", "0");
|
||||||
|
const td = document.createElement("td");
|
||||||
|
|
||||||
|
td.innerHTML = Utils.escaped`
|
||||||
|
<div class="menu-icon hover-highlight">
|
||||||
|
<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 this.currentTab();
|
||||||
|
const currentTab = await Utils.currentTab();
|
||||||
|
const url = currentTab.url;
|
||||||
|
addRemoveAllowedSite(identity.cookieStoreId, url);
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const list = document.querySelector("#picker-identities-list");
|
||||||
|
|
||||||
|
list.innerHTML = "";
|
||||||
|
list.appendChild(fragment);
|
||||||
|
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// P_CONTAINER_ASSIGNMENTS: Shows Site Assignments and allows editing.
|
// P_CONTAINER_ASSIGNMENTS: Shows Site Assignments and allows editing.
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1259,7 +1347,15 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
||||||
|
|
||||||
const userContextId = Logic.currentUserContextId();
|
const userContextId = Logic.currentUserContextId();
|
||||||
const assignments = await Logic.getAssignmentObjectByContainer(userContextId);
|
const assignments = await Logic.getAssignmentObjectByContainer(userContextId);
|
||||||
|
|
||||||
|
this._addAllowedForm = document.getElementById("add-allowed-site-form");
|
||||||
|
this._addAllowedForm.addEventListener("submit", e => {
|
||||||
|
e.preventDefault();
|
||||||
|
this._submitAddAllowedSiteForm();
|
||||||
|
});
|
||||||
|
|
||||||
this.showAssignedContainers(assignments);
|
this.showAssignedContainers(assignments);
|
||||||
|
this.showAllowedContainers(identity.isIsolated, identity.allowedSites);
|
||||||
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
},
|
},
|
||||||
|
@ -1277,18 +1373,7 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
||||||
}
|
}
|
||||||
assignmentKeys.forEach((siteKey) => {
|
assignmentKeys.forEach((siteKey) => {
|
||||||
const site = assignments[siteKey];
|
const site = assignments[siteKey];
|
||||||
const trElement = document.createElement("tr");
|
const { trElement, deleteButton, assumedUrl } = this._createContainerRow(site.hostname);
|
||||||
/* 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 */
|
|
||||||
const assumedUrl = `https://${site.hostname}/favicon.ico`;
|
|
||||||
trElement.innerHTML = Utils.escaped`
|
|
||||||
<td>
|
|
||||||
<div class="favicon"></div>
|
|
||||||
<span title="${site.hostname}" class="menu-text">${site.hostname}</span>
|
|
||||||
<img class="trash-button delete-assignment" src="/img/container-delete.svg" />
|
|
||||||
</td>`;
|
|
||||||
trElement.getElementsByClassName("favicon")[0].appendChild(Utils.createFavIconElement(assumedUrl));
|
|
||||||
const deleteButton = trElement.querySelector(".trash-button");
|
|
||||||
Utils.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
|
||||||
|
@ -1297,11 +1382,67 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
||||||
delete assignments[siteKey];
|
delete assignments[siteKey];
|
||||||
this.showAssignedContainers(assignments);
|
this.showAssignedContainers(assignments);
|
||||||
});
|
});
|
||||||
trElement.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
|
||||||
tableElement.appendChild(trElement);
|
tableElement.appendChild(trElement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showAllowedContainers(isIsolated, allowed) {
|
||||||
|
const allowedSitesPanel = document.querySelector(".edit-allowed-sites-panel");
|
||||||
|
allowedSitesPanel.hidden = !isIsolated;
|
||||||
|
|
||||||
|
const tableElement = document.getElementById("edit-sites-allowed");
|
||||||
|
// Clear the previous list list
|
||||||
|
while (tableElement.firstChild) {
|
||||||
|
tableElement.firstChild.remove();
|
||||||
|
}
|
||||||
|
allowed.forEach((allowedKey, idx) => {
|
||||||
|
const hostname = Utils.getLabelForAllowedSiteKey(allowedKey);
|
||||||
|
const { trElement, deleteButton } = this._createContainerRow(hostname);
|
||||||
|
Utils.addEnterHandler(deleteButton, async () => {
|
||||||
|
const currentCookieStoreId = Logic.currentCookieStoreId();
|
||||||
|
addRemoveAllowedSite(currentCookieStoreId, hostname, true);
|
||||||
|
allowed.splice(idx, 1);
|
||||||
|
this.showAllowedContainers(isIsolated, allowed);
|
||||||
|
});
|
||||||
|
tableElement.appendChild(trElement);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_createContainerRow(hostname) {
|
||||||
|
const trElement = document.createElement("tr");
|
||||||
|
/* 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 */
|
||||||
|
const assumedUrl = `https://${hostname}/favicon.ico`;
|
||||||
|
trElement.innerHTML = Utils.escaped`
|
||||||
|
<td>
|
||||||
|
<div class="favicon"></div>
|
||||||
|
<span title="${hostname}" class="menu-text truncate-text">${hostname}</span>
|
||||||
|
<img class="trash-button delete-assignment" src="/img/container-delete.svg" />
|
||||||
|
</td>`;
|
||||||
|
trElement
|
||||||
|
.getElementsByClassName("favicon")[0]
|
||||||
|
.appendChild(Utils.createFavIconElement(assumedUrl));
|
||||||
|
|
||||||
|
const deleteButton = trElement.querySelector(".trash-button");
|
||||||
|
trElement.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
||||||
|
|
||||||
|
return { trElement, deleteButton, assumedUrl };
|
||||||
|
},
|
||||||
|
|
||||||
|
async _submitAddAllowedSiteForm() {
|
||||||
|
const formValues = new FormData(this._addAllowedForm);
|
||||||
|
|
||||||
|
const currentCookieStoreId = Logic.currentCookieStoreId();
|
||||||
|
const allowedSite = formValues.get("add-allowed-site-name");
|
||||||
|
|
||||||
|
await addRemoveAllowedSite(currentCookieStoreId, allowedSite);
|
||||||
|
|
||||||
|
await Logic.refreshCurrentIdentity();
|
||||||
|
const identity = Logic.currentIdentity();
|
||||||
|
this.showAllowedContainers(identity.isIsolated, identity.allowedSites);
|
||||||
|
this._addAllowedForm.reset();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// P_CONTAINER_EDIT: Editor for a container.
|
// P_CONTAINER_EDIT: Editor for a container.
|
||||||
|
|
|
@ -129,7 +129,81 @@ const Utils = {
|
||||||
assignedUserContextId,
|
assignedUserContextId,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the allowed site key for a given url, hostname, or hostname:port
|
||||||
|
* @param {string} pageUrl
|
||||||
|
* @returns the allowed site key for the given url
|
||||||
|
*/
|
||||||
|
getAllowedSiteKeyFor(pageUrl) {
|
||||||
|
if (!pageUrl) {
|
||||||
|
throw new Error("pageUrl cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageUrl.startsWith("allowedSiteKey@@_")) {
|
||||||
|
// we trust that you're a key already
|
||||||
|
return pageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to parse the attribute as a naked hostname
|
||||||
|
if (this._isValidHostname(pageUrl)) {
|
||||||
|
return this._allowedSiteKeyForHostPort(pageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to parse the attribute as a hostname:port
|
||||||
|
if (pageUrl.includes(":")) {
|
||||||
|
const parts = pageUrl.split(":");
|
||||||
|
if (parts.length === 2) {
|
||||||
|
const potentialHost = parts[0];
|
||||||
|
const potentialPort = parts[1];
|
||||||
|
if (this._isValidHostname(potentialHost) && this._isValidPort(potentialPort)) {
|
||||||
|
return this._allowedSiteKeyForHostPort(potentialHost, potentialPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try parsing the attribute as a page url
|
||||||
|
try {
|
||||||
|
const url = new window.URL(pageUrl);
|
||||||
|
return this._allowedSiteKeyForHostPort(url.hostname, url.port);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`paramter ${pageUrl} was not parsed as a url`);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("pageUrl could not be parsed");
|
||||||
|
},
|
||||||
|
|
||||||
|
getLabelForAllowedSiteKey(allowedSiteKey) {
|
||||||
|
if (!allowedSiteKey) {
|
||||||
|
throw new Error("pageUrl cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowedSiteKey.startsWith("allowedSiteKey@@_")) {
|
||||||
|
return allowedSiteKey.replace("allowedSiteKey@@_", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowedSiteKey;
|
||||||
|
},
|
||||||
|
|
||||||
|
_isValidPort(potentialPort) {
|
||||||
|
return potentialPort > 0 && potentialPort <= 65535;
|
||||||
|
},
|
||||||
|
|
||||||
|
_isValidHostname(potentialHostname) {
|
||||||
|
// From @bkr https://stackoverflow.com/a/20204811
|
||||||
|
return /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/.test(
|
||||||
|
potentialHostname
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_allowedSiteKeyForHostPort(hostname, port) {
|
||||||
|
if (port === undefined || port === "" || port === "80" || port === "443") {
|
||||||
|
return `allowedSiteKey@@_${hostname}`;
|
||||||
|
} else {
|
||||||
|
return `allowedSiteKey@@_${hostname}:${port}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,15 @@
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="menu-item hover-highlight keyboard-nav" id="allow-open-in" tabindex="0">
|
||||||
|
<td>
|
||||||
|
<img class="menu-icon" alt="Open in New Tab" src="/img/container-allowin-16.svg" />
|
||||||
|
<span class="menu-text">Allow Opening This Site in...</span>
|
||||||
|
<span class="menu-arrow">
|
||||||
|
<img alt="Container Info" src="/img/arrow-icon-right.svg" />
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="sub-header">
|
<div class="sub-header">
|
||||||
|
@ -325,17 +334,41 @@
|
||||||
</h3>
|
</h3>
|
||||||
<button class="btn-return arrow-left" id="close-container-assignment-panel"></button>
|
<button class="btn-return arrow-left" id="close-container-assignment-panel"></button>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="scrollable edit-sites-assigned">
|
<div class="scrollable">
|
||||||
<div class="sub-header">Sites assigned to this container</div>
|
<div class="edit-sites-assigned">
|
||||||
<table class="menu scrollable" id="edit-sites-assigned">
|
<div class="sub-header">Sites assigned to this container</div>
|
||||||
<tr class="menu-item hover-highlight" tabindex="0">
|
<table class="menu scrollable" id="edit-sites-assigned">
|
||||||
<td>
|
<tr class="menu-item hover-highlight" tabindex="0">
|
||||||
<div class="favicon"><img class="menu-icon" src="https://www.mozilla.org/favicon.ico" /></div>
|
<td>
|
||||||
<span class="menu-text truncate-text">www.mozillllllllllllllllllllllllllllla.org</span>
|
<div class="favicon"><img class="menu-icon" src="https://www.mozilla.org/favicon.ico" /></div>
|
||||||
<img class="trash-button" src="/img/container-delete.svg" />
|
<span class="menu-text truncate-text">www.mozillllllllllllllllllllllllllllla.org</span>
|
||||||
</td>
|
<img class="trash-button" src="/img/container-delete.svg" />
|
||||||
</tr>
|
</td>
|
||||||
</table>
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="edit-allowed-sites-panel">
|
||||||
|
<hr>
|
||||||
|
<div class="sub-header">Allowed sites for this container</div>
|
||||||
|
<table class="menu scrollable" id="edit-sites-allowed">
|
||||||
|
<tr class="menu-item hover-highlight" tabindex="0">
|
||||||
|
<td>
|
||||||
|
<div class="favicon"><img class="menu-icon" src="https://www.mozilla.org/favicon.ico" /></div>
|
||||||
|
<span class="menu-text truncate-text">www.mozillllllllllllllllllllllllllllla.org</span>
|
||||||
|
<img class="trash-button" src="/img/container-delete.svg" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="edit-form">
|
||||||
|
<form id="add-allowed-site-form">
|
||||||
|
<fieldset>
|
||||||
|
<legend class="form-header" id="add-allowed-site-name-label">Add Allowed Site</legend>
|
||||||
|
<input type="text" name="add-allowed-site-name" id="add-allowed-site-name-input" class="edit-container-panel-name-input" aria-labelledby="add-allowed-site-name-label"/>
|
||||||
|
</fieldset>
|
||||||
|
<button type="submit">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue