Merge pull request #280 from mozilla/master
Merge 1.0 into production branch to publish signed .xpi
This commit is contained in:
commit
55dea83cfd
8 changed files with 144 additions and 36 deletions
|
@ -105,16 +105,23 @@ value, or chrome url path as an alternate selector mitiages this bug.*/
|
||||||
#userContext-indicator {
|
#userContext-indicator {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
list-style-image: none !important;
|
list-style-image: none !important;
|
||||||
|
vertical-align: middle;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#userContext-label {
|
#userContext-label {
|
||||||
margin-inline-end: 5px;
|
|
||||||
color: var(--identity-tab-color) !important;
|
color: var(--identity-tab-color) !important;
|
||||||
|
margin-inline-end: 5px;
|
||||||
|
max-inline-size: 75px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#userContext-icons {
|
#userContext-icons {
|
||||||
-moz-box-align: center;
|
-moz-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
max-inline-size: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.userContext-icon,
|
.userContext-icon,
|
||||||
|
|
|
@ -169,6 +169,15 @@ of a `testpilottest` telemetry ping for each scenario.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* When a user encounters the disabled "move" feature because of incompatible add-ons
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
"uuid": <uuid>,
|
||||||
|
"event": "incompatible-addons-detected"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### A Redshift schema for the payload:
|
### A Redshift schema for the payload:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|
66
index.js
66
index.js
|
@ -8,6 +8,12 @@ const DEFAULT_TAB = "about:newtab";
|
||||||
const SHOW_MENU_TIMEOUT = 100;
|
const SHOW_MENU_TIMEOUT = 100;
|
||||||
const HIDE_MENU_TIMEOUT = 300;
|
const HIDE_MENU_TIMEOUT = 300;
|
||||||
|
|
||||||
|
const INCOMPATIBLE_ADDON_IDS = [
|
||||||
|
"pulse@mozilla.com",
|
||||||
|
"snoozetabs@mozilla.com",
|
||||||
|
"jid1-NeEaf3sAHdKHPA@jetpack" // PageShot
|
||||||
|
];
|
||||||
|
|
||||||
const IDENTITY_COLORS = [
|
const IDENTITY_COLORS = [
|
||||||
{ name: "blue", color: "#00a7e0" },
|
{ name: "blue", color: "#00a7e0" },
|
||||||
{ name: "turquoise", color: "#01bdad" },
|
{ name: "turquoise", color: "#01bdad" },
|
||||||
|
@ -40,6 +46,7 @@ const PREFS = [
|
||||||
[ "privacy.usercontext.about_newtab_segregation.enabled", true ],
|
[ "privacy.usercontext.about_newtab_segregation.enabled", true ],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
|
||||||
const { attachTo, detachFrom } = require("sdk/content/mod");
|
const { attachTo, detachFrom } = require("sdk/content/mod");
|
||||||
const { Cu } = require("chrome");
|
const { Cu } = require("chrome");
|
||||||
const { ContextualIdentityService } = require("resource://gre/modules/ContextualIdentityService.jsm");
|
const { ContextualIdentityService } = require("resource://gre/modules/ContextualIdentityService.jsm");
|
||||||
|
@ -105,6 +112,7 @@ const ContextualIdentityProxy = {
|
||||||
const ContainerService = {
|
const ContainerService = {
|
||||||
_identitiesState: {},
|
_identitiesState: {},
|
||||||
_windowMap: new Map(),
|
_windowMap: new Map(),
|
||||||
|
_containerWasEnabled: false,
|
||||||
|
|
||||||
init(installation) {
|
init(installation) {
|
||||||
// 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
|
||||||
|
@ -139,7 +147,22 @@ const ContainerService = {
|
||||||
identity.color);
|
identity.color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let's create the default containers in case there are none.
|
||||||
|
if (prefService.get("privacy.userContext.enabled") !== true &&
|
||||||
|
ss.storage.savedConfiguration.preInstalledIdentities.length === 0) {
|
||||||
|
// Note: we have to create them in this way because there is no way to
|
||||||
|
// reuse the same ID and the localized strings.
|
||||||
|
ContextualIdentityService.create("Personal", "fingerprint", "blue");
|
||||||
|
ContextualIdentityService.create("Work", "briefcase", "orange");
|
||||||
|
ContextualIdentityService.create("Finance", "dollar", "green");
|
||||||
|
ContextualIdentityService.create("Shopping", "cart", "pink");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's see if containers were enabled before this addon.
|
||||||
|
this._containerWasEnabled =
|
||||||
|
ss.storage.savedConfiguration.prefs["privacy.userContext.enabled"];
|
||||||
|
|
||||||
// Enabling preferences
|
// Enabling preferences
|
||||||
|
|
||||||
|
@ -170,7 +193,8 @@ const ContainerService = {
|
||||||
"removeIdentity",
|
"removeIdentity",
|
||||||
"updateIdentity",
|
"updateIdentity",
|
||||||
"getPreference",
|
"getPreference",
|
||||||
"sendTelemetryPayload"
|
"sendTelemetryPayload",
|
||||||
|
"checkIncompatibleAddons"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Map of identities.
|
// Map of identities.
|
||||||
|
@ -477,6 +501,21 @@ const ContainerService = {
|
||||||
this._sendEvent(payload);
|
this._sendEvent(payload);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
checkIncompatibleAddons() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
AddonManager.getAddonsByIDs(INCOMPATIBLE_ADDON_IDS, (addons) => {
|
||||||
|
addons = addons.filter((a) => a && a.isActive);
|
||||||
|
const incompatibleAddons = addons.length !== 0;
|
||||||
|
if (incompatibleAddons) {
|
||||||
|
this.sendTelemetryPayload({
|
||||||
|
"event": "incompatible-addons-detected"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve(incompatibleAddons);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// Tabs management
|
// Tabs management
|
||||||
|
|
||||||
hideTabs(args) {
|
hideTabs(args) {
|
||||||
|
@ -673,6 +712,11 @@ const ContainerService = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._remapTabsIfMissing(args.userContextId);
|
||||||
|
if (!this._isKnownContainer(args.userContextId)) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
this.sendTelemetryPayload({
|
this.sendTelemetryPayload({
|
||||||
"event": "move-tabs-to-window",
|
"event": "move-tabs-to-window",
|
||||||
"userContextId": args.userContextId,
|
"userContextId": args.userContextId,
|
||||||
|
@ -686,7 +730,8 @@ const ContainerService = {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
if (list.length === 0) {
|
if (list.length === 0 &&
|
||||||
|
this._identitiesState[args.userContextId].hiddenTabs.length === 0) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -702,6 +747,13 @@ const ContainerService = {
|
||||||
newBrowserWindow.gBrowser.adoptTab(viewFor(tab), pos++, false);
|
newBrowserWindow.gBrowser.adoptTab(viewFor(tab), pos++, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let's show the hidden tabs.
|
||||||
|
for (let object of this._identitiesState[args.userContextId].hiddenTabs) { // eslint-disable-line prefer-const
|
||||||
|
newBrowserWindow.gBrowser.addTab(object.url || DEFAULT_TAB, { userContextId: args.userContextId });
|
||||||
|
}
|
||||||
|
|
||||||
|
this._identitiesState[args.userContextId].hiddenTabs = [];
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -964,10 +1016,9 @@ const ContainerService = {
|
||||||
|
|
||||||
for (let window of windows.browserWindows) { // eslint-disable-line prefer-const
|
for (let window of windows.browserWindows) { // eslint-disable-line prefer-const
|
||||||
// Let's close all the container tabs.
|
// Let's close all the container tabs.
|
||||||
// Note 1: we don't care if containers are supported but the current FF
|
// Note: We cannot use _closeTabs() because at this point tab.window is
|
||||||
// version.
|
|
||||||
// Note 2: We cannot use _closeTabs() because at this point tab.window is
|
|
||||||
// null.
|
// null.
|
||||||
|
if (!this._containerWasEnabled) {
|
||||||
for (let tab of window.tabs) { // eslint-disable-line prefer-const
|
for (let tab of window.tabs) { // eslint-disable-line prefer-const
|
||||||
if (this._getUserContextIdFromTab(tab)) {
|
if (this._getUserContextIdFromTab(tab)) {
|
||||||
tab.close();
|
tab.close();
|
||||||
|
@ -976,6 +1027,7 @@ const ContainerService = {
|
||||||
} catch(e) {} // eslint-disable-line no-empty
|
} catch(e) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._getOrCreateContainerWindow(viewFor(window)).shutdown();
|
this._getOrCreateContainerWindow(viewFor(window)).shutdown();
|
||||||
}
|
}
|
||||||
|
@ -1101,6 +1153,7 @@ ContainerWindow.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_configurePlusButtonMenuElement(buttonElement) {
|
_configurePlusButtonMenuElement(buttonElement) {
|
||||||
|
if (buttonElement) {
|
||||||
// Let's remove the tooltip because it can go over our panel.
|
// Let's remove the tooltip because it can go over our panel.
|
||||||
this._tooltipCache.set(buttonElement, buttonElement.getAttribute("tooltip"));
|
this._tooltipCache.set(buttonElement, buttonElement.getAttribute("tooltip"));
|
||||||
buttonElement.setAttribute("tooltip", "");
|
buttonElement.setAttribute("tooltip", "");
|
||||||
|
@ -1109,6 +1162,7 @@ ContainerWindow.prototype = {
|
||||||
buttonElement.addEventListener("mouseover", this);
|
buttonElement.addEventListener("mouseover", this);
|
||||||
buttonElement.addEventListener("click", this);
|
buttonElement.addEventListener("click", this);
|
||||||
buttonElement.addEventListener("mouseout", this);
|
buttonElement.addEventListener("mouseout", this);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_configurePlusButtonMenu() {
|
_configurePlusButtonMenu() {
|
||||||
|
@ -1318,12 +1372,14 @@ ContainerWindow.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_shutDownPlusButtonMenuElement(buttonElement) {
|
_shutDownPlusButtonMenuElement(buttonElement) {
|
||||||
|
if (buttonElement) {
|
||||||
this._shutdownElement(buttonElement);
|
this._shutdownElement(buttonElement);
|
||||||
buttonElement.setAttribute("tooltip", this._tooltipCache.get(buttonElement));
|
buttonElement.setAttribute("tooltip", this._tooltipCache.get(buttonElement));
|
||||||
|
|
||||||
buttonElement.removeEventListener("mouseover", this);
|
buttonElement.removeEventListener("mouseover", this);
|
||||||
buttonElement.removeEventListener("click", this);
|
buttonElement.removeEventListener("click", this);
|
||||||
buttonElement.removeEventListener("mouseout", this);
|
buttonElement.removeEventListener("mouseout", this);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_shutdownPlusButtonMenu() {
|
_shutdownPlusButtonMenu() {
|
||||||
|
|
|
@ -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": "0.9.4",
|
"version": "1.0.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"
|
||||||
|
|
|
@ -416,11 +416,24 @@ span ~ .panel-header-text {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Container info list */
|
/* Container info list */
|
||||||
|
#container-info-name {
|
||||||
|
margin-inline-end: 0.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
#container-info-hideorshow {
|
#container-info-hideorshow {
|
||||||
margin-block-start: 4px;
|
margin-block-start: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-info-tab-row:not(.clickable) {
|
#container-info-movetabs-incompat {
|
||||||
|
font-size: 10px;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-info-tab-row:not(.clickable),
|
||||||
|
.select-row:not(.clickable) {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,26 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector("#container-info-movetabs").addEventListener("click", () => {
|
// Check if the user has incompatible add-ons installed
|
||||||
|
browser.runtime.sendMessage({
|
||||||
|
method: "checkIncompatibleAddons"
|
||||||
|
}).then(incompatible => {
|
||||||
|
const moveTabsEl = document.querySelector("#container-info-movetabs");
|
||||||
|
if (incompatible) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
const incompatEl = document.createElement("div");
|
||||||
|
|
||||||
|
moveTabsEl.classList.remove("clickable");
|
||||||
|
moveTabsEl.setAttribute("title", "Moving container tabs is incompatible with Pulse, PageShot, and SnoozeTabs.");
|
||||||
|
|
||||||
|
fragment.appendChild(incompatEl);
|
||||||
|
incompatEl.setAttribute("id", "container-info-movetabs-incompat");
|
||||||
|
incompatEl.innerText = "Incompatible with other Experiments.";
|
||||||
|
incompatEl.classList.add("container-info-tab-row");
|
||||||
|
|
||||||
|
moveTabsEl.parentNode.insertBefore(fragment, moveTabsEl.nextSibling);
|
||||||
|
} else {
|
||||||
|
moveTabsEl.addEventListener("click", () => {
|
||||||
return browser.runtime.sendMessage({
|
return browser.runtime.sendMessage({
|
||||||
method: "moveTabsToWindow",
|
method: "moveTabsToWindow",
|
||||||
userContextId: Logic.currentIdentity().userContextId,
|
userContextId: Logic.currentIdentity().userContextId,
|
||||||
|
@ -310,6 +329,10 @@ Logic.registerPanel(P_CONTAINER_INFO, {
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
throw new Error("Could not check for incompatible add-ons.");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// This method is called when the panel is shown.
|
// This method is called when the panel is shown.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "Containers Experiment",
|
"name": "Containers Experiment",
|
||||||
"version": "0.9.4",
|
"version": "1.0.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": {
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
<form id="edit-container-panel-form">
|
<form id="edit-container-panel-form">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Name</legend>
|
<legend>Name</legend>
|
||||||
<input type="text" name="container-name" id="edit-container-panel-name-input"/>
|
<input type="text" name="container-name" id="edit-container-panel-name-input" maxlength="25"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset id="edit-container-panel-choose-color">
|
<fieldset id="edit-container-panel-choose-color">
|
||||||
<legend>Choose a color</legend>
|
<legend>Choose a color</legend>
|
||||||
|
|
Loading…
Add table
Reference in a new issue