diff --git a/index.js b/index.js
index 7d96902..5898741 100644
--- a/index.js
+++ b/index.js
@@ -2,12 +2,6 @@ const webExtension = require('sdk/webextension');
const {ContextualIdentityService} = require('resource://gre/modules/ContextualIdentityService.jsm');
function handleWebExtensionMessage(message, sender, sendReply) {
- console.log(message);
- if (message === 'get-identities') {
- sendReply({
- content: {identities: ContextualIdentityService.getIdentities()}
- });
- }
}
webExtension.startup().then(api=> {
diff --git a/webextension-experiment/LICENSE b/webextension-experiment/LICENSE
new file mode 100644
index 0000000..f979cc3
--- /dev/null
+++ b/webextension-experiment/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2010 - 2016, Mozilla Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+* Neither the name of the Mozilla Corporation nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/webextension-experiment/README.md b/webextension-experiment/README.md
new file mode 100644
index 0000000..17794f1
--- /dev/null
+++ b/webextension-experiment/README.md
@@ -0,0 +1,7 @@
+
+## Webextension contextual identities API extension
+
+This project contains the implementation of the Firefox
+browser.contextualIdentites API.
+
+Based on: https://bugzilla.mozilla.org/show_bug.cgi?id=1322856
diff --git a/webextension-experiment/api.js b/webextension-experiment/api.js
new file mode 100644
index 0000000..0d17c69
--- /dev/null
+++ b/webextension-experiment/api.js
@@ -0,0 +1,118 @@
+"use strict";
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+const {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+const CONTAINER_STORE = "firefox-container-";
+
+XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
+ "resource://gre/modules/ContextualIdentityService.jsm");
+
+function convert(identity) {
+ let result = {
+ name: ContextualIdentityService.getUserContextLabel(identity.userContextId),
+ icon: identity.icon,
+ color: identity.color,
+ cookieStoreId: getCookieStoreIdForContainer(identity.userContextId),
+ };
+
+ return result;
+}
+
+function getCookieStoreIdForContainer(containerId) {
+ return CONTAINER_STORE + containerId;
+}
+
+class API extends ExtensionAPI {
+ getAPI(context) {
+ let self = {
+ contextualIdentities: {
+ get(cookieStoreId) {
+ let containerId = getContainerForCookieStoreId(cookieStoreId);
+ if (!containerId) {
+ return Promise.resolve(null);
+ }
+
+ let identity = ContextualIdentityService.getIdentityFromId(containerId);
+ return Promise.resolve(convert(identity));
+ },
+
+ query(details) {
+ let identities = [];
+ ContextualIdentityService.getIdentities().forEach(identity => {
+ if (details.name &&
+ ContextualIdentityService.getUserContextLabel(identity.userContextId) != details.name) {
+ return;
+ }
+
+ identities.push(convert(identity));
+ });
+
+ return Promise.resolve(identities);
+ },
+
+ create(details) {
+ let identity = ContextualIdentityService.create(details.name,
+ details.icon,
+ details.color);
+ return Promise.resolve(convert(identity));
+ },
+
+ update(cookieStoreId, details) {
+ let containerId = getContainerForCookieStoreId(cookieStoreId);
+ if (!containerId) {
+ return Promise.resolve(null);
+ }
+
+ let identity = ContextualIdentityService.getIdentityFromId(containerId);
+ if (!identity) {
+ return Promise.resolve(null);
+ }
+
+ if (details.name !== null) {
+ identity.name = details.name;
+ }
+
+ if (details.color !== null) {
+ identity.color = details.color;
+ }
+
+ if (details.icon !== null) {
+ identity.icon = details.icon;
+ }
+
+ if (!ContextualIdentityService.update(identity.userContextId,
+ identity.name, identity.icon,
+ identity.color)) {
+ return Promise.resolve(null);
+ }
+
+ return Promise.resolve(convert(identity));
+ },
+
+ remove(cookieStoreId) {
+ let containerId = getContainerForCookieStoreId(cookieStoreId);
+ if (!containerId) {
+ return Promise.resolve(null);
+ }
+
+ let identity = ContextualIdentityService.getIdentityFromId(containerId);
+ if (!identity) {
+ return Promise.resolve(null);
+ }
+
+ // We have to create the identity object before removing it.
+ let convertedIdentity = convert(identity);
+
+ if (!ContextualIdentityService.remove(identity.userContextId)) {
+ return Promise.resolve(null);
+ }
+
+ return Promise.resolve(convertedIdentity);
+ },
+ },
+ };
+
+ return self;
+ }
+}
diff --git a/webextension-experiment/install.rdf b/webextension-experiment/install.rdf
new file mode 100644
index 0000000..e1d4e0b
--- /dev/null
+++ b/webextension-experiment/install.rdf
@@ -0,0 +1,31 @@
+
+browser.contextualIdentities
API to query and modify contextual identity, also called as containers.",
+ "permissions": ["contextualidentities"],
+ "types": [
+ {
+ "id": "ContextualIdentity",
+ "type": "object",
+ "description": "Represents information about a contextual identity.",
+ "properties": {
+ "name": {"type": "string", "description": "The name of the contextual identity."},
+ "icon": {"type": "string", "description": "The icon of the contextual identity."},
+ "color": {"type": "string", "description": "The color of the contextual identity."},
+ "cookieStoreId": {"type": "string", "description": "The cookie store ID of the contextual identity."}
+ }
+ }
+ ],
+ "functions": [
+ {
+ "name": "get",
+ "type": "function",
+ "description": "Retrieves information about a single contextual identity.",
+ "async": true,
+ "parameters": [
+ {
+ "type": "string",
+ "name": "cookieStoreId",
+ "description": "The ID of the contextual identity cookie store. "
+ }
+ ]
+ },
+ {
+ "name": "query",
+ "type": "function",
+ "description": "Retrieves all contextual identities",
+ "async": true,
+ "parameters": [
+ {
+ "type": "object",
+ "name": "details",
+ "description": "Information to filter the contextual identities being retrieved.",
+ "properties": {
+ "name": {"type": "string", "optional": true, "description": "Filters the contextual identity by name."}
+ }
+ }
+ ]
+ },
+ {
+ "name": "create",
+ "type": "function",
+ "description": "Creates a contextual identity with the given data.",
+ "async": true,
+ "parameters": [
+ {
+ "type": "object",
+ "name": "details",
+ "description": "Details about the contextual identity being created.",
+ "properties": {
+ "name": {"type": "string", "optional": false, "description": "The name of the contextual identity." },
+ "color": {"type": "string", "optional": false, "description": "The color of the contextual identity." },
+ "icon": {"type": "string", "optional": false, "description": "The icon of the contextual identity." }
+ }
+ }
+ ]
+ },
+ {
+ "name": "update",
+ "type": "function",
+ "description": "Updates a contextual identity with the given data.",
+ "async": true,
+ "parameters": [
+ {
+ "type": "string",
+ "name": "cookieStoreId",
+ "description": "The ID of the contextual identity cookie store. "
+ },
+ {
+ "type": "object",
+ "name": "details",
+ "description": "Details about the contextual identity being created.",
+ "properties": {
+ "name": {"type": "string", "optional": true, "description": "The name of the contextual identity." },
+ "color": {"type": "string", "optional": true, "description": "The color of the contextual identity." },
+ "icon": {"type": "string", "optional": true, "description": "The icon of the contextual identity." }
+ }
+ }
+ ]
+ },
+ {
+ "name": "remove",
+ "type": "function",
+ "description": "Deletes a contetual identity by its cookie Store ID.",
+ "async": true,
+ "parameters": [
+ {
+ "type": "string",
+ "name": "cookieStoreId",
+ "description": "The ID of the contextual identity cookie store. "
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/webextension/js/popup.js b/webextension/js/popup.js
index 013dca1..f22da15 100644
--- a/webextension/js/popup.js
+++ b/webextension/js/popup.js
@@ -1,12 +1,5 @@
-const IDENTITY_L10NID_MATCH_INDEX = 1;
-
-browser.runtime.sendMessage('get-identities').then(reply=> {
- if (reply) {
- reply.content.identities.forEach(identity=> {
- const identityName = identity.l10nID.match(/userContext(\w*)\.label/)[IDENTITY_L10NID_MATCH_INDEX];
-
- document.querySelector('.identities-list').innerHTML += `