Auto send users to sites they assigned to a container. Fixes #306
This commit is contained in:
parent
9e1da083ee
commit
3e657a2e8d
8 changed files with 383 additions and 28 deletions
30
index.js
30
index.js
|
@ -117,7 +117,7 @@ const ContainerService = {
|
||||||
_identitiesState: {},
|
_identitiesState: {},
|
||||||
_windowMap: new Map(),
|
_windowMap: new Map(),
|
||||||
_containerWasEnabled: false,
|
_containerWasEnabled: false,
|
||||||
_onThemeChangedCallback: null,
|
_onBackgroundConnectCallback: null,
|
||||||
|
|
||||||
init(installation, reason) {
|
init(installation, reason) {
|
||||||
// If we are just been installed, we must store some information for the
|
// If we are just been installed, we must store some information for the
|
||||||
|
@ -260,7 +260,7 @@ const ContainerService = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.registerThemeConnection(api);
|
this.registerBackgroundConnection(api);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
throw new Error("WebExtension startup failed. Unable to continue.");
|
throw new Error("WebExtension startup failed. Unable to continue.");
|
||||||
});
|
});
|
||||||
|
@ -307,28 +307,28 @@ const ContainerService = {
|
||||||
Services.obs.addObserver(this, "lightweight-theme-changed", false);
|
Services.obs.addObserver(this, "lightweight-theme-changed", false);
|
||||||
},
|
},
|
||||||
|
|
||||||
registerThemeConnection(api) {
|
registerBackgroundConnection(api) {
|
||||||
// This is only used for theme notifications
|
// This is only used for theme and container deletion notifications
|
||||||
api.browser.runtime.onConnect.addListener((port) => {
|
api.browser.runtime.onConnect.addListener((port) => {
|
||||||
this.onThemeChanged((theme, topic) => {
|
this._onBackgroundConnectCallback = (message, topic) => {
|
||||||
port.postMessage({
|
port.postMessage({
|
||||||
type: topic,
|
type: topic,
|
||||||
theme
|
message
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
triggerThemeChanged(theme, topic) {
|
triggerBackgroundCallback(message, topic) {
|
||||||
if (this._onThemeChangedCallback) {
|
if (this._onBackgroundConnectCallback) {
|
||||||
this._onThemeChangedCallback(theme, topic);
|
this._onBackgroundConnectCallback(message, topic);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
observe(subject, topic) {
|
observe(subject, topic) {
|
||||||
if (topic === "lightweight-theme-changed") {
|
if (topic === "lightweight-theme-changed") {
|
||||||
this.getTheme().then((theme) => {
|
this.getTheme().then((theme) => {
|
||||||
this.triggerThemeChanged(theme, topic);
|
this.triggerBackgroundCallback(theme, topic);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
throw new Error("Unable to get theme");
|
throw new Error("Unable to get theme");
|
||||||
});
|
});
|
||||||
|
@ -346,10 +346,6 @@ const ContainerService = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onThemeChanged(callback) {
|
|
||||||
this._onThemeChangedCallback = callback;
|
|
||||||
},
|
|
||||||
|
|
||||||
// utility methods
|
// utility methods
|
||||||
|
|
||||||
_containerTabCount(userContextId) {
|
_containerTabCount(userContextId) {
|
||||||
|
@ -960,12 +956,13 @@ const ContainerService = {
|
||||||
},
|
},
|
||||||
|
|
||||||
removeIdentity(args) {
|
removeIdentity(args) {
|
||||||
|
const eventName = "delete-container";
|
||||||
if (!("userContextId" in args)) {
|
if (!("userContextId" in args)) {
|
||||||
return Promise.reject("removeIdentity must be called with userContextId argument.");
|
return Promise.reject("removeIdentity must be called with userContextId argument.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sendTelemetryPayload({
|
this.sendTelemetryPayload({
|
||||||
"event": "delete-container",
|
"event": eventName,
|
||||||
"userContextId": args.userContextId
|
"userContextId": args.userContextId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -976,6 +973,7 @@ const ContainerService = {
|
||||||
|
|
||||||
return this._closeTabs(tabsToClose).then(() => {
|
return this._closeTabs(tabsToClose).then(() => {
|
||||||
const removed = ContextualIdentityProxy.remove(args.userContextId);
|
const removed = ContextualIdentityProxy.remove(args.userContextId);
|
||||||
|
this.triggerBackgroundCallback({userContextId: args.userContextId}, eventName);
|
||||||
this._forgetIdentity(args.userContextId);
|
this._forgetIdentity(args.userContextId);
|
||||||
return this._refreshNeeded().then(() => removed );
|
return this._refreshNeeded().then(() => removed );
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "testpilot-containers",
|
"name": "testpilot-containers",
|
||||||
"title": "Containers Experiment",
|
"title": "Containers Experiment",
|
||||||
"description": "Containers works by isolating cookie jars using separate origin-attributes defined visually by colored ‘Container Tabs’. This add-on is a modified version of the containers feature for Firefox Test Pilot.",
|
"description": "Containers works by isolating cookie jars using separate origin-attributes defined visually by colored ‘Container Tabs’. This add-on is a modified version of the containers feature for Firefox Test Pilot.",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"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,14 +1,266 @@
|
||||||
|
const assignManager = {
|
||||||
|
CLOSEABLE_WINDOWS: new Set([
|
||||||
|
"about:startpage",
|
||||||
|
"about:newtab",
|
||||||
|
"about:home",
|
||||||
|
"about:blank"
|
||||||
|
]),
|
||||||
|
MENU_ASSIGN_ID: "open-in-this-container",
|
||||||
|
MENU_REMOVE_ID: "remove-open-in-this-container",
|
||||||
|
storageArea: {
|
||||||
|
area: browser.storage.local,
|
||||||
|
|
||||||
|
getSiteStoreKey(pageUrl) {
|
||||||
|
const url = new window.URL(pageUrl);
|
||||||
|
const storagePrefix = "siteContainerMap@@_";
|
||||||
|
return `${storagePrefix}${url.hostname}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
get(pageUrl) {
|
||||||
|
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.area.get([siteStoreKey]).then((storageResponse) => {
|
||||||
|
if (storageResponse && siteStoreKey in storageResponse) {
|
||||||
|
resolve(storageResponse[siteStoreKey]);
|
||||||
|
}
|
||||||
|
resolve(null);
|
||||||
|
}).catch((e) => {
|
||||||
|
reject(e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
set(pageUrl, data) {
|
||||||
|
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||||
|
return this.area.set({
|
||||||
|
[siteStoreKey]: data
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
remove(pageUrl) {
|
||||||
|
const siteStoreKey = this.getSiteStoreKey(pageUrl);
|
||||||
|
return this.area.remove([siteStoreKey]);
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteContainer(userContextId) {
|
||||||
|
const removeKeys = [];
|
||||||
|
this.area.get().then((siteConfigs) => {
|
||||||
|
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)) {
|
||||||
|
removeKeys.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.area.remove(removeKeys);
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
init() {
|
||||||
|
browser.tabs.onActivated.addListener((info) => {
|
||||||
|
browser.tabs.get(info.tabId).then((tab) => {
|
||||||
|
this.calculateContextMenu(tab);
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.windows.onFocusChanged.addListener((windowId) => {
|
||||||
|
browser.tabs.query({active: true, windowId}).then((tabs) => {
|
||||||
|
if (tabs && tabs[0]) {
|
||||||
|
this.calculateContextMenu(tabs[0]);
|
||||||
|
}
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener((neverAskMessage) => {
|
||||||
|
const pageUrl = neverAskMessage.pageUrl;
|
||||||
|
if (neverAskMessage.neverAsk === true) {
|
||||||
|
// 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;
|
||||||
|
this.storageArea.set(pageUrl, siteSettings);
|
||||||
|
}
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
|
const userContextId = this.getUserContextIdFromCookieStore(tab);
|
||||||
|
// Mapping ${URL(info.pageUrl).hostname} to ${userContextId}
|
||||||
|
if (userContextId) {
|
||||||
|
let actionName;
|
||||||
|
let storageAction;
|
||||||
|
if (info.menuItemId === this.MENU_ASSIGN_ID) {
|
||||||
|
actionName = "added";
|
||||||
|
storageAction = this.storageArea.set(info.pageUrl, {
|
||||||
|
userContextId,
|
||||||
|
neverAsk: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
actionName = "removed";
|
||||||
|
storageAction = this.storageArea.remove(info.pageUrl);
|
||||||
|
}
|
||||||
|
storageAction.then(() => {
|
||||||
|
browser.notifications.create({
|
||||||
|
type: "basic",
|
||||||
|
title: "Containers",
|
||||||
|
message: `Successfully ${actionName} site to always open in this container`,
|
||||||
|
iconUrl: browser.extension.getURL("/img/onboarding-1.png")
|
||||||
|
});
|
||||||
|
this.calculateContextMenu(tab);
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.webRequest.onBeforeRequest.addListener((options) => {
|
||||||
|
if (options.frameId !== 0 || options.tabId === -1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadPageInContainer(options.url, siteSettings.userContextId, tab.index, siteSettings.neverAsk);
|
||||||
|
this.calculateContextMenu(tab);
|
||||||
|
// If the user just opened the tab, we can auto close it
|
||||||
|
if (this.CLOSEABLE_WINDOWS.has(tab.url)) {
|
||||||
|
browser.tabs.remove(tab.id);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
cancel: true,
|
||||||
|
};
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
},{urls: ["<all_urls>"], types: ["main_frame"]}, ["blocking"]);
|
||||||
|
|
||||||
|
browser.webRequest.onCompleted.addListener((options) => {
|
||||||
|
if (options.frameId !== 0 || options.tabId === -1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
browser.tabs.get(options.tabId).then((tab) => {
|
||||||
|
this.calculateContextMenu(tab);
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
},{urls: ["<all_urls>"], types: ["main_frame"]});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
deleteContainer(userContextId) {
|
||||||
|
this.storageArea.deleteContainer(userContextId);
|
||||||
|
},
|
||||||
|
|
||||||
|
getUserContextIdFromCookieStore(tab) {
|
||||||
|
if (!("cookieStoreId" in tab)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const cookieStore = tab.cookieStoreId;
|
||||||
|
const container = cookieStore.replace("firefox-container-", "");
|
||||||
|
if (container !== cookieStore) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
isTabPermittedAssign(tab) {
|
||||||
|
// Ensure we are not an important about url
|
||||||
|
// Ensure we are not in incognito mode
|
||||||
|
if (this.CLOSEABLE_WINDOWS.has(tab.url)
|
||||||
|
|| tab.incognito) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
calculateContextMenu(tab) {
|
||||||
|
// There is a focus issue in this menu where if you change window with a context menu click
|
||||||
|
// you get the wrong menu display because of async
|
||||||
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1215376#c16
|
||||||
|
// We also can't change for always private mode
|
||||||
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1352102
|
||||||
|
const cookieStore = this.getUserContextIdFromCookieStore(tab);
|
||||||
|
browser.contextMenus.remove(this.MENU_ASSIGN_ID);
|
||||||
|
browser.contextMenus.remove(this.MENU_REMOVE_ID);
|
||||||
|
// Ensure we have a cookieStore to assign to
|
||||||
|
if (cookieStore
|
||||||
|
&& this.isTabPermittedAssign(tab)) {
|
||||||
|
this.storageArea.get(tab.url).then((siteSettings) => {
|
||||||
|
// ✓ This is to mitigate https://bugzilla.mozilla.org/show_bug.cgi?id=1351418
|
||||||
|
let prefix = " "; // Alignment of non breaking space, unknown why this requires so many spaces to align with the tick
|
||||||
|
let menuId = this.MENU_ASSIGN_ID;
|
||||||
|
if (siteSettings) {
|
||||||
|
prefix = "✓";
|
||||||
|
menuId = this.MENU_REMOVE_ID;
|
||||||
|
}
|
||||||
|
browser.contextMenus.create({
|
||||||
|
id: menuId,
|
||||||
|
title: `${prefix} Always Open in This Container`,
|
||||||
|
checked: true,
|
||||||
|
contexts: ["all"],
|
||||||
|
});
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reloadPageInContainer(url, userContextId, index, neverAsk = false) {
|
||||||
|
const loadPage = browser.extension.getURL("confirm-page.html");
|
||||||
|
// 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: `firefox-container-${userContextId}`, index});
|
||||||
|
} else {
|
||||||
|
const confirmUrl = `${loadPage}?url=${url}`;
|
||||||
|
browser.tabs.create({url: confirmUrl, cookieStoreId: `firefox-container-${userContextId}`, index}).then(() => {
|
||||||
|
// We don't want to sync this URL ever nor clutter the users history
|
||||||
|
browser.history.deleteUrl({url: confirmUrl});
|
||||||
|
}).catch((e) => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const messageHandler = {
|
||||||
|
init() {
|
||||||
|
const port = browser.runtime.connect();
|
||||||
|
port.onMessage.addListener(m => {
|
||||||
|
switch (m.type) {
|
||||||
|
case "lightweight-theme-changed":
|
||||||
|
themeManager.update(m.message);
|
||||||
|
break;
|
||||||
|
case "delete-container":
|
||||||
|
assignManager.deleteContainer(m.message.userContextId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unhandled message type: ${m.message}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const themeManager = {
|
const themeManager = {
|
||||||
existingTheme: null,
|
existingTheme: null,
|
||||||
init() {
|
init() {
|
||||||
this.check();
|
this.check();
|
||||||
|
|
||||||
const port = browser.runtime.connect();
|
|
||||||
port.onMessage.addListener(m => {
|
|
||||||
if (m.type === "lightweight-theme-changed") {
|
|
||||||
this.update(m.theme);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
setPopupIcon(theme) {
|
setPopupIcon(theme) {
|
||||||
let icons = {
|
let icons = {
|
||||||
|
@ -77,8 +329,11 @@ const tabPageCounter = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assignManager.init();
|
||||||
themeManager.init();
|
themeManager.init();
|
||||||
tabPageCounter.init();
|
tabPageCounter.init();
|
||||||
|
// Lets do this last as theme manager did a check before connecting before
|
||||||
|
messageHandler.init();
|
||||||
|
|
||||||
browser.runtime.sendMessage({
|
browser.runtime.sendMessage({
|
||||||
method: "getPreference",
|
method: "getPreference",
|
||||||
|
|
33
webextension/confirm-page.html
Normal file
33
webextension/confirm-page.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
<title>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 rel="stylesheet" href="/css/confirm-page.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div class="title">
|
||||||
|
<h1 class="title-text">Should we open this in your container?</h1>
|
||||||
|
</div>
|
||||||
|
<form id="redirect-form">
|
||||||
|
<p>
|
||||||
|
Looks like you requested:
|
||||||
|
</p>
|
||||||
|
<div id="redirect-url"></div>
|
||||||
|
<p>
|
||||||
|
You asked <dfn id="browser-name" title="Thanks for trying out Containers. Sorry we may have got your browser name wrong. #FxNightly" >Firefox</dfn> to always open <dfn id="redirect-site"></dfn> in <dfn>this</dfn> type of container. Would you like to proceed?<br />
|
||||||
|
</p>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<input id="never-ask" type="checkbox" /><label for="never-ask">Remember my decision for this site</label>
|
||||||
|
<br />
|
||||||
|
<div class="button-container">
|
||||||
|
<button id="confirm" class="button primary">Take me there</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="js/confirm-page.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
37
webextension/css/confirm-page.css
Normal file
37
webextension/css/confirm-page.css
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/* General Rules and Resets */
|
||||||
|
.title {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background: url(/img/onboarding-1.png) no-repeat;
|
||||||
|
background-position: -10px -15px;
|
||||||
|
background-size: 285px;
|
||||||
|
margin-inline-start: -285px;
|
||||||
|
padding-inline-start: 285px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1300px) {
|
||||||
|
main {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for a mid sized window we have enough for this but not our image */
|
||||||
|
.title {
|
||||||
|
background-image: url("chrome://global/skin/icons/info.svg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font: message-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#redirect-url,
|
||||||
|
#redirect-origin {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
|
@ -383,7 +383,7 @@ span ~ .panel-header-text {
|
||||||
.panel-footer {
|
.panel-footer {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #efefef;
|
background: #efefef;
|
||||||
block-size: 55px;
|
block-size: 54px;
|
||||||
border-block-end: 1px solid #d8d8d8;
|
border-block-end: 1px solid #d8d8d8;
|
||||||
color: #000;
|
color: #000;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
26
webextension/js/confirm-page.js
Normal file
26
webextension/js/confirm-page.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
const redirectUrl = new URL(window.location).searchParams.get("url");
|
||||||
|
document.getElementById("redirect-url").textContent = redirectUrl;
|
||||||
|
const redirectSite = new URL(redirectUrl).hostname;
|
||||||
|
document.getElementById("redirect-site").textContent = redirectSite;
|
||||||
|
|
||||||
|
document.getElementById("redirect-form").addEventListener("submit", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const neverAsk = document.getElementById("never-ask").checked;
|
||||||
|
// Sending neverAsk message to background to store for next time we see this process
|
||||||
|
if (neverAsk) {
|
||||||
|
browser.runtime.sendMessage({
|
||||||
|
neverAsk: true,
|
||||||
|
pageUrl: redirectUrl
|
||||||
|
}).then(() => {
|
||||||
|
redirect();
|
||||||
|
}).catch(() => {
|
||||||
|
// Can't really do much here user will have to click it again
|
||||||
|
});
|
||||||
|
}
|
||||||
|
redirect();
|
||||||
|
});
|
||||||
|
|
||||||
|
function redirect() {
|
||||||
|
const redirectUrl = document.getElementById("redirect-url").textContent;
|
||||||
|
document.location.replace(redirectUrl);
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "Containers Experiment",
|
"name": "Containers Experiment",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
|
|
||||||
"description": "Containers works by isolating cookie jars using separate origin-attributes defined visually by colored ‘Container Tabs’. This add-on is a modified version of the containers feature for Firefox Test Pilot.",
|
"description": "Containers works by isolating cookie jars using separate origin-attributes defined visually by colored ‘Container Tabs’. This add-on is a modified version of the containers feature for Firefox Test Pilot.",
|
||||||
"icons": {
|
"icons": {
|
||||||
|
@ -19,10 +19,16 @@
|
||||||
"homepage_url": "https://testpilot.firefox.com/",
|
"homepage_url": "https://testpilot.firefox.com/",
|
||||||
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
|
"<all_urls>",
|
||||||
|
"activeTab",
|
||||||
"cookies",
|
"cookies",
|
||||||
|
"contextMenus",
|
||||||
|
"history",
|
||||||
|
"notifications",
|
||||||
|
"storage",
|
||||||
"tabs",
|
"tabs",
|
||||||
"webRequest",
|
"webRequestBlocking",
|
||||||
"<all_urls>"
|
"webRequest"
|
||||||
],
|
],
|
||||||
|
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
|
|
Loading…
Add table
Reference in a new issue