Bug 1354602 - Enabling containers for container addons on startup. r?aswan r?baku draft
authorJonathan Kingston <jkt@mozilla.com>
Mon, 03 Jul 2017 17:19:34 -0700
changeset 618454 d8079f9f75526eff54ce49530ab00a2a2ca79a0e
parent 618027 2fba314d7de77ad8ab693a2ea0112c0cda5dd564
child 640076 38229ca8f2e2f84db0aa7374b933de55073a0e77
push id71340
push userjkingston@mozilla.com
push dateMon, 31 Jul 2017 14:36:56 +0000
reviewersaswan, baku
bugs1354602
milestone56.0a1
Bug 1354602 - Enabling containers for container addons on startup. r?aswan r?baku MozReview-Commit-ID: BXLyQz8CGDl
browser/components/preferences/in-content-new/main.js
browser/components/preferences/in-content/privacy.js
toolkit/components/contextualidentity/ContextualIdentityService.jsm
toolkit/components/extensions/ext-contextualIdentities.js
toolkit/components/extensions/ext-toolkit.js
toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
--- a/browser/components/preferences/in-content-new/main.js
+++ b/browser/components/preferences/in-content-new/main.js
@@ -943,17 +943,16 @@ var gMainPane = {
     let cancelButton = bundlePreferences.getString("disableContainersButton2");
 
     let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
                       (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
 
     let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
                                        okButton, cancelButton, null, null, {});
     if (rv == 0) {
-      ContextualIdentityService.closeContainerTabs();
       Services.prefs.setBoolPref("privacy.userContext.enabled", false);
       return;
     }
 
     checkbox.checked = true;
   },
 
   /**
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -82,17 +82,16 @@ var gPrivacyPane = {
     let checkbox = document.getElementById("browserContainersCheckbox");
     if (checkbox.checked) {
       Services.prefs.setBoolPref("privacy.userContext.enabled", true);
       return;
     }
 
     let count = ContextualIdentityService.countContainerTabs();
     if (count == 0) {
-      ContextualIdentityService.notifyAllContainersCleared();
       Services.prefs.setBoolPref("privacy.userContext.enabled", false);
       return;
     }
 
     let bundlePreferences = document.getElementById("bundlePreferences");
 
     let title = bundlePreferences.getString("disableContainersAlertTitle");
     let message = PluralForm.get(count, bundlePreferences.getString("disableContainersMsg"))
@@ -103,19 +102,16 @@ var gPrivacyPane = {
 
     let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
                       (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
 
     let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
                                        okButton, cancelButton, null, null, {});
     if (rv == 0) {
       Services.prefs.setBoolPref("privacy.userContext.enabled", false);
-      ContextualIdentityService.closeContainerTabs().then(() => {
-        ContextualIdentityService.notifyAllContainersCleared();
-      });
       return;
     }
 
     checkbox.checked = true;
   },
 
   /**
    * Sets up the UI for the number of days of history to keep, and updates the
--- a/toolkit/components/contextualidentity/ContextualIdentityService.jsm
+++ b/toolkit/components/contextualidentity/ContextualIdentityService.jsm
@@ -6,16 +6,17 @@ this.EXPORTED_SYMBOLS = ["ContextualIden
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const DEFAULT_TAB_COLOR = "#909090";
 const SAVE_DELAY_MS = 1500;
+const CONTEXTUAL_IDENTITY_ENABLED_PREF = "privacy.userContext.enabled";
 
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle("chrome://browser/locale/browser.properties");
 });
 
 XPCOMUtils.defineLazyGetter(this, "gTextDecoder", function() {
   return new TextDecoder();
 });
@@ -109,16 +110,28 @@ function _ContextualIdentityService(path
 
   _path: null,
   _dataReady: false,
 
   _saver: null,
 
   init(path) {
     this._path = path;
+
+    Services.prefs.addObserver(CONTEXTUAL_IDENTITY_ENABLED_PREF, this);
+  },
+
+  // observe() is only used to listen to container enabling pref
+  async observe() {
+    const contextualIdentitiesEnabled = Services.prefs.getBoolPref(CONTEXTUAL_IDENTITY_ENABLED_PREF);
+    if (!contextualIdentitiesEnabled) {
+      await this.closeContainerTabs();
+      this.notifyAllContainersCleared();
+      this.resetDefault();
+    }
   },
 
   load() {
     OS.File.read(this._path).then(bytes => {
       // If synchronous loading happened in the meantime, exit now.
       if (this._dataReady) {
         return;
       }
@@ -142,18 +155,23 @@ function _ContextualIdentityService(path
         this.loadError(error);
       }
     }, (error) => {
       this.loadError(error);
     });
   },
 
   resetDefault() {
-    this._identities = this._defaultIdentities;
-    this._lastUserContextId = this._defaultIdentities.length;
+    this._identities = [];
+    // Clone the array
+    this._defaultIdentities.forEach((identity) => {
+      this._identities.push(Object.assign({}, identity));
+    });
+    this._lastUserContextId = this._identities.length;
+    this._openedIdentities = new Set();
 
     this._dataReady = true;
 
     this.saveSoon();
   },
 
   loadError(error) {
     if (error != null &&
--- a/toolkit/components/extensions/ext-contextualIdentities.js
+++ b/toolkit/components/extensions/ext-contextualIdentities.js
@@ -1,81 +1,98 @@
 "use strict";
 
 // The ext-* files are imported into the same scopes.
 /* import-globals-from ext-toolkit.js */
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
                                   "resource://gre/modules/ContextualIdentityService.jsm");
