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;
|
||||
}
|
||||
|
||||
.edit-allowed-sites-panel fieldset {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.edit-container-panel fieldset:last-of-type {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
@ -729,6 +734,7 @@ h3.title {
|
|||
}
|
||||
|
||||
/* 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,
|
||||
#container-info-table .menu-icon {
|
||||
inline-size: 16px;
|
||||
|
@ -952,6 +958,16 @@ tr:hover > td > .trash-button {
|
|||
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) {
|
||||
:root {
|
||||
--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 |
|
@ -109,10 +109,10 @@ window.assignManager = {
|
|||
const siteConfigs = await this.area.get();
|
||||
for(const urlKey of Object.keys(siteConfigs)) {
|
||||
if (urlKey.includes("siteContainerMap@@_")) {
|
||||
// For some reason this is stored as string... lets check
|
||||
// For some reason this is stored as string... lets check
|
||||
// them both as that
|
||||
if (!!userContextId &&
|
||||
String(siteConfigs[urlKey].userContextId)
|
||||
if (!!userContextId &&
|
||||
String(siteConfigs[urlKey].userContextId)
|
||||
!== String(userContextId)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ window.assignManager = {
|
|||
},
|
||||
|
||||
/*
|
||||
* Looks for abandoned site assignments. If there is no identity with
|
||||
* Looks for abandoned site assignments. If there is no identity with
|
||||
* the site assignment's userContextId (cookieStoreId), then the assignment
|
||||
* is removed.
|
||||
*/
|
||||
|
@ -136,8 +136,8 @@ window.assignManager = {
|
|||
const macConfigs = await this.area.get();
|
||||
for(const configKey of Object.keys(macConfigs)) {
|
||||
if (configKey.includes("siteContainerMap@@_")) {
|
||||
const cookieStoreId =
|
||||
"firefox-container-" + macConfigs[configKey].userContextId;
|
||||
const cookieStoreId =
|
||||
"firefox-container-" + macConfigs[configKey].userContextId;
|
||||
const match = identitiesList.find(
|
||||
localIdentity => localIdentity.cookieStoreId === cookieStoreId
|
||||
);
|
||||
|
@ -146,7 +146,7 @@ window.assignManager = {
|
|||
continue;
|
||||
}
|
||||
const updatedSiteAssignment = macConfigs[configKey];
|
||||
updatedSiteAssignment.identityMacAddonUUID =
|
||||
updatedSiteAssignment.identityMacAddonUUID =
|
||||
await identityState.lookupMACaddonUUID(match.cookieStoreId);
|
||||
await this.set(
|
||||
configKey,
|
||||
|
@ -164,7 +164,7 @@ window.assignManager = {
|
|||
_neverAsk(m) {
|
||||
const pageUrl = m.pageUrl;
|
||||
if (m.neverAsk === true) {
|
||||
// If we have existing data and for some reason it hasn't been
|
||||
// If we have existing data and for some reason it hasn't been
|
||||
// deleted etc lets update it
|
||||
this.storageArea.get(pageUrl).then((siteSettings) => {
|
||||
if (siteSettings) {
|
||||
|
@ -210,9 +210,10 @@ window.assignManager = {
|
|||
return {};
|
||||
}
|
||||
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
||||
const url = options.url;
|
||||
|
||||
// https://github.com/mozilla/multi-account-containers/issues/847
|
||||
//
|
||||
//
|
||||
// Handle the case where this request's URL is not assigned to any particular
|
||||
// container. We must do the following check:
|
||||
//
|
||||
|
@ -228,8 +229,11 @@ window.assignManager = {
|
|||
// - 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
|
||||
// - in this case, we must re-open "www.amazon.com" in a new tab in the default container
|
||||
const siteIsolatedReloadInDefault =
|
||||
await this._maybeSiteIsolatedReloadInDefault(siteSettings, tab);
|
||||
const siteIsolatedReloadInDefault = await this._maybeSiteIsolatedReloadInDefault(
|
||||
siteSettings,
|
||||
tab,
|
||||
url
|
||||
);
|
||||
|
||||
if (!siteIsolatedReloadInDefault) {
|
||||
if (!siteSettings
|
||||
|
@ -246,7 +250,7 @@ window.assignManager = {
|
|||
const openTabId = removeTab ? tab.openerTabId : tab.id;
|
||||
|
||||
if (!this.canceledRequests[tab.id]) {
|
||||
// we decided to cancel the request at this point, register
|
||||
// we decided to cancel the request at this point, register
|
||||
// canceled request
|
||||
this.canceledRequests[tab.id] = {
|
||||
requestIds: {
|
||||
|
@ -313,7 +317,7 @@ window.assignManager = {
|
|||
- As the history won't span from one container to another it
|
||||
seems most sane to not try and reopen a tab on history.back()
|
||||
- When users open a new tab themselves we want to make sure we
|
||||
don't end up with three tabs as per:
|
||||
don't end up with three tabs as per:
|
||||
https://github.com/mozilla/testpilot-containers/issues/421
|
||||
If we are coming from an internal url that are used for the new
|
||||
tab page (NEW_TAB_PAGES), we can safely close as user is unlikely
|
||||
|
@ -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.
|
||||
if (!("cookieStoreId" in tab)) {
|
||||
return false;
|
||||
|
@ -348,7 +352,7 @@ window.assignManager = {
|
|||
// I.e. it will be opened in that container anyway, so we don't need to check if the
|
||||
// current tab's container is locked or not.
|
||||
if (siteSettings) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
//tab is alredy reopening in the default container
|
||||
|
@ -358,13 +362,27 @@ window.assignManager = {
|
|||
// 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.
|
||||
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() {
|
||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||
info.bookmarkId ?
|
||||
this._onClickedBookmark(info) :
|
||||
info.bookmarkId ?
|
||||
this._onClickedBookmark(info) :
|
||||
this._onClickedHandler(info, tab);
|
||||
});
|
||||
|
||||
|
@ -479,7 +497,7 @@ window.assignManager = {
|
|||
async _onClickedBookmark(info) {
|
||||
|
||||
async function _getBookmarksFromInfo(info) {
|
||||
const [bookmarkTreeNode] =
|
||||
const [bookmarkTreeNode] =
|
||||
await browser.bookmarks.get(info.bookmarkId);
|
||||
if (bookmarkTreeNode.type === "folder") {
|
||||
return browser.bookmarks.getChildren(bookmarkTreeNode.id);
|
||||
|
@ -489,9 +507,9 @@ window.assignManager = {
|
|||
|
||||
const bookmarks = await _getBookmarksFromInfo(info);
|
||||
for (const bookmark of bookmarks) {
|
||||
// Some checks on the urls from
|
||||
// Some checks on the urls from
|
||||
// https://github.com/Rob--W/bookmark-container-tab/ thanks!
|
||||
if ( !/^(javascript|place):/i.test(bookmark.url) &&
|
||||
if ( !/^(javascript|place):/i.test(bookmark.url) &&
|
||||
bookmark.type !== "folder") {
|
||||
const openInReaderMode = bookmark.url.startsWith("about:reader");
|
||||
if(openInReaderMode) {
|
||||
|
@ -569,12 +587,12 @@ window.assignManager = {
|
|||
actionName = "removed from assigned sites list";
|
||||
|
||||
// remove site isolation if now empty
|
||||
await this._maybeRemoveSiteIsolation(userContextId);
|
||||
await this._maybeRemoveSiteIsolation(userContextId);
|
||||
}
|
||||
|
||||
if (tabId) {
|
||||
const tab = await browser.tabs.get(tabId);
|
||||
setTimeout(function(){
|
||||
setTimeout(function(){
|
||||
browser.tabs.sendMessage(tabId, {
|
||||
text: `Successfully ${actionName}`
|
||||
});
|
||||
|
@ -677,17 +695,17 @@ window.assignManager = {
|
|||
reloadPageInDefaultContainer(url, index, active, openerTabId) {
|
||||
// To create a new tab in the default container, it is easiest just to omit the
|
||||
// cookieStoreId entirely.
|
||||
//
|
||||
//
|
||||
// Unfortunately, if you create a new tab WITHOUT a cookieStoreId but WITH an openerTabId,
|
||||
// then the new tab automatically inherits the opener tab's cookieStoreId.
|
||||
// I.e. it opens in the wrong container!
|
||||
//
|
||||
//
|
||||
// So we have to explicitly pass in a cookieStoreId when creating the tab, since we
|
||||
// are specifying the openerTabId. There doesn't seem to be any way
|
||||
// to look up the default container's cookieStoreId programatically, so sadly
|
||||
// we have to hardcode it here as "firefox-default". This is potentially
|
||||
// not cross-browser compatible.
|
||||
//
|
||||
//
|
||||
// Note that we could have just omitted BOTH cookieStoreId and openerTabId. But the
|
||||
// drawback then is that if the user later closes the newly-created tab, the browser
|
||||
// does not automatically return to the original opener tab. To get this desired behaviour,
|
||||
|
|
|
@ -143,7 +143,7 @@ const backgroundLogic = {
|
|||
if ("isIsolated" in containerState || remove) {
|
||||
delete containerState.isIsolated;
|
||||
} else {
|
||||
containerState.isIsolated = "locked";
|
||||
containerState.isIsolated = "locked";
|
||||
}
|
||||
return await identityState.storageArea.set(cookieStoreId, containerState);
|
||||
} catch (error) {
|
||||
|
@ -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) {
|
||||
const requiredArguments = ["cookieStoreId", "windowId"];
|
||||
this.checkArgs(requiredArguments, options, "moveTabsToWindow");
|
||||
|
@ -257,7 +293,8 @@ const backgroundLogic = {
|
|||
hasOpenTabs: !!openTabs.length,
|
||||
numberOfHiddenTabs: containerState.hiddenTabs.length,
|
||||
numberOfOpenTabs: openTabs.length,
|
||||
isIsolated: !!containerState.isIsolated
|
||||
isIsolated: !!containerState.isIsolated,
|
||||
allowedSites: containerState.allowedSites || []
|
||||
};
|
||||
return;
|
||||
});
|
||||
|
|
|
@ -19,5 +19,6 @@
|
|||
<script type="text/javascript" src="identityState.js"></script>
|
||||
<script type="text/javascript" src="messageHandler.js"></script>
|
||||
<script type="text/javascript" src="sync.js"></script>
|
||||
<script type="text/javascript" src="../utils.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -35,6 +35,16 @@ const messageHandler = {
|
|||
case "addRemoveSiteIsolation":
|
||||
response = backgroundLogic.addRemoveSiteIsolation(m.cookieStoreId);
|
||||
break;
|
||||
case "addRemoveAllowedSite":
|
||||
response = backgroundLogic.addRemoveAllowedSite(
|
||||
m.cookieStoreId,
|
||||
m.allowedSiteUrl,
|
||||
m.remove
|
||||
);
|
||||
break;
|
||||
case "clearAllowedSites":
|
||||
response = backgroundLogic.clearAllowedSites(m.cookieStoreId);
|
||||
break;
|
||||
case "getAssignment":
|
||||
response = browser.tabs.get(m.tabId).then((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 REOPEN_IN_CONTAINER_PICKER = "reopen-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_EDIT = "containerEdit";
|
||||
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() {
|
||||
const manifestPath = browser.runtime.getURL("manifest.json");
|
||||
const response = await fetch(manifestPath);
|
||||
|
@ -225,6 +235,7 @@ const Logic = {
|
|||
identity.numberOfHiddenTabs = stateObject.numberOfHiddenTabs;
|
||||
identity.numberOfOpenTabs = stateObject.numberOfOpenTabs;
|
||||
identity.isIsolated = stateObject.isIsolated;
|
||||
identity.allowedSites = stateObject.allowedSites;
|
||||
}
|
||||
if (containerOrder) {
|
||||
identity.order = containerOrder[identity.cookieStoreId];
|
||||
|
@ -302,6 +313,14 @@ const Logic = {
|
|||
return this._currentIdentity;
|
||||
},
|
||||
|
||||
async refreshCurrentIdentity() {
|
||||
const current = this.currentIdentity();
|
||||
await this.refreshIdentities();
|
||||
this._currentIdentity = this.identities().find(
|
||||
identity => identity.cookieStoreId === current.cookieStoreId
|
||||
);
|
||||
},
|
||||
|
||||
currentUserContextId() {
|
||||
const identity = Logic.currentIdentity();
|
||||
return Utils.userContextId(identity.cookieStoreId);
|
||||
|
@ -645,6 +664,9 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
|||
Utils.addEnterHandler(document.querySelector("#always-open-in"), () => {
|
||||
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"), () => {
|
||||
browser.runtime.openOptionsPage();
|
||||
});
|
||||
|
@ -668,6 +690,13 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
|
|||
async prepare() {
|
||||
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 => {
|
||||
const tr = document.createElement("tr");
|
||||
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.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -1259,7 +1347,15 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
|||
|
||||
const userContextId = Logic.currentUserContextId();
|
||||
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.showAllowedContainers(identity.isIsolated, identity.allowedSites);
|
||||
|
||||
return Promise.resolve(null);
|
||||
},
|
||||
|
@ -1277,18 +1373,7 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
|||
}
|
||||
assignmentKeys.forEach((siteKey) => {
|
||||
const site = assignments[siteKey];
|
||||
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://${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");
|
||||
const { trElement, deleteButton, assumedUrl } = this._createContainerRow(site.hostname);
|
||||
Utils.addEnterHandler(deleteButton, async () => {
|
||||
const userContextId = Logic.currentUserContextId();
|
||||
// Lets show the message to the current tab
|
||||
|
@ -1297,11 +1382,67 @@ Logic.registerPanel(P_CONTAINER_ASSIGNMENTS, {
|
|||
delete assignments[siteKey];
|
||||
this.showAssignedContainers(assignments);
|
||||
});
|
||||
trElement.classList.add("menu-item", "hover-highlight", "keyboard-nav");
|
||||
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.
|
||||
|
|
102
src/js/utils.js
102
src/js/utils.js
|
@ -62,7 +62,7 @@ const Utils = {
|
|||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
addEnterHandler(element, handler) {
|
||||
element.addEventListener("click", (e) => {
|
||||
handler(e);
|
||||
|
@ -82,7 +82,7 @@ const Utils = {
|
|||
handler(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
userContextId(cookieStoreId = "") {
|
||||
const userContextId = cookieStoreId.replace("firefox-container-", "");
|
||||
|
@ -102,10 +102,10 @@ const Utils = {
|
|||
async reloadInContainer(url, currentUserContextId, newUserContextId, tabIndex, active) {
|
||||
return await browser.runtime.sendMessage({
|
||||
method: "reloadInContainer",
|
||||
url,
|
||||
currentUserContextId,
|
||||
newUserContextId,
|
||||
tabIndex,
|
||||
url,
|
||||
currentUserContextId,
|
||||
newUserContextId,
|
||||
tabIndex,
|
||||
active
|
||||
});
|
||||
},
|
||||
|
@ -116,20 +116,94 @@ const Utils = {
|
|||
if (currentTab.cookieStoreId !== identity.cookieStoreId) {
|
||||
return await browser.runtime.sendMessage({
|
||||
method: "assignAndReloadInContainer",
|
||||
url: currentTab.url,
|
||||
currentUserContextId: false,
|
||||
newUserContextId: assignedUserContextId,
|
||||
tabIndex: currentTab.index +1,
|
||||
url: currentTab.url,
|
||||
currentUserContextId: false,
|
||||
newUserContextId: assignedUserContextId,
|
||||
tabIndex: currentTab.index +1,
|
||||
active:currentTab.active
|
||||
});
|
||||
}
|
||||
await Utils.setOrRemoveAssignment(
|
||||
currentTab.id,
|
||||
currentTab.url,
|
||||
assignedUserContextId,
|
||||
currentTab.id,
|
||||
currentTab.url,
|
||||
assignedUserContextId,
|
||||
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>
|
||||
</td>
|
||||
</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>
|
||||
<hr>
|
||||
<div class="sub-header">
|
||||
|
@ -191,7 +200,7 @@
|
|||
</div>
|
||||
<div class="bottom-btn keyboard-nav hover-highlight" id="manage-containers-link" tabindex="0">
|
||||
Manage Containers
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -247,7 +256,7 @@
|
|||
<span class="menu-text truncate-text">www.mozillllllllllllllllllllllllllllllllllllla.org</span>
|
||||
<img class="trash-button" src="/img/container-close-tab.svg" />
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="bottom-btn keyboard-nav hover-highlight" id="manage-container-link" tabindex="0">
|
||||
|
@ -325,17 +334,41 @@
|
|||
</h3>
|
||||
<button class="btn-return arrow-left" id="close-container-assignment-panel"></button>
|
||||
<hr>
|
||||
<div class="scrollable edit-sites-assigned">
|
||||
<div class="sub-header">Sites assigned to this container</div>
|
||||
<table class="menu scrollable" id="edit-sites-assigned">
|
||||
<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="scrollable">
|
||||
<div class="edit-sites-assigned">
|
||||
<div class="sub-header">Sites assigned to this container</div>
|
||||
<table class="menu scrollable" id="edit-sites-assigned">
|
||||
<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-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>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue