Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| adadb98482 | |||
| 25e760cd64 | |||
| 0ff8e17005 | |||
| c433c6b39e | |||
| 1c09c29104 | |||
| c1e9cc3c56 | |||
| 27296d24c5 | |||
| 030e635417 | |||
| 07711aaecc | |||
| 16ed8992e2 | |||
| 88e6dc7a05 | |||
| 28e8d46743 | |||
| 77ba1b723f | |||
| b0cc6e7c2f | |||
| e84e482130 | |||
| b5ae20b874 | |||
| 3ec81e3d1f | |||
| fb5436c287 | |||
| 01a628822b | |||
| 66e2c8e297 | |||
| 80661d68f2 | |||
| ef8aa3be75 | |||
| 6bc056e019 | |||
| 75deab139b | |||
| ae79f0a303 | |||
| 9b83068234 | |||
| fec2be9429 | |||
| 9f1b06ddd3 | |||
| ad2198e8b5 | |||
| 1791fdf0ef |
@@ -3,6 +3,7 @@ docs/
|
|||||||
test/
|
test/
|
||||||
.npm/
|
.npm/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
bin/
|
||||||
|
|
||||||
.env
|
.env
|
||||||
.eslintrc.js
|
.eslintrc.js
|
||||||
@@ -14,6 +15,7 @@ node_modules/
|
|||||||
.stylelintrc
|
.stylelintrc
|
||||||
.travis.yml
|
.travis.yml
|
||||||
*.xpi
|
*.xpi
|
||||||
|
*.md
|
||||||
.vimrc
|
.vimrc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.gdb_history
|
.gdb_history
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Firefox Multi-Account Containers
|
# Multi-Account Containers
|
||||||
|
|
||||||
[](https://testpilot.firefox.com/experiments/containers)
|
|
||||||
|
|
||||||
[Embedded Web Extension](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Embedded_WebExtensions) to build [Containers](https://blog.mozilla.org/tanvi/2016/06/16/contextual-identities-on-the-web/) as a Firefox [Test Pilot](https://testpilot.firefox.com/) Experiment and [Shield Study](https://wiki.mozilla.org/Firefox/Shield/Shield_Studies) to learn:
|
The Firefox Multi-Account Containers extension lets you carve out a separate box for each of your online lives – no more opening a different browser just to check your work email! [Learn More Here](https://blog.mozilla.org/firefox/introducing-firefox-multi-account-containers/)
|
||||||
|
|
||||||
* Will a general Firefox audience understand the Containers feature?
|
[Available on addons.mozilla.org](https://addons.mozilla.org/en-GB/firefox/addon/multi-account-containers/)
|
||||||
* Is the UI as currently implemented in Nightly clear or discoverable?
|
|
||||||
|
**Note:** Firefox 57 + 58 users should Install from our [latest GitHub Release](https://github.com/mozilla/testpilot-containers/releases/latest)
|
||||||
|
|
||||||
For more info, see:
|
For more info, see:
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -7,16 +7,16 @@
|
|||||||
<em:bootstrap>true</em:bootstrap>
|
<em:bootstrap>true</em:bootstrap>
|
||||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||||
<em:hasEmbeddedWebExtension>true</em:hasEmbeddedWebExtension>
|
<em:hasEmbeddedWebExtension>true</em:hasEmbeddedWebExtension>
|
||||||
<em:name>Firefox Multi-Account Containers</em:name>
|
<em:name>Multi-Account Containers</em:name>
|
||||||
<em:description>Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.</em:description>
|
<em:description>Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.</em:description>
|
||||||
<em:targetApplication>
|
<em:targetApplication>
|
||||||
<Description>
|
<Description>
|
||||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--Firefox-->
|
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--Firefox-->
|
||||||
<em:minVersion>51.0a1</em:minVersion>
|
<em:minVersion>53.0</em:minVersion>
|
||||||
<em:maxVersion>*</em:maxVersion>
|
<em:maxVersion>*</em:maxVersion>
|
||||||
</Description>
|
</Description>
|
||||||
</em:targetApplication>
|
</em:targetApplication>
|
||||||
<em:version>4.0.1</em:version>
|
<em:version>4.0.3</em:version>
|
||||||
<em:unpack>false</em:unpack>
|
<em:unpack>false</em:unpack>
|
||||||
</Description>
|
</Description>
|
||||||
</RDF>
|
</RDF>
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "testpilot-containers",
|
"name": "testpilot-containers",
|
||||||
"title": "Firefox Multi-Account Containers",
|
"title": "Multi-Account Containers",
|
||||||
"description": "Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.",
|
"description": "Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.",
|
||||||
"version": "4.0.1",
|
"version": "4.0.3",
|
||||||
"author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston",
|
"author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/mozilla/testpilot-containers/issues"
|
"url": "https://github.com/mozilla/testpilot-containers/issues"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<title>Firefox Multi-Account Containers Confirm Navigation</title>
|
<title>Multi-Account Containers Confirm Navigation</title>
|
||||||
<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
|
<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
|
||||||
<link rel="stylesheet" href="/css/confirm-page.css" />
|
<link rel="stylesheet" href="/css/confirm-page.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -113,6 +113,61 @@ const assignManager = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 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 {};
|
||||||
|
}
|
||||||
|
this.removeContextMenu();
|
||||||
|
const [tab, siteSettings] = await Promise.all([
|
||||||
|
browser.tabs.get(options.tabId),
|
||||||
|
this.storageArea.get(options.url)
|
||||||
|
]);
|
||||||
|
let container;
|
||||||
|
try {
|
||||||
|
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
|
||||||
|
if (!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 {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadPageInContainer(options.url, userContextId, siteSettings.userContextId, tab.index + 1, siteSettings.neverAsk);
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
if (backgroundLogic.NEW_TAB_PAGES.has(tab.url)
|
||||||
|
|| (messageHandler.lastCreatedTab
|
||||||
|
&& messageHandler.lastCreatedTab.id === tab.id)) {
|
||||||
|
browser.tabs.remove(tab.id);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
cancel: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
browser.contextMenus.onClicked.addListener((info, tab) => {
|
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
this._onClickedHandler(info, tab);
|
this._onClickedHandler(info, tab);
|
||||||
@@ -120,47 +175,7 @@ const assignManager = {
|
|||||||
|
|
||||||
// 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
|
||||||
browser.webRequest.onBeforeRequest.addListener((options) => {
|
browser.webRequest.onBeforeRequest.addListener((options) => {
|
||||||
if (options.frameId !== 0 || options.tabId === -1) {
|
return this.onBeforeRequest(options);
|
||||||
return {};
|
|
||||||
}
|
|
||||||
this.removeContextMenu();
|
|
||||||
return Promise.all([
|
|
||||||
browser.tabs.get(options.tabId),
|
|
||||||
this.storageArea.get(options.url)
|
|
||||||
]).then(([tab, siteSettings]) => {
|
|
||||||
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
|
||||||
if (!siteSettings
|
|
||||||
|| userContextId === siteSettings.userContextId
|
|
||||||
|| tab.incognito
|
|
||||||
|| this.storageArea.isExempted(options.url, tab.id)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.reloadPageInContainer(options.url, userContextId, siteSettings.userContextId, tab.index + 1, siteSettings.neverAsk);
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
if (backgroundLogic.NEW_TAB_PAGES.has(tab.url)
|
|
||||||
|| (messageHandler.lastCreatedTab
|
|
||||||
&& messageHandler.lastCreatedTab.id === tab.id)) {
|
|
||||||
browser.tabs.remove(tab.id);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
cancel: true,
|
|
||||||
};
|
|
||||||
}).catch((e) => {
|
|
||||||
throw e;
|
|
||||||
});
|
|
||||||
},{urls: ["<all_urls>"], types: ["main_frame"]}, ["blocking"]);
|
},{urls: ["<all_urls>"], types: ["main_frame"]}, ["blocking"]);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -328,6 +343,13 @@ const assignManager = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
encodeURLProperty(url) {
|
||||||
|
return encodeURIComponent(url).replace(/[!'()*]/g, (c) => {
|
||||||
|
const charCode = c.charCodeAt(0).toString(16);
|
||||||
|
return `%${charCode}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
reloadPageInContainer(url, currentUserContextId, userContextId, index, neverAsk = false) {
|
reloadPageInContainer(url, currentUserContextId, userContextId, index, neverAsk = false) {
|
||||||
const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
|
const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
|
||||||
const loadPage = browser.extension.getURL("confirm-page.html");
|
const loadPage = browser.extension.getURL("confirm-page.html");
|
||||||
@@ -336,7 +358,7 @@ const assignManager = {
|
|||||||
if (neverAsk) {
|
if (neverAsk) {
|
||||||
browser.tabs.create({url, cookieStoreId, index});
|
browser.tabs.create({url, cookieStoreId, index});
|
||||||
} else {
|
} else {
|
||||||
let confirmUrl = `${loadPage}?url=${encodeURIComponent(url)}&cookieStoreId=${cookieStoreId}`;
|
let confirmUrl = `${loadPage}?url=${this.encodeURLProperty(url)}&cookieStoreId=${cookieStoreId}`;
|
||||||
let currentCookieStoreId;
|
let currentCookieStoreId;
|
||||||
if (currentUserContextId) {
|
if (currentUserContextId) {
|
||||||
currentCookieStoreId = backgroundLogic.cookieStoreId(currentUserContextId);
|
currentCookieStoreId = backgroundLogic.cookieStoreId(currentUserContextId);
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ const backgroundLogic = {
|
|||||||
url = undefined;
|
url = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't open these we just have to throw them away
|
if (!this.isPermissibleURL(url)) {
|
||||||
if (new URL(url).protocol === "about:") {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +75,17 @@ const backgroundLogic = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isPermissibleURL(url) {
|
||||||
|
const protocol = new URL(url).protocol;
|
||||||
|
// We can't open these we just have to throw them away
|
||||||
|
if (protocol === "about:"
|
||||||
|
|| protocol === "chrome:"
|
||||||
|
|| protocol === "moz-extension:") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
checkArgs(requiredArguments, options, methodName) {
|
checkArgs(requiredArguments, options, methodName) {
|
||||||
requiredArguments.forEach((argument) => {
|
requiredArguments.forEach((argument) => {
|
||||||
if (!(argument in options)) {
|
if (!(argument in options)) {
|
||||||
@@ -118,20 +128,37 @@ const backgroundLogic = {
|
|||||||
containerState.hiddenTabs.length === 0) {
|
containerState.hiddenTabs.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newWindowObj = await browser.windows.create({
|
let newWindowObj;
|
||||||
tabId: list.shift().id
|
let hiddenDefaultTabToClose;
|
||||||
});
|
if (list.length) {
|
||||||
browser.tabs.move(list.map((tab) => tab.id), {
|
newWindowObj = await browser.windows.create({
|
||||||
windowId: newWindowObj.id,
|
tabId: list.shift().id
|
||||||
index: -1
|
});
|
||||||
});
|
browser.tabs.move(list.map((tab) => tab.id), {
|
||||||
|
windowId: newWindowObj.id,
|
||||||
|
index: -1
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//As we get a blank tab here we will need to await the tabs creation
|
||||||
|
newWindowObj = await browser.windows.create({
|
||||||
|
});
|
||||||
|
hiddenDefaultTabToClose = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showHiddenPromises = [];
|
||||||
|
|
||||||
// Let's show the hidden tabs.
|
// Let's show the hidden tabs.
|
||||||
for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const
|
for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const
|
||||||
browser.tabs.create(object.url || DEFAULT_TAB, {
|
showHiddenPromises.push(browser.tabs.create({
|
||||||
|
url: object.url || DEFAULT_TAB,
|
||||||
windowId: newWindowObj.id,
|
windowId: newWindowObj.id,
|
||||||
cookieStoreId
|
cookieStoreId
|
||||||
});
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hiddenDefaultTabToClose) {
|
||||||
|
// Lets wait for hidden tabs to show before closing the others
|
||||||
|
await showHiddenPromises;
|
||||||
}
|
}
|
||||||
|
|
||||||
containerState.hiddenTabs = [];
|
containerState.hiddenTabs = [];
|
||||||
@@ -139,9 +166,9 @@ const backgroundLogic = {
|
|||||||
// Let's close all the normal tab in the new window. In theory it
|
// Let's close all the normal tab in the new window. In theory it
|
||||||
// should be only the first tab, but maybe there are addons doing
|
// should be only the first tab, but maybe there are addons doing
|
||||||
// crazy stuff.
|
// crazy stuff.
|
||||||
const tabs = browser.tabs.query({windowId: newWindowObj.id});
|
const tabs = await browser.tabs.query({windowId: newWindowObj.id});
|
||||||
for (let tab of tabs) { // eslint-disable-line prefer-const
|
for (let tab of tabs) { // eslint-disable-line prefer-const
|
||||||
if (tabs.cookieStoreId !== cookieStoreId) {
|
if (tab.cookieStoreId !== cookieStoreId) {
|
||||||
browser.tabs.remove(tab.id);
|
browser.tabs.remove(tab.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ const badge = {
|
|||||||
const currentWindow = await browser.windows.getCurrent();
|
const currentWindow = await browser.windows.getCurrent();
|
||||||
this.displayBrowserActionBadge(currentWindow.incognito);
|
this.displayBrowserActionBadge(currentWindow.incognito);
|
||||||
},
|
},
|
||||||
async displayBrowserActionBadge(disable) {
|
|
||||||
if (disable) {
|
disableAddon(tabId) {
|
||||||
browser.browserAction.disable();
|
browser.browserAction.disable(tabId);
|
||||||
} else {
|
browser.browserAction.setTitle({ tabId, title: "Containers disabled in Private Browsing Mode" });
|
||||||
browser.browserAction.enable();
|
},
|
||||||
}
|
|
||||||
|
async displayBrowserActionBadge() {
|
||||||
const extensionInfo = await backgroundLogic.getExtensionInfo();
|
const extensionInfo = await backgroundLogic.getExtensionInfo();
|
||||||
const storage = await browser.storage.local.get({browserActionBadgesClicked: []});
|
const storage = await browser.storage.local.get({browserActionBadgesClicked: []});
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ const identityState = {
|
|||||||
const tabsByContainer = await browser.tabs.query({cookieStoreId, windowId});
|
const tabsByContainer = await browser.tabs.query({cookieStoreId, windowId});
|
||||||
tabsByContainer.forEach((tab) => {
|
tabsByContainer.forEach((tab) => {
|
||||||
const tabObject = this._createTabObject(tab);
|
const tabObject = this._createTabObject(tab);
|
||||||
|
if (!backgroundLogic.isPermissibleURL(tab.url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// This tab is going to be closed. Let's mark this tabObject as
|
// This tab is going to be closed. Let's mark this tabObject as
|
||||||
// non-active.
|
// non-active.
|
||||||
tabObject.active = false;
|
tabObject.active = false;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
"js/background/badge.js",
|
"js/background/badge.js",
|
||||||
"js/background/identityState.js",
|
"js/background/identityState.js",
|
||||||
"js/background/messageHandler.js",
|
"js/background/messageHandler.js",
|
||||||
"js/backdround/init.js"
|
|
||||||
]
|
]
|
||||||
-->
|
-->
|
||||||
<script type="text/javascript" src="backgroundLogic.js"></script>
|
<script type="text/javascript" src="backgroundLogic.js"></script>
|
||||||
@@ -19,6 +18,5 @@
|
|||||||
<script type="text/javascript" src="badge.js"></script>
|
<script type="text/javascript" src="badge.js"></script>
|
||||||
<script type="text/javascript" src="identityState.js"></script>
|
<script type="text/javascript" src="identityState.js"></script>
|
||||||
<script type="text/javascript" src="messageHandler.js"></script>
|
<script type="text/javascript" src="messageHandler.js"></script>
|
||||||
<script type="text/javascript" src="init.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
browser.runtime.sendMessage({
|
|
||||||
method: "getPreference",
|
|
||||||
pref: "browser.privatebrowsing.autostart"
|
|
||||||
}).then(pbAutoStart => {
|
|
||||||
|
|
||||||
// We don't want to disable the addon if we are in auto private-browsing.
|
|
||||||
if (!pbAutoStart) {
|
|
||||||
browser.tabs.onCreated.addListener(tab => {
|
|
||||||
if (tab.incognito) {
|
|
||||||
disableAddon(tab.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.tabs.query({}).then(tabs => {
|
|
||||||
for (let tab of tabs) { // eslint-disable-line prefer-const
|
|
||||||
if (tab.incognito) {
|
|
||||||
disableAddon(tab.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
}).catch(() => {});
|
|
||||||
|
|
||||||
function disableAddon(tabId) {
|
|
||||||
browser.browserAction.disable(tabId);
|
|
||||||
browser.browserAction.setTitle({ tabId, title: "Containers disabled in Private Browsing Mode" });
|
|
||||||
}
|
|
||||||
@@ -39,7 +39,7 @@ const messageHandler = {
|
|||||||
backgroundLogic.sortTabs();
|
backgroundLogic.sortTabs();
|
||||||
break;
|
break;
|
||||||
case "showTabs":
|
case "showTabs":
|
||||||
backgroundLogic.showTabs({cookieStoreId: m.cookieStoreId});
|
this.unhideContainer(m.cookieStoreId);
|
||||||
break;
|
break;
|
||||||
case "hideTabs":
|
case "hideTabs":
|
||||||
backgroundLogic.hideTabs({
|
backgroundLogic.hideTabs({
|
||||||
@@ -106,6 +106,9 @@ const messageHandler = {
|
|||||||
}, {urls: ["<all_urls>"], types: ["main_frame"]});
|
}, {urls: ["<all_urls>"], types: ["main_frame"]});
|
||||||
|
|
||||||
browser.tabs.onCreated.addListener((tab) => {
|
browser.tabs.onCreated.addListener((tab) => {
|
||||||
|
if (tab.incognito) {
|
||||||
|
badge.disableAddon(tab.id);
|
||||||
|
}
|
||||||
// lets remember the last tab created so we can close it if it looks like a redirect
|
// lets remember the last tab created so we can close it if it looks like a redirect
|
||||||
this.lastCreatedTab = tab;
|
this.lastCreatedTab = tab;
|
||||||
if (tab.cookieStoreId) {
|
if (tab.cookieStoreId) {
|
||||||
@@ -130,12 +133,11 @@ const messageHandler = {
|
|||||||
|
|
||||||
async onFocusChangedCallback(windowId) {
|
async onFocusChangedCallback(windowId) {
|
||||||
assignManager.removeContextMenu();
|
assignManager.removeContextMenu();
|
||||||
const currentWindow = await browser.windows.getCurrent();
|
|
||||||
// browserAction loses background color in new windows ...
|
// browserAction loses background color in new windows ...
|
||||||
// https://bugzil.la/1314674
|
// https://bugzil.la/1314674
|
||||||
// https://github.com/mozilla/testpilot-containers/issues/608
|
// https://github.com/mozilla/testpilot-containers/issues/608
|
||||||
// ... so re-call displayBrowserActionBadge on window changes
|
// ... so re-call displayBrowserActionBadge on window changes
|
||||||
badge.displayBrowserActionBadge(currentWindow.incognito);
|
badge.displayBrowserActionBadge();
|
||||||
browser.tabs.query({active: true, windowId}).then((tabs) => {
|
browser.tabs.query({active: true, windowId}).then((tabs) => {
|
||||||
if (tabs && tabs[0]) {
|
if (tabs && tabs[0]) {
|
||||||
assignManager.calculateContextMenu(tabs[0]);
|
assignManager.calculateContextMenu(tabs[0]);
|
||||||
|
|||||||
@@ -20,11 +20,15 @@ async function doAnimation(element, property, value) {
|
|||||||
async function addMessage(message) {
|
async function addMessage(message) {
|
||||||
const divElement = document.createElement("div");
|
const divElement = document.createElement("div");
|
||||||
divElement.classList.add("container-notification");
|
divElement.classList.add("container-notification");
|
||||||
// For the eager eyed, this is an experiment. It is however likely that a website will know it is "contained" anyway
|
// Ideally we would use https://bugzilla.mozilla.org/show_bug.cgi?id=1340930 when this is available
|
||||||
divElement.innerText = message.text;
|
divElement.innerText = message.text;
|
||||||
|
|
||||||
const imageElement = document.createElement("img");
|
const imageElement = document.createElement("img");
|
||||||
imageElement.src = browser.extension.getURL("/img/container-site-d-24.png");
|
const imagePath = browser.extension.getURL("/img/container-site-d-24.png");
|
||||||
|
const response = await fetch(imagePath);
|
||||||
|
const blob = await response.blob();
|
||||||
|
const objectUrl = URL.createObjectURL(blob);
|
||||||
|
imageElement.src = objectUrl;
|
||||||
divElement.prepend(imageElement);
|
divElement.prepend(imageElement);
|
||||||
|
|
||||||
document.body.appendChild(divElement);
|
document.body.appendChild(divElement);
|
||||||
|
|||||||
@@ -93,18 +93,7 @@ const Logic = {
|
|||||||
const data = await browser.storage.local.get([ONBOARDING_STORAGE_KEY]);
|
const data = await browser.storage.local.get([ONBOARDING_STORAGE_KEY]);
|
||||||
let onboarded = data[ONBOARDING_STORAGE_KEY];
|
let onboarded = data[ONBOARDING_STORAGE_KEY];
|
||||||
if (!onboarded) {
|
if (!onboarded) {
|
||||||
// Legacy local storage used before panel 5
|
onboarded = 0;
|
||||||
if (localStorage.getItem("onboarded4")) {
|
|
||||||
onboarded = 4;
|
|
||||||
} else if (localStorage.getItem("onboarded3")) {
|
|
||||||
onboarded = 3;
|
|
||||||
} else if (localStorage.getItem("onboarded2")) {
|
|
||||||
onboarded = 2;
|
|
||||||
} else if (localStorage.getItem("onboarded1")) {
|
|
||||||
onboarded = 1;
|
|
||||||
} else {
|
|
||||||
onboarded = 0;
|
|
||||||
}
|
|
||||||
this.setOnboardingStage(onboarded);
|
this.setOnboardingStage(onboarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +133,11 @@ const Logic = {
|
|||||||
browser.browserAction.setBadgeBackgroundColor({color: ""});
|
browser.browserAction.setBadgeBackgroundColor({color: ""});
|
||||||
browser.browserAction.setBadgeText({text: ""});
|
browser.browserAction.setBadgeText({text: ""});
|
||||||
storage.browserActionBadgesClicked.push(extensionInfo.version);
|
storage.browserActionBadgesClicked.push(extensionInfo.version);
|
||||||
browser.storage.local.set({browserActionBadgesClicked: storage.browserActionBadgesClicked});
|
// use set and spread to create a unique array
|
||||||
|
const browserActionBadgesClicked = [...new Set(storage.browserActionBadgesClicked)];
|
||||||
|
browser.storage.local.set({
|
||||||
|
browserActionBadgesClicked
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async identity(cookieStoreId) {
|
async identity(cookieStoreId) {
|
||||||
@@ -168,6 +161,7 @@ const Logic = {
|
|||||||
});
|
});
|
||||||
element.addEventListener("keydown", (e) => {
|
element.addEventListener("keydown", (e) => {
|
||||||
if (e.keyCode === 13) {
|
if (e.keyCode === 13) {
|
||||||
|
e.preventDefault();
|
||||||
handler(e);
|
handler(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -802,7 +796,7 @@ Logic.registerPanel(P_CONTAINERS_EDIT, {
|
|||||||
</td>`;
|
</td>`;
|
||||||
tr.querySelector(".container-name").textContent = identity.name;
|
tr.querySelector(".container-name").textContent = identity.name;
|
||||||
tr.querySelector(".edit-container").setAttribute("title", `Edit ${identity.name} container`);
|
tr.querySelector(".edit-container").setAttribute("title", `Edit ${identity.name} container`);
|
||||||
tr.querySelector(".remove-container").setAttribute("title", `Delete ${identity.name} container`);
|
tr.querySelector(".remove-container").setAttribute("title", `Remove ${identity.name} container`);
|
||||||
|
|
||||||
|
|
||||||
Logic.addEnterHandler(tr, e => {
|
Logic.addEnterHandler(tr, e => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "Firefox Multi-Account Containers",
|
"name": "Firefox Multi-Account Containers",
|
||||||
"version": "4.0.1",
|
"version": "4.0.3",
|
||||||
|
|
||||||
"description": "Firefox Multi-Account Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.",
|
"description": "Multi-Account Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.",
|
||||||
"icons": {
|
"icons": {
|
||||||
"48": "img/container-site-d-48.png",
|
"48": "img/container-site-d-48.png",
|
||||||
"96": "img/container-site-d-96.png"
|
"96": "img/container-site-d-96.png"
|
||||||
@@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
"applications": {
|
"applications": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"strict_min_version": "51.0",
|
"strict_min_version": "53.0"
|
||||||
"update_url": "https://testpilot.firefox.com/files/@testpilot-containers/updates.json"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -45,7 +44,7 @@
|
|||||||
"browser_action": {
|
"browser_action": {
|
||||||
"browser_style": true,
|
"browser_style": true,
|
||||||
"default_icon": "img/container-site.svg",
|
"default_icon": "img/container-site.svg",
|
||||||
"default_title": "Firefox Multi-Account Containers",
|
"default_title": "Multi-Account Containers",
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<title>Firefox Multi-Account Containers</title>
|
<title>Multi-Account Containers</title>
|
||||||
<link rel="stylesheet" href="/css/popup.css">
|
<link rel="stylesheet" href="/css/popup.css">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
Reference in New Issue
Block a user