diff --git a/.eslintrc.js b/.eslintrc.js index 3876d96..2650c58 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,7 +11,8 @@ module.exports = { "globals": { "CustomizableUI": true, "CustomizableWidgets": true, - "SessionStore": true + "SessionStore": true, + "Services": true }, "plugins": [ "promise" diff --git a/.travis.yml b/.travis.yml index c2a2c34..4614306 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ language: node_js node_js: - "6.1" + +notifications: + irc: + - "ircs://irc.mozilla.org:6697/#testpilot-containers-bots" diff --git a/README.md b/README.md index 7c8b6a8..3fead42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Containers: Test Pilot Experiment -Soon to be [![Available on Test Pilot](https://img.shields.io/badge/available_on-Test_Pilot-0996F8.svg)](https://testpilot.firefox.com/) +[![Available on Test Pilot](https://img.shields.io/badge/available_on-Test_Pilot-0996F8.svg)](https://testpilot.firefox.com/experiments/containers) [Embedded Web Extension](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Embedded_WebExtensions) to experiment with [Containers](https://blog.mozilla.org/tanvi/2016/06/16/contextual-identities-on-the-web/) in [Firefox Test Pilot](https://testpilot.firefox.com/) to learn: diff --git a/index.js b/index.js index 1fa0e91..2b9a1fa 100644 --- a/index.js +++ b/index.js @@ -30,6 +30,7 @@ const IDENTITY_ICONS = [ { name: "briefcase", image: "chrome://browser/skin/usercontext/work.svg" }, { name: "dollar", image: "chrome://browser/skin/usercontext/banking.svg" }, { name: "cart", image: "chrome://browser/skin/usercontext/shopping.svg" }, + // All of these do not exist in gecko { name: "gift", image: "gift" }, { name: "vacation", image: "vacation" }, { name: "food", image: "food" }, @@ -37,7 +38,7 @@ const IDENTITY_ICONS = [ { name: "pet", image: "pet" }, { name: "tree", image: "tree" }, { name: "chill", image: "chill" }, - { name: "circle", image: "circle" }, // this doesn't exist in m-b + { name: "circle", image: "circle" }, ]; const PREFS = [ @@ -51,10 +52,12 @@ const { attachTo, detachFrom } = require("sdk/content/mod"); const { Cu } = require("chrome"); const { ContextualIdentityService } = require("resource://gre/modules/ContextualIdentityService.jsm"); const { getFavicon } = require("sdk/places/favicon"); +const { LightweightThemeManager } = Cu.import("resource://gre/modules/LightweightThemeManager.jsm", {}); const Metrics = require("./testpilot-metrics"); const { modelFor } = require("sdk/model/core"); const prefService = require("sdk/preferences/service"); const self = require("sdk/self"); +const { Services } = require("resource://gre/modules/Services.jsm"); const ss = require("sdk/simple-storage"); const { Style } = require("sdk/stylesheet/style"); const tabs = require("sdk/tabs"); @@ -68,6 +71,7 @@ const windowUtils = require("sdk/window/utils"); Cu.import("resource:///modules/CustomizableUI.jsm"); Cu.import("resource:///modules/CustomizableWidgets.jsm"); Cu.import("resource:///modules/sessionstore/SessionStore.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); // ---------------------------------------------------------------------------- // ContextualIdentityProxy @@ -113,6 +117,7 @@ const ContainerService = { _identitiesState: {}, _windowMap: new Map(), _containerWasEnabled: false, + _onThemeChangedCallback: null, init(installation) { // If we are just been installed, we must store some information for the @@ -194,6 +199,7 @@ const ContainerService = { "updateIdentity", "getPreference", "sendTelemetryPayload", + "getTheme", "checkIncompatibleAddons" ]; @@ -252,6 +258,8 @@ const ContainerService = { sendReply(this[message.method](message)); } }); + + this.registerThemeConnection(api); }).catch(() => { throw new Error("WebExtension startup failed. Unable to continue."); }); @@ -290,6 +298,51 @@ const ContainerService = { }; } // End-Of-Hack + + Services.obs.addObserver(this, "lightweight-theme-changed", false); + }, + + registerThemeConnection(api) { + // This is only used for theme notifications + api.browser.runtime.onConnect.addListener((port) => { + this.onThemeChanged((theme, topic) => { + port.postMessage({ + type: topic, + theme + }); + }); + }); + }, + + triggerThemeChanged(theme, topic) { + if (this._onThemeChangedCallback) { + this._onThemeChangedCallback(theme, topic); + } + }, + + observe(subject, topic) { + if (topic === "lightweight-theme-changed") { + this.getTheme().then((theme) => { + this.triggerThemeChanged(theme, topic); + }).catch(() => { + throw new Error("Unable to get theme"); + }); + } + }, + + getTheme() { + const defaultTheme = "firefox-compact-light@mozilla.org"; + return new Promise(function (resolve) { + let theme = defaultTheme; + if (LightweightThemeManager.currentTheme && LightweightThemeManager.currentTheme.id) { + theme = LightweightThemeManager.currentTheme.id; + } + resolve(theme); + }); + }, + + onThemeChanged(callback) { + this._onThemeChangedCallback = callback; }, // utility methods @@ -1076,6 +1129,10 @@ const ContainerService = { ContextualIdentityProxy.getIdentities().forEach(identity => { if (!preInstalledIdentities.includes(identity.userContextId)) { ContextualIdentityProxy.remove(identity.userContextId); + } else { + // Let's cleanup all the cookies for this container. + Services.obs.notifyObservers(null, "clear-origin-attributes-data", + JSON.stringify({ userContextId: identity.userContextId })); } }); diff --git a/package.json b/package.json index 4a0172a..94d011e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "testpilot-containers", "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.", - "version": "1.0.4", + "version": "1.1.0", "author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston", "bugs": { "url": "https://github.com/mozilla/testpilot-containers/issues" diff --git a/webextension/background.js b/webextension/background.js index 3875580..e909c6b 100644 --- a/webextension/background.js +++ b/webextension/background.js @@ -1,3 +1,50 @@ + +const themeManager = { + existingTheme: null, + init() { + this.check(); + + const port = browser.runtime.connect(); + port.onMessage.addListener(m => { + if (m.type === "lightweight-theme-changed") { + this.update(m.theme); + } + }); + }, + setPopupIcon(theme) { + let icons = { + 16: "img/container-site-d-24.png", + 32: "img/container-site-d-48.png" + }; + if (theme === "firefox-compact-dark@mozilla.org") { + icons = { + 16: "img/container-site-w-24.png", + 32: "img/container-site-w-48.png" + }; + } + browser.browserAction.setIcon({ + path: icons + }); + }, + check() { + browser.runtime.sendMessage({ + method: "getTheme" + }).then((theme) => { + this.update(theme); + }).catch(() => { + throw new Error("Unable to get theme"); + }); + }, + update(theme) { + if (this.existingTheme !== theme) { + this.setPopupIcon(theme); + this.existingTheme = theme; + } + } +}; + +themeManager.init(); + browser.runtime.sendMessage({ method: "getPreference", pref: "browser.privatebrowsing.autostart" diff --git a/webextension/js/popup.js b/webextension/js/popup.js index c75985c..80c852e 100644 --- a/webextension/js/popup.js +++ b/webextension/js/popup.js @@ -32,7 +32,8 @@ const Logic = { // Routing to the correct panel. .then(() => { - if (localStorage.getItem("onboarded3")) { + // If localStorage is disabled, we don't show the onboarding. + if (!localStorage || localStorage.getItem("onboarded3")) { this.showPanel(P_CONTAINERS_LIST); } else if (localStorage.getItem("onboarded2")) { this.showPanel(P_ONBOARDING_3); @@ -481,22 +482,28 @@ Logic.registerPanel(P_CONTAINER_EDIT, { Logic.showPreviousPanel(); }); - document.querySelector("#edit-container-ok-link").addEventListener("click", () => { - const identity = Logic.currentIdentity(); - const formValues = new FormData(document.getElementById("edit-container-panel-form")); - browser.runtime.sendMessage({ - method: identity.userContextId ? "updateIdentity" : "createIdentity", - userContextId: identity.userContextId || 0, - name: document.getElementById("edit-container-panel-name-input").value || Logic.generateIdentityName(), - icon: formValues.get("container-icon") || DEFAULT_ICON, - color: formValues.get("container-color") || DEFAULT_COLOR, - }).then(() => { - return Logic.refreshIdentities(); - }).then(() => { - Logic.showPreviousPanel(); - }).catch(() => { - Logic.showPanel(P_CONTAINERS_LIST); - }); + this._editForm = document.getElementById("edit-container-panel-form"); + const editLink = document.querySelector("#edit-container-ok-link"); + editLink.addEventListener("click", this._submitForm); + editLink.addEventListener("submit", this._submitForm); + this._editForm.addEventListener("submit", this._submitForm); + }, + + _submitForm() { + const identity = Logic.currentIdentity(); + const formValues = new FormData(this._editForm); + browser.runtime.sendMessage({ + method: identity.userContextId ? "updateIdentity" : "createIdentity", + userContextId: identity.userContextId || 0, + name: document.getElementById("edit-container-panel-name-input").value || Logic.generateIdentityName(), + icon: formValues.get("container-icon") || DEFAULT_ICON, + color: formValues.get("container-color") || DEFAULT_COLOR, + }).then(() => { + return Logic.refreshIdentities(); + }).then(() => { + Logic.showPreviousPanel(); + }).catch(() => { + Logic.showPanel(P_CONTAINERS_LIST); }); }, diff --git a/webextension/manifest.json b/webextension/manifest.json index f7e473a..3bb95da 100644 --- a/webextension/manifest.json +++ b/webextension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Containers Experiment", - "version": "1.0.4", + "version": "1.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.", "icons": {