Merge branch 'master' into proxy-support
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
const assignManager = {
|
||||
window.assignManager = {
|
||||
MENU_ASSIGN_ID: "open-in-this-container",
|
||||
MENU_REMOVE_ID: "remove-open-in-this-container",
|
||||
MENU_SEPARATOR_ID: "separator",
|
||||
MENU_HIDE_ID: "hide-container",
|
||||
MENU_MOVE_ID: "move-to-new-window-container",
|
||||
|
||||
OPEN_IN_CONTAINER: "open-bookmark-in-container-tab",
|
||||
storageArea: {
|
||||
area: browser.storage.local,
|
||||
exemptedTabs: {},
|
||||
|
||||
getSiteStoreKey(pageUrl) {
|
||||
const url = new window.URL(pageUrl);
|
||||
getSiteStoreKey(pageUrlorUrlKey) {
|
||||
if (pageUrlorUrlKey.includes("siteContainerMap@@_")) return pageUrlorUrlKey;
|
||||
const url = new window.URL(pageUrlorUrlKey);
|
||||
const storagePrefix = "siteContainerMap@@_";
|
||||
if (url.port === "80" || url.port === "443") {
|
||||
return `${storagePrefix}${url.hostname}`;
|
||||
@@ -19,29 +20,43 @@ const assignManager = {
|
||||
}
|
||||
},
|
||||
|
||||
setExempted(pageUrl, tabId) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||
setExempted(pageUrlorUrlKey, tabId) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
|
||||
if (!(siteStoreKey in this.exemptedTabs)) {
|
||||
this.exemptedTabs[siteStoreKey] = [];
|
||||
}
|
||||
this.exemptedTabs[siteStoreKey].push(tabId);
|
||||
},
|
||||
|
||||
removeExempted(pageUrl) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||
removeExempted(pageUrlorUrlKey) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
|
||||
this.exemptedTabs[siteStoreKey] = [];
|
||||
},
|
||||
|
||||
isExempted(pageUrl, tabId) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||
isExempted(pageUrlorUrlKey, tabId) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
|
||||
if (!(siteStoreKey in this.exemptedTabs)) {
|
||||
return false;
|
||||
}
|
||||
return this.exemptedTabs[siteStoreKey].includes(tabId);
|
||||
},
|
||||
|
||||
get(pageUrl) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||
get(pageUrlorUrlKey) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
|
||||
return this.getByUrlKey(siteStoreKey);
|
||||
},
|
||||
|
||||
async getSyncEnabled() {
|
||||
const { syncEnabled } = await browser.storage.local.get("syncEnabled");
|
||||
return !!syncEnabled;
|
||||
},
|
||||
|
||||
async getReplaceTabEnabled() {
|
||||
const { replaceTabEnabled } = await browser.storage.local.get("replaceTabEnabled");
|
||||
return !!replaceTabEnabled;
|
||||
},
|
||||
|
||||
getByUrlKey(siteStoreKey) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.area.get([siteStoreKey]).then((storageResponse) => {
|
||||
if (storageResponse && siteStoreKey in storageResponse) {
|
||||
@@ -54,51 +69,103 @@ const assignManager = {
|
||||
});
|
||||
},
|
||||
|
||||
set(pageUrl, data, exemptedTabIds) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||
async set(pageUrlorUrlKey, data, exemptedTabIds, backup = true) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
|
||||
if (exemptedTabIds) {
|
||||
exemptedTabIds.forEach((tabId) => {
|
||||
this.setExempted(pageUrl, tabId);
|
||||
this.setExempted(pageUrlorUrlKey, tabId);
|
||||
});
|
||||
}
|
||||
return this.area.set({
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
data.identityMacAddonUUID =
|
||||
await identityState.lookupMACaddonUUID(data.userContextId);
|
||||
await this.area.set({
|
||||
[siteStoreKey]: data
|
||||
});
|
||||
const syncEnabled = await this.getSyncEnabled();
|
||||
if (backup && syncEnabled) {
|
||||
await sync.storageArea.backup({undeleteSiteStoreKey: siteStoreKey});
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
remove(pageUrl) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||
async remove(pageUrlorUrlKey) {
|
||||
const siteStoreKey = this.getSiteStoreKey(pageUrlorUrlKey);
|
||||
// When we remove an assignment we should clear all the exemptions
|
||||
this.removeExempted(pageUrl);
|
||||
return this.area.remove([siteStoreKey]);
|
||||
this.removeExempted(pageUrlorUrlKey);
|
||||
await this.area.remove([siteStoreKey]);
|
||||
const syncEnabled = await this.getSyncEnabled();
|
||||
if (syncEnabled) await sync.storageArea.backup({siteStoreKey});
|
||||
return;
|
||||
},
|
||||
|
||||
async deleteContainer(userContextId) {
|
||||
const sitesByContainer = await this.getByContainer(userContextId);
|
||||
const sitesByContainer = await this.getAssignedSites(userContextId);
|
||||
this.area.remove(Object.keys(sitesByContainer));
|
||||
},
|
||||
|
||||
async getByContainer(userContextId) {
|
||||
async getAssignedSites(userContextId = null) {
|
||||
const sites = {};
|
||||
const siteConfigs = await this.area.get();
|
||||
Object.keys(siteConfigs).forEach((key) => {
|
||||
// For some reason this is stored as string... lets check them both as that
|
||||
if (String(siteConfigs[key].userContextId) === String(userContextId)) {
|
||||
const site = siteConfigs[key];
|
||||
for(const urlKey of Object.keys(siteConfigs)) {
|
||||
if (urlKey.includes("siteContainerMap@@_")) {
|
||||
// For some reason this is stored as string... lets check
|
||||
// them both as that
|
||||
if (!!userContextId &&
|
||||
String(siteConfigs[urlKey].userContextId)
|
||||
!== String(userContextId)) {
|
||||
continue;
|
||||
}
|
||||
const site = siteConfigs[urlKey];
|
||||
// In hindsight we should have stored this
|
||||
// TODO file a follow up to clean the storage onLoad
|
||||
site.hostname = key.replace(/^siteContainerMap@@_/, "");
|
||||
sites[key] = site;
|
||||
site.hostname = urlKey.replace(/^siteContainerMap@@_/, "");
|
||||
sites[urlKey] = site;
|
||||
}
|
||||
});
|
||||
}
|
||||
return sites;
|
||||
},
|
||||
|
||||
/*
|
||||
* Looks for abandoned site assignments. If there is no identity with
|
||||
* the site assignment's userContextId (cookieStoreId), then the assignment
|
||||
* is removed.
|
||||
*/
|
||||
async upgradeData() {
|
||||
const identitiesList = await browser.contextualIdentities.query({});
|
||||
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 match = identitiesList.find(
|
||||
localIdentity => localIdentity.cookieStoreId === cookieStoreId
|
||||
);
|
||||
if (!match) {
|
||||
await this.remove(configKey);
|
||||
continue;
|
||||
}
|
||||
const updatedSiteAssignment = macConfigs[configKey];
|
||||
updatedSiteAssignment.identityMacAddonUUID =
|
||||
await identityState.lookupMACaddonUUID(match.cookieStoreId);
|
||||
await this.set(
|
||||
configKey,
|
||||
updatedSiteAssignment,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_neverAsk(m) {
|
||||
const pageUrl = m.pageUrl;
|
||||
if (m.neverAsk === true) {
|
||||
// If we have existing data and for some reason it hasn't been deleted etc lets update it
|
||||
// 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) {
|
||||
siteSettings.neverAsk = true;
|
||||
@@ -113,12 +180,11 @@ const assignManager = {
|
||||
// We return here so the confirm page can load the tab when exempted
|
||||
async _exemptTab(m) {
|
||||
const pageUrl = m.pageUrl;
|
||||
this.storageArea.setExempted(pageUrl, m.tabId);
|
||||
await this.storageArea.setExempted(pageUrl, m.tabId);
|
||||
return true;
|
||||
},
|
||||
|
||||
async handleProxifiedRequest(requestInfo) {
|
||||
|
||||
// The following blocks potentially dangerous requests for privacy that come without a tabId
|
||||
if(requestInfo.tabId === -1)
|
||||
return Utils.getBogusProxy();
|
||||
@@ -129,7 +195,8 @@ const assignManager = {
|
||||
return proxy;
|
||||
},
|
||||
|
||||
// Before a request is handled by the browser we decide if we should route through a different container
|
||||
// Before a request is handled by the browser we decide if we should
|
||||
// route through a different container
|
||||
async onBeforeRequest(options) {
|
||||
if (options.frameId !== 0 || options.tabId === -1) {
|
||||
return {};
|
||||
@@ -141,31 +208,57 @@ const assignManager = {
|
||||
]);
|
||||
let container;
|
||||
try {
|
||||
container = await browser.contextualIdentities.get(backgroundLogic.cookieStoreId(siteSettings.userContextId));
|
||||
container = await browser.contextualIdentities
|
||||
.get(backgroundLogic.cookieStoreId(siteSettings.userContextId));
|
||||
} catch (e) {
|
||||
container = false;
|
||||
}
|
||||
|
||||
// The container we have in the assignment map isn't present any more so lets remove it
|
||||
// then continue the existing load
|
||||
// The container we have in the assignment map isn't present any
|
||||
// more so lets remove it then continue the existing load
|
||||
if (siteSettings && !container) {
|
||||
this.deleteContainer(siteSettings.userContextId);
|
||||
return {};
|
||||
}
|
||||
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
||||
if (!siteSettings
|
||||
|| userContextId === siteSettings.userContextId
|
||||
|| tab.incognito
|
||||
|| this.storageArea.isExempted(options.url, tab.id)) {
|
||||
return {};
|
||||
|
||||
// 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:
|
||||
//
|
||||
// If the current tab's container is "unlocked", we can just go ahead
|
||||
// and open the URL in the current tab, since an "unlocked" container accepts
|
||||
// any-and-all sites.
|
||||
//
|
||||
// But if the current tab's container has been "locked" by the user, then we must
|
||||
// re-open the page in the default container, because the user doesn't want random
|
||||
// sites polluting their locked container.
|
||||
//
|
||||
// For example:
|
||||
// - 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);
|
||||
|
||||
if (!siteIsolatedReloadInDefault) {
|
||||
if (!siteSettings
|
||||
|| userContextId === siteSettings.userContextId
|
||||
|| this.storageArea.isExempted(options.url, tab.id)) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
const replaceTabEnabled = await this.storageArea.getReplaceTabEnabled();
|
||||
const removeTab = backgroundLogic.NEW_TAB_PAGES.has(tab.url)
|
||||
|| (messageHandler.lastCreatedTab
|
||||
&& messageHandler.lastCreatedTab.id === tab.id);
|
||||
&& messageHandler.lastCreatedTab.id === tab.id)
|
||||
|| replaceTabEnabled;
|
||||
const openTabId = removeTab ? tab.openerTabId : tab.id;
|
||||
|
||||
if (!this.canceledRequests[tab.id]) {
|
||||
// we decided to cancel the request at this point, register canceled request
|
||||
// we decided to cancel the request at this point, register
|
||||
// canceled request
|
||||
this.canceledRequests[tab.id] = {
|
||||
requestIds: {
|
||||
[options.requestId]: true
|
||||
@@ -175,8 +268,10 @@ const assignManager = {
|
||||
}
|
||||
};
|
||||
|
||||
// since webRequest onCompleted and onErrorOccurred are not 100% reliable (see #1120)
|
||||
// we register a timer here to cleanup canceled requests, just to make sure we don't
|
||||
// since webRequest onCompleted and onErrorOccurred are not 100%
|
||||
// reliable (see #1120)
|
||||
// we register a timer here to cleanup canceled requests, just to
|
||||
// make sure we don't
|
||||
// end up in a situation where certain urls in a tab.id stay canceled
|
||||
setTimeout(() => {
|
||||
if (this.canceledRequests[tab.id]) {
|
||||
@@ -188,10 +283,12 @@ const assignManager = {
|
||||
if (this.canceledRequests[tab.id].requestIds[options.requestId] ||
|
||||
this.canceledRequests[tab.id].urls[options.url]) {
|
||||
// same requestId or url from the same tab
|
||||
// this is a redirect that we have to cancel early to prevent opening two tabs
|
||||
// this is a redirect that we have to cancel early to prevent
|
||||
// opening two tabs
|
||||
cancelEarly = true;
|
||||
}
|
||||
// we decided to cancel the request at this point, register canceled request
|
||||
// we decided to cancel the request at this point, register canceled
|
||||
// request
|
||||
this.canceledRequests[tab.id].requestIds[options.requestId] = true;
|
||||
this.canceledRequests[tab.id].urls[options.url] = true;
|
||||
if (cancelEarly) {
|
||||
@@ -201,27 +298,48 @@ const assignManager = {
|
||||
}
|
||||
}
|
||||
|
||||
this.reloadPageInContainer(
|
||||
options.url,
|
||||
userContextId,
|
||||
siteSettings.userContextId,
|
||||
tab.index + 1,
|
||||
tab.active,
|
||||
siteSettings.neverAsk,
|
||||
openTabId
|
||||
);
|
||||
if (siteIsolatedReloadInDefault) {
|
||||
this.reloadPageInDefaultContainer(
|
||||
options.url,
|
||||
tab.index + 1,
|
||||
tab.active,
|
||||
openTabId
|
||||
);
|
||||
} else {
|
||||
this.reloadPageInContainer(
|
||||
options.url,
|
||||
userContextId,
|
||||
siteSettings.userContextId,
|
||||
tab.index + 1,
|
||||
tab.active,
|
||||
siteSettings.neverAsk,
|
||||
openTabId
|
||||
);
|
||||
}
|
||||
this.calculateContextMenu(tab);
|
||||
|
||||
/* Removal of existing tabs:
|
||||
We aim to open the new assigned container tab / warning prompt in it's own tab:
|
||||
- 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: 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 losing history
|
||||
Detecting redirects on "new tab" opening actions is pretty hard as we don't get tab history:
|
||||
- Redirects happen from Short URLs and tracking links that act as a gateway
|
||||
- Extensions don't provide a way to history crawl for tabs, we could inject content scripts to do this
|
||||
however they don't run on about:blank so this would likely be just as hacky.
|
||||
We capture the time the tab was created and close if it was within the timeout to try to capture pages which haven't had user interaction or history.
|
||||
We aim to open the new assigned container tab / warning prompt in
|
||||
it's own tab:
|
||||
- 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:
|
||||
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
|
||||
losing history
|
||||
Detecting redirects on "new tab" opening actions is pretty hard
|
||||
as we don't get tab history:
|
||||
- Redirects happen from Short URLs and tracking links that act as
|
||||
a gateway
|
||||
- Extensions don't provide a way to history crawl for tabs, we
|
||||
could inject content scripts to do this
|
||||
however they don't run on about:blank so this would likely be
|
||||
just as hacky.
|
||||
We capture the time the tab was created and close if it was within
|
||||
the timeout to try to capture pages which haven't had user
|
||||
interaction or history.
|
||||
*/
|
||||
if (removeTab) {
|
||||
browser.tabs.remove(tab.id);
|
||||
@@ -231,15 +349,41 @@ const assignManager = {
|
||||
};
|
||||
},
|
||||
|
||||
async _maybeSiteIsolatedReloadInDefault(siteSettings, tab) {
|
||||
// Tab doesn't support cookies, so containers not supported either.
|
||||
if (!("cookieStoreId" in tab)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Requested page has been assigned to a specific container.
|
||||
// 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;
|
||||
}
|
||||
|
||||
//tab is alredy reopening in the default container
|
||||
if (tab.cookieStoreId === "firefox-default") {
|
||||
return false;
|
||||
}
|
||||
// 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;
|
||||
},
|
||||
|
||||
init() {
|
||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||
this._onClickedHandler(info, tab);
|
||||
info.bookmarkId ?
|
||||
this._onClickedBookmark(info) :
|
||||
this._onClickedHandler(info, tab);
|
||||
});
|
||||
|
||||
// Before anything happens we decide if the request should be proxified
|
||||
browser.proxy.onRequest.addListener(this.handleProxifiedRequest, {urls: ["<all_urls>"]});
|
||||
|
||||
// Before a request is handled by the browser we decide if we should route through a different container
|
||||
// Before a request is handled by the browser we decide if we should
|
||||
// route through a different container
|
||||
this.canceledRequests = {};
|
||||
browser.webRequest.onBeforeRequest.addListener((options) => {
|
||||
return this.onBeforeRequest(options);
|
||||
@@ -257,6 +401,60 @@ const assignManager = {
|
||||
}
|
||||
},{urls: ["<all_urls>"], types: ["main_frame"]});
|
||||
|
||||
this.resetBookmarksMenuItem();
|
||||
},
|
||||
|
||||
async resetBookmarksMenuItem() {
|
||||
const hasPermission = await browser.permissions.contains({
|
||||
permissions: ["bookmarks"]
|
||||
});
|
||||
if (this.hadBookmark === hasPermission) {
|
||||
return;
|
||||
}
|
||||
this.hadBookmark = hasPermission;
|
||||
if (hasPermission) {
|
||||
this.initBookmarksMenu();
|
||||
browser.contextualIdentities.onCreated
|
||||
.addListener(this.contextualIdentityCreated);
|
||||
browser.contextualIdentities.onUpdated
|
||||
.addListener(this.contextualIdentityUpdated);
|
||||
browser.contextualIdentities.onRemoved
|
||||
.addListener(this.contextualIdentityRemoved);
|
||||
} else {
|
||||
this.removeBookmarksMenu();
|
||||
browser.contextualIdentities.onCreated
|
||||
.removeListener(this.contextualIdentityCreated);
|
||||
browser.contextualIdentities.onUpdated
|
||||
.removeListener(this.contextualIdentityUpdated);
|
||||
browser.contextualIdentities.onRemoved
|
||||
.removeListener(this.contextualIdentityRemoved);
|
||||
}
|
||||
},
|
||||
|
||||
contextualIdentityCreated(changeInfo) {
|
||||
browser.contextMenus.create({
|
||||
parentId: assignManager.OPEN_IN_CONTAINER,
|
||||
id: changeInfo.contextualIdentity.cookieStoreId,
|
||||
title: changeInfo.contextualIdentity.name,
|
||||
icons: { "16": `img/usercontext.svg#${
|
||||
changeInfo.contextualIdentity.icon
|
||||
}` }
|
||||
});
|
||||
},
|
||||
|
||||
contextualIdentityUpdated(changeInfo) {
|
||||
browser.contextMenus.update(
|
||||
changeInfo.contextualIdentity.cookieStoreId, {
|
||||
title: changeInfo.contextualIdentity.name,
|
||||
icons: { "16": `img/usercontext.svg#${
|
||||
changeInfo.contextualIdentity.icon}` }
|
||||
});
|
||||
},
|
||||
|
||||
contextualIdentityRemoved(changeInfo) {
|
||||
browser.contextMenus.remove(
|
||||
changeInfo.contextualIdentity.cookieStoreId
|
||||
);
|
||||
},
|
||||
|
||||
async _onClickedHandler(info, tab) {
|
||||
@@ -272,7 +470,9 @@ const assignManager = {
|
||||
} else {
|
||||
remove = true;
|
||||
}
|
||||
await this._setOrRemoveAssignment(tab.id, info.pageUrl, userContextId, remove);
|
||||
await this._setOrRemoveAssignment(
|
||||
tab.id, info.pageUrl, userContextId, remove
|
||||
);
|
||||
break;
|
||||
case this.MENU_MOVE_ID:
|
||||
backgroundLogic.moveTabsToWindow({
|
||||
@@ -290,6 +490,41 @@ const assignManager = {
|
||||
}
|
||||
},
|
||||
|
||||
async _onClickedBookmark(info) {
|
||||
|
||||
async function _getBookmarksFromInfo(info) {
|
||||
const [bookmarkTreeNode] =
|
||||
await browser.bookmarks.get(info.bookmarkId);
|
||||
if (bookmarkTreeNode.type === "folder") {
|
||||
return browser.bookmarks.getChildren(bookmarkTreeNode.id);
|
||||
}
|
||||
return [bookmarkTreeNode];
|
||||
}
|
||||
|
||||
const bookmarks = await _getBookmarksFromInfo(info);
|
||||
for (const bookmark of bookmarks) {
|
||||
// Some checks on the urls from
|
||||
// https://github.com/Rob--W/bookmark-container-tab/ thanks!
|
||||
if ( !/^(javascript|place):/i.test(bookmark.url) &&
|
||||
bookmark.type !== "folder") {
|
||||
const openInReaderMode = bookmark.url.startsWith("about:reader");
|
||||
if(openInReaderMode) {
|
||||
try {
|
||||
const parsed = new URL(bookmark.url);
|
||||
bookmark.url = parsed.searchParams.get("url") + parsed.hash;
|
||||
} catch (err) {
|
||||
return err.message;
|
||||
}
|
||||
}
|
||||
browser.tabs.create({
|
||||
cookieStoreId: info.menuItemId,
|
||||
url: bookmark.url,
|
||||
openInReaderMode: openInReaderMode
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
deleteContainer(userContextId) {
|
||||
this.storageArea.deleteContainer(userContextId);
|
||||
@@ -299,16 +534,16 @@ const assignManager = {
|
||||
if (!("cookieStoreId" in tab)) {
|
||||
return false;
|
||||
}
|
||||
return backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId);
|
||||
return backgroundLogic.getUserContextIdFromCookieStoreId(
|
||||
tab.cookieStoreId
|
||||
);
|
||||
},
|
||||
|
||||
isTabPermittedAssign(tab) {
|
||||
// Ensure we are not an important about url
|
||||
// Ensure we are not in incognito mode
|
||||
const url = new URL(tab.url);
|
||||
if (url.protocol === "about:"
|
||||
|| url.protocol === "moz-extension:"
|
||||
|| tab.incognito) {
|
||||
|| url.protocol === "moz-extension:") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -316,7 +551,6 @@ const assignManager = {
|
||||
|
||||
async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) {
|
||||
let actionName;
|
||||
|
||||
// https://github.com/mozilla/testpilot-containers/issues/626
|
||||
// Context menu has stored context IDs as strings, so we need to coerce
|
||||
// the value to a string for accurate checking
|
||||
@@ -341,16 +575,40 @@ const assignManager = {
|
||||
userContextId,
|
||||
neverAsk: false
|
||||
}, exemptedTabIds);
|
||||
actionName = "added";
|
||||
actionName = "assigned site to always open in this container";
|
||||
} else {
|
||||
// Remove assignment
|
||||
await this.storageArea.remove(pageUrl);
|
||||
actionName = "removed";
|
||||
|
||||
actionName = "removed from assigned sites list";
|
||||
|
||||
// remove site isolation if now empty
|
||||
await this._maybeRemoveSiteIsolation(userContextId);
|
||||
}
|
||||
browser.tabs.sendMessage(tabId, {
|
||||
text: `Successfully ${actionName} site to always open in this container`
|
||||
});
|
||||
const tab = await browser.tabs.get(tabId);
|
||||
this.calculateContextMenu(tab);
|
||||
|
||||
if (tabId) {
|
||||
const tab = await browser.tabs.get(tabId);
|
||||
setTimeout(function(){
|
||||
browser.tabs.sendMessage(tabId, {
|
||||
text: `Successfully ${actionName}`
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
|
||||
this.calculateContextMenu(tab);
|
||||
}
|
||||
},
|
||||
|
||||
async _maybeRemoveSiteIsolation(userContextId) {
|
||||
const assignments = await this.storageArea.getByContainer(userContextId);
|
||||
const hasAssignments = assignments && Object.keys(assignments).length > 0;
|
||||
if (hasAssignments) {
|
||||
return;
|
||||
}
|
||||
await backgroundLogic.addRemoveSiteIsolation(
|
||||
backgroundLogic.cookieStoreId(userContextId),
|
||||
true
|
||||
);
|
||||
},
|
||||
|
||||
async _getAssignment(tab) {
|
||||
@@ -358,13 +616,13 @@ const assignManager = {
|
||||
// Ensure we have a cookieStore to assign to
|
||||
if (cookieStore
|
||||
&& this.isTabPermittedAssign(tab)) {
|
||||
return await this.storageArea.get(tab.url);
|
||||
return this.storageArea.get(tab.url);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_getByContainer(userContextId) {
|
||||
return this.storageArea.getByContainer(userContextId);
|
||||
return this.storageArea.getAssignedSites(userContextId);
|
||||
},
|
||||
|
||||
removeContextMenu() {
|
||||
@@ -430,13 +688,35 @@ const 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,
|
||||
// we MUST specify the openerTabId when creating the new tab.
|
||||
const cookieStoreId = "firefox-default";
|
||||
browser.tabs.create({url, cookieStoreId, index, active, openerTabId});
|
||||
},
|
||||
|
||||
reloadPageInContainer(url, currentUserContextId, userContextId, index, active, neverAsk = false, openerTabId = null) {
|
||||
const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
|
||||
const loadPage = browser.extension.getURL("confirm-page.html");
|
||||
// False represents assignment is not permitted
|
||||
// If the user has explicitly checked "Never Ask Again" on the warning page we will send them straight there
|
||||
if (neverAsk) {
|
||||
browser.tabs.create({url, cookieStoreId, index, active, openerTabId});
|
||||
return browser.tabs.create({url, cookieStoreId, index, active, openerTabId});
|
||||
} else {
|
||||
let confirmUrl = `${loadPage}?url=${this.encodeURLProperty(url)}&cookieStoreId=${cookieStoreId}`;
|
||||
let currentCookieStoreId;
|
||||
@@ -444,7 +724,7 @@ const assignManager = {
|
||||
currentCookieStoreId = backgroundLogic.cookieStoreId(currentUserContextId);
|
||||
confirmUrl += `¤tCookieStoreId=${currentCookieStoreId}`;
|
||||
}
|
||||
browser.tabs.create({
|
||||
return browser.tabs.create({
|
||||
url: confirmUrl,
|
||||
cookieStoreId: currentCookieStoreId,
|
||||
openerTabId,
|
||||
@@ -457,7 +737,33 @@ const assignManager = {
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async initBookmarksMenu() {
|
||||
browser.contextMenus.create({
|
||||
id: this.OPEN_IN_CONTAINER,
|
||||
title: "Open Bookmark in Container Tab",
|
||||
contexts: ["bookmark"],
|
||||
});
|
||||
|
||||
const identities = await browser.contextualIdentities.query({});
|
||||
for (const identity of identities) {
|
||||
browser.contextMenus.create({
|
||||
parentId: this.OPEN_IN_CONTAINER,
|
||||
id: identity.cookieStoreId,
|
||||
title: identity.name,
|
||||
icons: { "16": `img/usercontext.svg#${identity.icon}` }
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async removeBookmarksMenu() {
|
||||
browser.contextMenus.remove(this.OPEN_IN_CONTAINER);
|
||||
const identities = await browser.contextualIdentities.query({});
|
||||
for (const identity of identities) {
|
||||
browser.contextMenus.remove(identity.cookieStoreId);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
assignManager.init();
|
||||
|
||||
Reference in New Issue
Block a user