-XPCOMUtils.defineLazyPreferenceGetter(this, "containersEnabled",
-                                      "privacy.userContext.enabled");
+
+Cu.import("resource://gre/modules/ExtensionPreferencesManager.jsm");
+
+const CONTAINER_PREF_INSTALL_DEFAULTS = {
+  "privacy.userContext.enabled": true,
+  "privacy.userContext.longPressBehavior": 2,
+  "privacy.userContext.ui.enabled": true,
+  "privacy.usercontext.about_newtab_segregation.enabled": true,
+};
+
+const CONTAINERS_ENABLED_SETTING_NAME = "privacy.containers";
 
 const convertIdentity = identity => {
   let result = {
     name: ContextualIdentityService.getUserContextLabel(identity.userContextId),
     icon: identity.icon,
     color: identity.color,
     cookieStoreId: getCookieStoreIdForContainer(identity.userContextId),
   };
 
   return result;
 };
 
+ExtensionPreferencesManager.addSetting(CONTAINERS_ENABLED_SETTING_NAME, {
+  prefNames: Object.keys(CONTAINER_PREF_INSTALL_DEFAULTS),
+
+  setCallback(value) {
+    if (value === true) {
+      return CONTAINER_PREF_INSTALL_DEFAULTS;
+    }
+
+    let prefs = {};
+    for (let pref of this.prefNames) {
+      prefs[pref] = undefined;
+    }
+    return prefs;
+  },
+});
+
 this.contextualIdentities = class extends ExtensionAPI {
+  onStartup() {
+    let {extension} = this;
+
+    if (extension.hasPermission("contextualIdentities")) {
+      ExtensionPreferencesManager.setSetting(extension, CONTAINERS_ENABLED_SETTING_NAME, true);
+    }
+  }
+
   getAPI(context) {
     let self = {
       contextualIdentities: {
         get(cookieStoreId) {
-          if (!containersEnabled) {
-            return Promise.resolve(false);
-          }
-
           let containerId = getContainerForCookieStoreId(cookieStoreId);
           if (!containerId) {
             return Promise.resolve(null);
           }
 
           let identity = ContextualIdentityService.getPublicIdentityFromId(containerId);
           return Promise.resolve(convertIdentity(identity));
         },
 
         query(details) {
-          if (!containersEnabled) {
-            return Promise.resolve(false);
-          }
-
           let identities = [];
           ContextualIdentityService.getPublicIdentities().forEach(identity => {
             if (details.name &&
                 ContextualIdentityService.getUserContextLabel(identity.userContextId) != details.name) {
               return;
             }
 
             identities.push(convertIdentity(identity));
           });
 
           return Promise.resolve(identities);
         },
 
         create(details) {
-          if (!containersEnabled) {
-            return Promise.resolve(false);
-          }
-
           let identity = ContextualIdentityService.create(details.name,
                                                           details.icon,
                                                           details.color);
           return Promise.resolve(convertIdentity(identity));
         },
 
         update(cookieStoreId, details) {
-          if (!containersEnabled) {
-            return Promise.resolve(false);
-          }
-
           let containerId = getContainerForCookieStoreId(cookieStoreId);
           if (!containerId) {
             return Promise.resolve(null);
           }
 
           let identity = ContextualIdentityService.getPublicIdentityFromId(containerId);
           if (!identity) {
             return Promise.resolve(null);
@@ -98,20 +115,16 @@ this.contextualIdentities = class extend
                                                 identity.color)) {
             return Promise.resolve(null);
           }
 
           return Promise.resolve(convertIdentity(identity));
         },
 
         remove(cookieStoreId) {
-          if (!containersEnabled) {
-            return Promise.resolve(false);
-          }
-
           let containerId = getContainerForCookieStoreId(cookieStoreId);
           if (!containerId) {
             return Promise.resolve(null);
           }
 
           let identity = ContextualIdentityService.getPublicIdentityFromId(containerId);
           if (!identity) {
             return Promise.resolve(null);
--- a/toolkit/components/extensions/ext-toolkit.js
+++ b/toolkit/components/extensions/ext-toolkit.js
@@ -103,16 +103,17 @@ extensions.registerModules({
     paths: [
       ["browserSettings"],
     ],
   },
   contextualIdentities: {
     url: "chrome://extensions/content/ext-contextualIdentities.js",
     schema: "chrome://extensions/content/schemas/contextual_identities.json",
     scopes: ["addon_parent"],
+    events: ["startup"],
     paths: [
       ["contextualIdentities"],
     ],
   },
   cookies: {
     url: "chrome://extensions/content/ext-cookies.js",
     schema: "chrome://extensions/content/schemas/cookies.json",
     scopes: ["addon_parent"],
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
@@ -1,73 +1,33 @@
 "use strict";
 
 do_get_profile();
 
 add_task(async function test_contextualIdentities_without_permissions() {
-  function backgroundScript() {
+  function background() {
     browser.test.assertTrue(!browser.contextualIdentities,
                             "contextualIdentities API is not available when the contextualIdentities permission is not required");
     browser.test.notifyPass("contextualIdentities_permission");
   }
 
   let extension = ExtensionTestUtils.loadExtension({
-    background: `(${backgroundScript})()`,
+    background,
     manifest: {
       permissions: [],
     },
   });
 
   await extension.startup();
   await extension.awaitFinish("contextualIdentities_permission");
   await extension.unload();
 });
 
-
-add_task(async function test_contextualIdentity_no_containers() {
-  async function backgroundScript() {
-    let ci = await browser.contextualIdentities.get("foobar");
-    browser.test.assertEq(false, ci, "No identity should be returned here");
-
-    ci = await browser.contextualIdentities.get("firefox-container-1");
-    browser.test.assertEq(false, ci, "We don't have any identity");
-
-    let cis = await browser.contextualIdentities.query({});
-    browser.test.assertEq(false, cis, "no containers, 0 containers");
-
-    ci = await browser.contextualIdentities.create({name: "foobar", color: "red", icon: "icon"});
-    browser.test.assertEq(false, ci, "We don't have any identity");
-
-    ci = await browser.contextualIdentities.update("firefox-container-1", {name: "barfoo", color: "blue", icon: "icon icon"});
-    browser.test.assertEq(false, ci, "We don't have any identity");
-
-    ci = await browser.contextualIdentities.remove("firefox-container-1");
-    browser.test.assertEq(false, ci, "We have an identity");
-
-    browser.test.notifyPass("contextualIdentities");
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${backgroundScript})()`,
-    manifest: {
-      permissions: ["contextualIdentities"],
-    },
-  });
-
-  Services.prefs.setBoolPref("privacy.userContext.enabled", false);
-
-  await extension.startup();
-  await extension.awaitFinish("contextualIdentities");
-  await extension.unload();
-
-  Services.prefs.clearUserPref("privacy.userContext.enabled");
-});
-
 add_task(async function test_contextualIdentity_with_permissions() {
-  async function backgroundScript() {
+  async function background() {
     let ci = await browser.contextualIdentities.get("foobar");
     browser.test.assertEq(null, ci, "No identity should be returned here");
 
     ci = await browser.contextualIdentities.get("firefox-container-1");
     browser.test.assertTrue(!!ci, "We have an identity");
     browser.test.assertTrue("name" in ci, "We have an identity.name");
     browser.test.assertTrue("color" in ci, "We have an identity.color");
     browser.test.assertTrue("icon" in ci, "We have an identity.icon");
@@ -118,23 +78,35 @@ add_task(async function test_contextualI
     browser.test.assertEq("icon icon", ci.icon, "identity.icon is correct");
 
     cis = await browser.contextualIdentities.query({});
     browser.test.assertEq(4, cis.length, "we are back to 4 identities");
 
     browser.test.notifyPass("contextualIdentities");
   }
 
+  Services.prefs.setBoolPref("privacy.userContext.enabled", false);
   let extension = ExtensionTestUtils.loadExtension({
-    background: `(${backgroundScript})()`,
+    background,
     manifest: {
       permissions: ["contextualIdentities"],
     },
   });
 
-  Services.prefs.setBoolPref("privacy.userContext.enabled", true);
-
   await extension.startup();
+  browser.test.assertTrue(Services.prefs.getBoolPref("privacy.userContext.enabled"), "Pref should now be enabled");
   await extension.awaitFinish("contextualIdentities");
   await extension.unload();
 
+  Services.prefs.setBoolPref("privacy.userContext.enabled", true);
+
+  let extension2 = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      permissions: ["contextualIdentities"],
+    },
+  });
+  await extension2.startup();
+  await extension2.awaitFinish("contextualIdentities");
+  await extension2.unload();
+
   Services.prefs.clearUserPref("privacy.userContext.enabled");
 });