Merge pull request #1903 from mozilla/proxy-support
Updated per-container proxy support
This commit is contained in:
commit
98e3412d68
11 changed files with 321 additions and 59 deletions
|
@ -1,6 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 8
|
"ecmaVersion": 2018
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
|
@ -18,7 +18,8 @@ module.exports = {
|
||||||
"XPCOMUtils": true,
|
"XPCOMUtils": true,
|
||||||
"OS": true,
|
"OS": true,
|
||||||
"ADDON_UNINSTALL": true,
|
"ADDON_UNINSTALL": true,
|
||||||
"ADDON_DISABLE": true
|
"ADDON_DISABLE": true,
|
||||||
|
"proxifiedContainers": true
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"promise",
|
"promise",
|
||||||
|
|
|
@ -586,7 +586,7 @@ manage things like container crud */
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-container-panel fieldset:last-of-type {
|
.edit-container-panel fieldset:last-of-type {
|
||||||
margin-block-end: 0;
|
margin-block-start: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-container-panel input[type="text"] {
|
.edit-container-panel input[type="text"] {
|
||||||
|
@ -886,12 +886,11 @@ input {
|
||||||
|
|
||||||
.site-isolation {
|
.site-isolation {
|
||||||
inset-block-end: auto;
|
inset-block-end: auto;
|
||||||
position: fixed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.options-label {
|
.options-label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding-inline-start: 25px;
|
padding-inline-start: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.manage-assigned-sites-list {
|
.manage-assigned-sites-list {
|
||||||
|
|
|
@ -109,10 +109,10 @@ window.assignManager = {
|
||||||
const siteConfigs = await this.area.get();
|
const siteConfigs = await this.area.get();
|
||||||
for(const urlKey of Object.keys(siteConfigs)) {
|
for(const urlKey of Object.keys(siteConfigs)) {
|
||||||
if (urlKey.includes("siteContainerMap@@_")) {
|
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
|
// them both as that
|
||||||
if (!!userContextId &&
|
if (!!userContextId &&
|
||||||
String(siteConfigs[urlKey].userContextId)
|
String(siteConfigs[urlKey].userContextId)
|
||||||
!== String(userContextId)) {
|
!== String(userContextId)) {
|
||||||
continue;
|
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
|
* the site assignment's userContextId (cookieStoreId), then the assignment
|
||||||
* is removed.
|
* is removed.
|
||||||
*/
|
*/
|
||||||
|
@ -136,8 +136,8 @@ window.assignManager = {
|
||||||
const macConfigs = await this.area.get();
|
const macConfigs = await this.area.get();
|
||||||
for(const configKey of Object.keys(macConfigs)) {
|
for(const configKey of Object.keys(macConfigs)) {
|
||||||
if (configKey.includes("siteContainerMap@@_")) {
|
if (configKey.includes("siteContainerMap@@_")) {
|
||||||
const cookieStoreId =
|
const cookieStoreId =
|
||||||
"firefox-container-" + macConfigs[configKey].userContextId;
|
"firefox-container-" + macConfigs[configKey].userContextId;
|
||||||
const match = identitiesList.find(
|
const match = identitiesList.find(
|
||||||
localIdentity => localIdentity.cookieStoreId === cookieStoreId
|
localIdentity => localIdentity.cookieStoreId === cookieStoreId
|
||||||
);
|
);
|
||||||
|
@ -146,7 +146,7 @@ window.assignManager = {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const updatedSiteAssignment = macConfigs[configKey];
|
const updatedSiteAssignment = macConfigs[configKey];
|
||||||
updatedSiteAssignment.identityMacAddonUUID =
|
updatedSiteAssignment.identityMacAddonUUID =
|
||||||
await identityState.lookupMACaddonUUID(match.cookieStoreId);
|
await identityState.lookupMACaddonUUID(match.cookieStoreId);
|
||||||
await this.set(
|
await this.set(
|
||||||
configKey,
|
configKey,
|
||||||
|
@ -164,7 +164,7 @@ window.assignManager = {
|
||||||
_neverAsk(m) {
|
_neverAsk(m) {
|
||||||
const pageUrl = m.pageUrl;
|
const pageUrl = m.pageUrl;
|
||||||
if (m.neverAsk === true) {
|
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
|
// deleted etc lets update it
|
||||||
this.storageArea.get(pageUrl).then((siteSettings) => {
|
this.storageArea.get(pageUrl).then((siteSettings) => {
|
||||||
if (siteSettings) {
|
if (siteSettings) {
|
||||||
|
@ -184,6 +184,17 @@ window.assignManager = {
|
||||||
return true;
|
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();
|
||||||
|
|
||||||
|
const tab = await browser.tabs.get(requestInfo.tabId);
|
||||||
|
const proxy = await proxifiedContainers.retrieveFromBackground(tab.cookieStoreId);
|
||||||
|
|
||||||
|
return proxy;
|
||||||
|
},
|
||||||
|
|
||||||
// Before a request is handled by the browser we decide if we should
|
// Before a request is handled by the browser we decide if we should
|
||||||
// route through a different container
|
// route through a different container
|
||||||
async onBeforeRequest(options) {
|
async onBeforeRequest(options) {
|
||||||
|
@ -212,7 +223,7 @@ window.assignManager = {
|
||||||
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
||||||
|
|
||||||
// https://github.com/mozilla/multi-account-containers/issues/847
|
// https://github.com/mozilla/multi-account-containers/issues/847
|
||||||
//
|
//
|
||||||
// Handle the case where this request's URL is not assigned to any particular
|
// Handle the case where this request's URL is not assigned to any particular
|
||||||
// container. We must do the following check:
|
// container. We must do the following check:
|
||||||
//
|
//
|
||||||
|
@ -228,7 +239,7 @@ 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(siteSettings, tab);
|
await this._maybeSiteIsolatedReloadInDefault(siteSettings, tab);
|
||||||
|
|
||||||
if (!siteIsolatedReloadInDefault) {
|
if (!siteIsolatedReloadInDefault) {
|
||||||
|
@ -246,7 +257,7 @@ window.assignManager = {
|
||||||
const openTabId = removeTab ? tab.openerTabId : tab.id;
|
const openTabId = removeTab ? tab.openerTabId : tab.id;
|
||||||
|
|
||||||
if (!this.canceledRequests[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
|
// canceled request
|
||||||
this.canceledRequests[tab.id] = {
|
this.canceledRequests[tab.id] = {
|
||||||
requestIds: {
|
requestIds: {
|
||||||
|
@ -313,7 +324,7 @@ window.assignManager = {
|
||||||
- As the history won't span from one container to another it
|
- 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()
|
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
|
- 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
|
https://github.com/mozilla/testpilot-containers/issues/421
|
||||||
If we are coming from an internal url that are used for the new
|
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
|
tab page (NEW_TAB_PAGES), we can safely close as user is unlikely
|
||||||
|
@ -348,7 +359,7 @@ window.assignManager = {
|
||||||
// I.e. it will be opened in that container anyway, so we don't need to check if the
|
// 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.
|
// current tab's container is locked or not.
|
||||||
if (siteSettings) {
|
if (siteSettings) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//tab is alredy reopening in the default container
|
//tab is alredy reopening in the default container
|
||||||
|
@ -363,11 +374,14 @@ window.assignManager = {
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
info.bookmarkId ?
|
info.bookmarkId ?
|
||||||
this._onClickedBookmark(info) :
|
this._onClickedBookmark(info) :
|
||||||
this._onClickedHandler(info, tab);
|
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
|
// Before a request is handled by the browser we decide if we should
|
||||||
// route through a different container
|
// route through a different container
|
||||||
this.canceledRequests = {};
|
this.canceledRequests = {};
|
||||||
|
@ -479,7 +493,7 @@ window.assignManager = {
|
||||||
async _onClickedBookmark(info) {
|
async _onClickedBookmark(info) {
|
||||||
|
|
||||||
async function _getBookmarksFromInfo(info) {
|
async function _getBookmarksFromInfo(info) {
|
||||||
const [bookmarkTreeNode] =
|
const [bookmarkTreeNode] =
|
||||||
await browser.bookmarks.get(info.bookmarkId);
|
await browser.bookmarks.get(info.bookmarkId);
|
||||||
if (bookmarkTreeNode.type === "folder") {
|
if (bookmarkTreeNode.type === "folder") {
|
||||||
return browser.bookmarks.getChildren(bookmarkTreeNode.id);
|
return browser.bookmarks.getChildren(bookmarkTreeNode.id);
|
||||||
|
@ -489,9 +503,9 @@ window.assignManager = {
|
||||||
|
|
||||||
const bookmarks = await _getBookmarksFromInfo(info);
|
const bookmarks = await _getBookmarksFromInfo(info);
|
||||||
for (const bookmark of bookmarks) {
|
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!
|
// 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") {
|
bookmark.type !== "folder") {
|
||||||
const openInReaderMode = bookmark.url.startsWith("about:reader");
|
const openInReaderMode = bookmark.url.startsWith("about:reader");
|
||||||
if(openInReaderMode) {
|
if(openInReaderMode) {
|
||||||
|
@ -569,12 +583,12 @@ window.assignManager = {
|
||||||
actionName = "removed from assigned sites list";
|
actionName = "removed from assigned sites list";
|
||||||
|
|
||||||
// remove site isolation if now empty
|
// remove site isolation if now empty
|
||||||
await this._maybeRemoveSiteIsolation(userContextId);
|
await this._maybeRemoveSiteIsolation(userContextId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabId) {
|
if (tabId) {
|
||||||
const tab = await browser.tabs.get(tabId);
|
const tab = await browser.tabs.get(tabId);
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
browser.tabs.sendMessage(tabId, {
|
browser.tabs.sendMessage(tabId, {
|
||||||
text: `Successfully ${actionName}`
|
text: `Successfully ${actionName}`
|
||||||
});
|
});
|
||||||
|
@ -677,17 +691,17 @@ window.assignManager = {
|
||||||
reloadPageInDefaultContainer(url, index, active, openerTabId) {
|
reloadPageInDefaultContainer(url, index, active, openerTabId) {
|
||||||
// To create a new tab in the default container, it is easiest just to omit the
|
// To create a new tab in the default container, it is easiest just to omit the
|
||||||
// cookieStoreId entirely.
|
// cookieStoreId entirely.
|
||||||
//
|
//
|
||||||
// Unfortunately, if you create a new tab WITHOUT a cookieStoreId but WITH an openerTabId,
|
// 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.
|
// then the new tab automatically inherits the opener tab's cookieStoreId.
|
||||||
// I.e. it opens in the wrong container!
|
// I.e. it opens in the wrong container!
|
||||||
//
|
//
|
||||||
// So we have to explicitly pass in a cookieStoreId when creating the tab, since we
|
// 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
|
// are specifying the openerTabId. There doesn't seem to be any way
|
||||||
// to look up the default container's cookieStoreId programatically, so sadly
|
// to look up the default container's cookieStoreId programatically, so sadly
|
||||||
// we have to hardcode it here as "firefox-default". This is potentially
|
// we have to hardcode it here as "firefox-default". This is potentially
|
||||||
// not cross-browser compatible.
|
// not cross-browser compatible.
|
||||||
//
|
//
|
||||||
// Note that we could have just omitted BOTH cookieStoreId and openerTabId. But the
|
// 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
|
// 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,
|
// does not automatically return to the original opener tab. To get this desired behaviour,
|
||||||
|
|
|
@ -45,6 +45,10 @@ const backgroundLogic = {
|
||||||
await browser.contextualIdentities.remove(this.cookieStoreId(userContextId));
|
await browser.contextualIdentities.remove(this.cookieStoreId(userContextId));
|
||||||
}
|
}
|
||||||
assignManager.deleteContainer(userContextId);
|
assignManager.deleteContainer(userContextId);
|
||||||
|
|
||||||
|
// Now remove the identity->proxy association in proxifiedContainers also
|
||||||
|
proxifiedContainers.delete(this.cookieStoreId(userContextId));
|
||||||
|
|
||||||
return {done: true, userContextId};
|
return {done: true, userContextId};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -55,8 +59,17 @@ const backgroundLogic = {
|
||||||
this.cookieStoreId(options.userContextId),
|
this.cookieStoreId(options.userContextId),
|
||||||
options.params
|
options.params
|
||||||
);
|
);
|
||||||
|
|
||||||
|
proxifiedContainers.set(this.cookieStoreId(options.userContextId), options.proxy);
|
||||||
} else {
|
} else {
|
||||||
donePromise = browser.contextualIdentities.create(options.params);
|
donePromise = browser.contextualIdentities.create(options.params);
|
||||||
|
|
||||||
|
// We cannot yet access the new cookieStoreId via this.cookieStoreId(...), so we take this from the resolved promise
|
||||||
|
donePromise.then((identity) => {
|
||||||
|
proxifiedContainers.set(identity.cookieStoreId, options.proxy);
|
||||||
|
}).catch(() => {
|
||||||
|
// Empty because this should never happen theoretically.
|
||||||
|
});
|
||||||
}
|
}
|
||||||
await donePromise;
|
await donePromise;
|
||||||
},
|
},
|
||||||
|
@ -183,7 +196,7 @@ const backgroundLogic = {
|
||||||
index: -1
|
index: -1
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//As we get a blank tab here we will need to await the tabs creation
|
// As we get a blank tab here we will need to await the tabs creation
|
||||||
newWindowObj = await browser.windows.create({
|
newWindowObj = await browser.windows.create({
|
||||||
});
|
});
|
||||||
hiddenDefaultTabToClose = true;
|
hiddenDefaultTabToClose = true;
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
"js/background/messageHandler.js",
|
"js/background/messageHandler.js",
|
||||||
]
|
]
|
||||||
-->
|
-->
|
||||||
|
<script type="text/javascript" src="../utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../proxified-containers.js"></script>
|
||||||
<script type="text/javascript" src="backgroundLogic.js"></script>
|
<script type="text/javascript" src="backgroundLogic.js"></script>
|
||||||
<script type="text/javascript" src="assignManager.js"></script>
|
<script type="text/javascript" src="assignManager.js"></script>
|
||||||
<script type="text/javascript" src="badge.js"></script>
|
<script type="text/javascript" src="badge.js"></script>
|
||||||
|
|
|
@ -307,6 +307,10 @@ const Logic = {
|
||||||
return Utils.userContextId(identity.cookieStoreId);
|
return Utils.userContextId(identity.cookieStoreId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cookieStoreId(userContextId) {
|
||||||
|
return `firefox-container-${userContextId}`;
|
||||||
|
},
|
||||||
|
|
||||||
currentCookieStoreId() {
|
currentCookieStoreId() {
|
||||||
const identity = Logic.currentIdentity();
|
const identity = Logic.currentIdentity();
|
||||||
return identity.cookieStoreId;
|
return identity.cookieStoreId;
|
||||||
|
@ -1348,8 +1352,9 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
params: {
|
params: {
|
||||||
name: document.getElementById("edit-container-panel-name-input").value || Logic.generateIdentityName(),
|
name: document.getElementById("edit-container-panel-name-input").value || Logic.generateIdentityName(),
|
||||||
icon: formValues.get("container-icon") || DEFAULT_ICON,
|
icon: formValues.get("container-icon") || DEFAULT_ICON,
|
||||||
color: formValues.get("container-color") || DEFAULT_COLOR,
|
color: formValues.get("container-color") || DEFAULT_COLOR
|
||||||
}
|
},
|
||||||
|
proxy: proxifiedContainers.parseProxy(document.getElementById("edit-container-panel-proxy").value) || Utils.DEFAULT_PROXY
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await Logic.refreshIdentities();
|
await Logic.refreshIdentities();
|
||||||
|
@ -1423,6 +1428,37 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
|
||||||
iconInput.checked = iconInput.value === identity.icon;
|
iconInput.checked = iconInput.value === identity.icon;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Clear the proxy field before doing the retrieval requests below
|
||||||
|
document.querySelector("#edit-container-panel-proxy").value = "";
|
||||||
|
|
||||||
|
const edit_proxy_dom = function(result) {
|
||||||
|
const proxyInput = document.querySelector("#edit-container-panel-proxy");
|
||||||
|
if (result.type === "direct" || typeof result.type === "undefined") {
|
||||||
|
proxyInput.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proxyInput.value = `${result.type}://${result.host}:${result.port}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
proxifiedContainers.retrieve(identity.cookieStoreId).then((result) => {
|
||||||
|
edit_proxy_dom(result.proxy);
|
||||||
|
}, (error) => {
|
||||||
|
if(error.error === "uninitialized" || error.error === "doesnotexist") {
|
||||||
|
proxifiedContainers.set(identity.cookieStoreId, Utils.DEFAULT_PROXY, error.error === "uninitialized").then((result) => {
|
||||||
|
edit_proxy_dom(result);
|
||||||
|
}, (error) => {
|
||||||
|
proxifiedContainers.report_proxy_error(error, "popup.js: unexpected set(...) error");
|
||||||
|
}).catch((error) => {
|
||||||
|
proxifiedContainers.report_proxy_error(error, "popup.js: unexpected set(...) exception");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
proxifiedContainers.report_proxy_error(error, "popup.js: unknown error");
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
proxifiedContainers.report_proxy_error(err, "popup.js: unexpected retrieve error");
|
||||||
|
});
|
||||||
|
|
||||||
const deleteButton = document.getElementById("delete-container-button");
|
const deleteButton = document.getElementById("delete-container-button");
|
||||||
Utils.addEnterHandler(deleteButton, () => {
|
Utils.addEnterHandler(deleteButton, () => {
|
||||||
Logic.showPanel(P_CONTAINER_DELETE, identity);
|
Logic.showPanel(P_CONTAINER_DELETE, identity);
|
||||||
|
|
147
src/js/proxified-containers.js
Normal file
147
src/js/proxified-containers.js
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// This object allows other scripts to access the list mapping containers to their proxies
|
||||||
|
proxifiedContainers = {
|
||||||
|
|
||||||
|
// Slightly modified version of 'retrieve' which returns a direct proxy whenever an error is met.
|
||||||
|
retrieveFromBackground(cookieStoreId = null) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
proxifiedContainers.retrieve(cookieStoreId).then((success) => {
|
||||||
|
resolve(success.proxy);
|
||||||
|
}, function() {
|
||||||
|
resolve(Utils.DEFAULT_PROXY);
|
||||||
|
}).catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
report_proxy_error(error, identifier = null) {
|
||||||
|
// Currently I print to console but this is inefficient
|
||||||
|
const relevant_id_str = identifier === null ? "" : ` call supplied with id: ${identifier.toString()}`;
|
||||||
|
browser.extension.getBackgroundPage().console.log(`proxifiedContainers error occured ${relevant_id_str}: ${JSON.stringify(error)}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Resolves to a proxy object which can be used in the return of the listener required for browser.proxy.onRequest.addListener
|
||||||
|
retrieve(cookieStoreId = null) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
browser.storage.local.get("proxifiedContainersKey").then((results) => {
|
||||||
|
// Steps to test:
|
||||||
|
// 1. Is result empty? If so we must inform the caller to intialize proxifiedContainersStore with some initial info.
|
||||||
|
// 2. Is cookieStoreId null? This means the caller probably wants everything currently in the proxifiedContainersStore object store
|
||||||
|
// 3. If there doesn't exist an entry for the associated cookieStoreId, inform the caller of this
|
||||||
|
// 4. Normal operation - if the cookieStoreId exists in the map, we can simply resolve with the correct proxy value
|
||||||
|
|
||||||
|
const results_array = results["proxifiedContainersKey"];
|
||||||
|
|
||||||
|
if (Object.getOwnPropertyNames(results).length === 0) {
|
||||||
|
reject({
|
||||||
|
error: "uninitialized",
|
||||||
|
message: ""
|
||||||
|
});
|
||||||
|
} else if (cookieStoreId === null) {
|
||||||
|
resolve(results_array);
|
||||||
|
} else {
|
||||||
|
const val = results_array.find(o => o.cookieStoreId === cookieStoreId);
|
||||||
|
|
||||||
|
if (typeof val !== "object" || val === null) {
|
||||||
|
reject({
|
||||||
|
error: "doesnotexist",
|
||||||
|
message: ""
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, (error) => {
|
||||||
|
reject({
|
||||||
|
error: "internal",
|
||||||
|
message: error
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
proxifiedContainers.report_proxy_error(error, "proxified-containers.js: error 1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
set(cookieStoreId, proxy, initialize = false) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (initialize === true) {
|
||||||
|
const proxifiedContainersStore = [];
|
||||||
|
proxifiedContainersStore.push({
|
||||||
|
cookieStoreId: cookieStoreId,
|
||||||
|
proxy: proxy
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.storage.local.set({
|
||||||
|
proxifiedContainersKey: proxifiedContainersStore
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes proxy is a properly formatted object
|
||||||
|
proxifiedContainers.retrieve().then((proxifiedContainersStore) => {
|
||||||
|
let index = proxifiedContainersStore.findIndex(i => i.cookieStoreId === cookieStoreId);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
proxifiedContainersStore.push({
|
||||||
|
cookieStoreId: cookieStoreId,
|
||||||
|
proxy: proxy
|
||||||
|
});
|
||||||
|
index = proxifiedContainersStore.length - 1;
|
||||||
|
} else {
|
||||||
|
proxifiedContainersStore[index] = {
|
||||||
|
cookieStoreId: cookieStoreId,
|
||||||
|
proxy: proxy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.storage.local.set({
|
||||||
|
proxifiedContainersKey: proxifiedContainersStore
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(proxifiedContainersStore[index]);
|
||||||
|
}, (errorObj) => {
|
||||||
|
reject(errorObj);
|
||||||
|
}).catch((error) => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
//Parses a proxy description string of the format type://host[:port] or type://username:password@host[:port] (port is optional)
|
||||||
|
parseProxy(proxy_str) {
|
||||||
|
const proxyRegexp = /(?<type>(https?)|(socks4?)):\/\/(\b(?<username>\w+):(?<password>\w+)@)?(?<host>((?:\d{1,3}\.){3}\d{1,3}\b)|(\b([\w.-]+)(\.([\w.-]+))+))(:(?<port>\d+))?/;
|
||||||
|
if (proxyRegexp.test(proxy_str) !== true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const matches = proxyRegexp.exec(proxy_str);
|
||||||
|
return matches.groups;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Deletes the proxy information object for a specified cookieStoreId [useful for cleaning]
|
||||||
|
delete(cookieStoreId) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Assumes proxy is a properly formatted object
|
||||||
|
proxifiedContainers.retrieve().then((proxifiedContainersStore) => {
|
||||||
|
const index = proxifiedContainersStore.findIndex(i => i.cookieStoreId === cookieStoreId);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
reject({error: "not-found", message: `Container '${cookieStoreId}' not found.`});
|
||||||
|
} else {
|
||||||
|
proxifiedContainersStore.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.storage.local.set({
|
||||||
|
proxifiedContainersKey: proxifiedContainersStore
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
}, (errorObj) => {
|
||||||
|
reject(errorObj);
|
||||||
|
}).catch((error) => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,3 +1,5 @@
|
||||||
|
/*global getBogusProxy */
|
||||||
|
|
||||||
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
|
||||||
|
@ -19,6 +21,40 @@ const Utils = {
|
||||||
imageElement.addEventListener("load", loadListener);
|
imageElement.addEventListener("load", loadListener);
|
||||||
return imageElement;
|
return imageElement;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// See comment in PR #313 - so far the (hacky) method being used to block proxies is to produce a sufficiently long random address
|
||||||
|
getBogusProxy() {
|
||||||
|
const bogusFailover = 1;
|
||||||
|
const bogusType = "socks4";
|
||||||
|
const bogusPort = 9999;
|
||||||
|
const bogusUsername = "foo";
|
||||||
|
if(typeof window.Utils.pregeneratedString !== "undefined")
|
||||||
|
{
|
||||||
|
return {type:bogusType, host:`w.${window.Utils.pregeneratedString}.coo`, port:bogusPort, username:bogusUsername, failoverTimeout:bogusFailover};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Initialize Utils.pregeneratedString
|
||||||
|
window.Utils.pregeneratedString = "";
|
||||||
|
|
||||||
|
// We generate a cryptographically random string (of length specified in bogusLength), but we only do so once - thus negating any time delay caused
|
||||||
|
const bogusLength = 8;
|
||||||
|
const array = new Uint8Array(bogusLength);
|
||||||
|
window.crypto.getRandomValues(array);
|
||||||
|
for(let i = 0; i < bogusLength; i++)
|
||||||
|
{
|
||||||
|
const s = array[i].toString(16);
|
||||||
|
if(s.length === 1)
|
||||||
|
window.Utils.pregeneratedString += `0${s}`;
|
||||||
|
else
|
||||||
|
window.Utils.pregeneratedString += s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only issue with this approach is that if (for some unknown reason) pregeneratedString is not saved, it will result in an infinite loop - but better than a privacy leak!
|
||||||
|
return getBogusProxy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escapes any occurances of &, ", <, > or / with XML entities.
|
* Escapes any occurances of &, ", <, > or / with XML entities.
|
||||||
*
|
*
|
||||||
|
@ -62,7 +98,7 @@ const Utils = {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
addEnterHandler(element, handler) {
|
addEnterHandler(element, handler) {
|
||||||
element.addEventListener("click", (e) => {
|
element.addEventListener("click", (e) => {
|
||||||
handler(e);
|
handler(e);
|
||||||
|
@ -82,7 +118,7 @@ const Utils = {
|
||||||
handler(e);
|
handler(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
userContextId(cookieStoreId = "") {
|
userContextId(cookieStoreId = "") {
|
||||||
const userContextId = cookieStoreId.replace("firefox-container-", "");
|
const userContextId = cookieStoreId.replace("firefox-container-", "");
|
||||||
|
@ -102,10 +138,10 @@ const Utils = {
|
||||||
async reloadInContainer(url, currentUserContextId, newUserContextId, tabIndex, active) {
|
async reloadInContainer(url, currentUserContextId, newUserContextId, tabIndex, active) {
|
||||||
return await browser.runtime.sendMessage({
|
return await browser.runtime.sendMessage({
|
||||||
method: "reloadInContainer",
|
method: "reloadInContainer",
|
||||||
url,
|
url,
|
||||||
currentUserContextId,
|
currentUserContextId,
|
||||||
newUserContextId,
|
newUserContextId,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
active
|
active
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -116,22 +152,30 @@ const Utils = {
|
||||||
if (currentTab.cookieStoreId !== identity.cookieStoreId) {
|
if (currentTab.cookieStoreId !== identity.cookieStoreId) {
|
||||||
return await browser.runtime.sendMessage({
|
return await browser.runtime.sendMessage({
|
||||||
method: "assignAndReloadInContainer",
|
method: "assignAndReloadInContainer",
|
||||||
url: currentTab.url,
|
url: currentTab.url,
|
||||||
currentUserContextId: false,
|
currentUserContextId: false,
|
||||||
newUserContextId: assignedUserContextId,
|
newUserContextId: assignedUserContextId,
|
||||||
tabIndex: currentTab.index +1,
|
tabIndex: currentTab.index +1,
|
||||||
active:currentTab.active
|
active:currentTab.active
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await Utils.setOrRemoveAssignment(
|
await Utils.setOrRemoveAssignment(
|
||||||
currentTab.id,
|
currentTab.id,
|
||||||
currentTab.url,
|
currentTab.url,
|
||||||
assignedUserContextId,
|
assignedUserContextId,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.Utils = Utils;
|
||||||
|
|
||||||
window.Utils = Utils;
|
// The following creates a fake (but convincing) constant Utils.DEFAULT_PROXY
|
||||||
|
Object.defineProperty(window.Utils, "DEFAULT_PROXY", {
|
||||||
|
value: Object.freeze({type: "direct"}),
|
||||||
|
writable: false,
|
||||||
|
enumerable: true,
|
||||||
|
|
||||||
|
// Setting configurable to false avoids deletion of Utils.DEFAULT_PROXY
|
||||||
|
configurable: false
|
||||||
|
});
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
"unlimitedStorage",
|
"unlimitedStorage",
|
||||||
"tabs",
|
"tabs",
|
||||||
"webRequestBlocking",
|
"webRequestBlocking",
|
||||||
"webRequest"
|
"webRequest",
|
||||||
|
"proxy"
|
||||||
],
|
],
|
||||||
"optional_permissions": [
|
"optional_permissions": [
|
||||||
"bookmarks"
|
"bookmarks"
|
||||||
|
|
|
@ -229,7 +229,7 @@
|
||||||
<span class="menu-text truncate-text">www.mozillllllllllllllllllllllllllllllllllllla.org</span>
|
<span class="menu-text truncate-text">www.mozillllllllllllllllllllllllllllllllllllla.org</span>
|
||||||
<img class="trash-button" src="/img/container-close-tab.svg" />
|
<img class="trash-button" src="/img/container-close-tab.svg" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-btn keyboard-nav hover-highlight" id="manage-container-link" tabindex="0" data-i18n-message-id="manageThisContainer"></div>
|
<div class="bottom-btn keyboard-nav hover-highlight" id="manage-container-link" tabindex="0" data-i18n-message-id="manageThisContainer"></div>
|
||||||
|
@ -277,6 +277,10 @@
|
||||||
<fieldset id="edit-container-panel-choose-icon" class="radio-choice">
|
<fieldset id="edit-container-panel-choose-icon" class="radio-choice">
|
||||||
<legend class="form-header" data-i18n-message-id="icon"></legend>
|
<legend class="form-header" data-i18n-message-id="icon"></legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Proxy (Optional)</legend>
|
||||||
|
<input type="text" name="container-proxy" id="edit-container-panel-proxy" maxlength="50" placeholder="type://host:port"/>
|
||||||
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
<div id="edit-container-options">
|
<div id="edit-container-options">
|
||||||
<div class="options-header" data-i18n-message-id="options"></div>
|
<div class="options-header" data-i18n-message-id="options"></div>
|
||||||
|
@ -332,6 +336,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="js/utils.js"></script>
|
<script src="js/utils.js"></script>
|
||||||
|
<script src="js/proxified-containers.js"></script>
|
||||||
<script src="js/popup.js"></script>
|
<script src="js/popup.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
"node": true,
|
"node": true,
|
||||||
"mocha": true
|
"mocha": true
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2018
|
"ecmaVersion": 2018
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-restricted-globals": ["error", "browser"]
|
"no-restricted-globals": ["error", "browser"]
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue