This commit is contained in:
Phil Ringnalda 2016-12-17 20:13:12 -08:00
Родитель d611d76a94 983071b932
Коммит 961aa85243
13 изменённых файлов: 401 добавлений и 19 удалений

Просмотреть файл

@ -655,7 +655,7 @@ skip-if = (toolkit == 'android') # Android: Bug 775227
[test_innersize_scrollport.html]
[test_integer_attr_with_leading_zero.html]
[test_intersectionobservers.html]
skip-if = (os == "android") # Timing issues
skip-if = true # Track Bug 1320704
[test_link_prefetch.html]
skip-if = !e10s # Track Bug 1281415
[test_link_stylesheet.html]

Просмотреть файл

@ -615,9 +615,9 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"InstallTrigger",
// IMPORTANT: Do not change this list without review from a DOM peer!
"IntersectionObserver",
{name: "IntersectionObserver", disabled: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IntersectionObserverEntry",
{name: "IntersectionObserverEntry", disabled: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"KeyEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!

Просмотреть файл

@ -5556,4 +5556,3 @@ pref("prompts.authentication_dialog_abuse_limit", 3);
// To enable the DOM implementation, turn on "dom.storageManager.enabled"
pref("browser.storageManager.enabled", false);
pref("dom.IntersectionObserver.enabled", true);

Просмотреть файл

@ -16,7 +16,13 @@ module.exports = { // eslint-disable-line no-undef
"Extension": true,
"ExtensionManagement": true,
"extensions": true,
"getContainerForCookieStoreId": true,
"getCookieStoreIdForContainer": true,
"global": true,
"isContainerCookieStoreId": true,
"isDefaultCookieStoreId": true,
"isPrivateCookieStoreId": true,
"isValidCookieStoreId": true,
"NetUtil": true,
"openOptionsPage": true,
"require": false,

Просмотреть файл

@ -429,10 +429,21 @@ this.ExtensionData = class {
// Errors are handled by the type checks above.
}
let containersEnabled = true;
try {
containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
} catch (e) {
// If we fail here, we are in some xpcshell test.
}
let permissions = this.manifest.permissions || [];
let whitelist = [];
for (let perm of permissions) {
if (perm == "contextualIdentities" && !containersEnabled) {
continue;
}
this.permissions.add(perm);
let match = /^(\w+)(?:\.(\w+)(?:\.\w+)*)?$/.exec(perm);

Просмотреть файл

@ -0,0 +1,109 @@
"use strict";
const {interfaces: Ci, utils: Cu} = Components;
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;
}
extensions.registerSchemaAPI("contextualIdentities", "addon_parent", 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;
});

Просмотреть файл

@ -22,7 +22,7 @@ global.getCookieStoreIdForTab = function(data, tab) {
}
if (tab.userContextId) {
return CONTAINER_STORE + tab.userContextId;
return getCookieStoreIdForContainer(tab.userContextId);
}
return DEFAULT_STORE;
@ -40,8 +40,12 @@ global.isContainerCookieStoreId = function(storeId) {
return storeId !== null && storeId.startsWith(CONTAINER_STORE);
};
global.getCookieStoreIdForContainer = function(containerId) {
return CONTAINER_STORE + containerId;
};
global.getContainerForCookieStoreId = function(storeId) {
if (!global.isContainerCookieStoreId(storeId)) {
if (!isContainerCookieStoreId(storeId)) {
return null;
}
@ -54,9 +58,9 @@ global.getContainerForCookieStoreId = function(storeId) {
};
global.isValidCookieStoreId = function(storeId) {
return global.isDefaultCookieStoreId(storeId) ||
global.isPrivateCookieStoreId(storeId) ||
global.isContainerCookieStoreId(storeId);
return isDefaultCookieStoreId(storeId) ||
isPrivateCookieStoreId(storeId) ||
isContainerCookieStoreId(storeId);
};
function convert({cookie, isPrivate}) {
@ -76,7 +80,7 @@ function convert({cookie, isPrivate}) {
}
if (cookie.originAttributes.userContextId) {
result.storeId = CONTAINER_STORE + cookie.originAttributes.userContextId;
result.storeId = getCookieStoreIdForContainer(cookie.originAttributes.userContextId);
} else if (cookie.originAttributes.privateBrowsingId || isPrivate) {
result.storeId = PRIVATE_STORE;
} else {
@ -197,17 +201,17 @@ function* query(detailsIn, props, context) {
let userContextId = 0;
let isPrivate = context.incognito;
if (details.storeId) {
if (!global.isValidCookieStoreId(details.storeId)) {
if (!isValidCookieStoreId(details.storeId)) {
return;
}
if (global.isDefaultCookieStoreId(details.storeId)) {
if (isDefaultCookieStoreId(details.storeId)) {
isPrivate = false;
} else if (global.isPrivateCookieStoreId(details.storeId)) {
} else if (isPrivateCookieStoreId(details.storeId)) {
isPrivate = true;
} else if (global.isContainerCookieStoreId(details.storeId)) {
} else if (isContainerCookieStoreId(details.storeId)) {
isPrivate = false;
userContextId = global.getContainerForCookieStoreId(details.storeId);
userContextId = getContainerForCookieStoreId(details.storeId);
if (!userContextId) {
return;
}
@ -368,12 +372,12 @@ extensions.registerSchemaAPI("cookies", "addon_parent", context => {
let expiry = isSession ? Number.MAX_SAFE_INTEGER : details.expirationDate;
let isPrivate = context.incognito;
let userContextId = 0;
if (global.isDefaultCookieStoreId(details.storeId)) {
if (isDefaultCookieStoreId(details.storeId)) {
isPrivate = false;
} else if (global.isPrivateCookieStoreId(details.storeId)) {
} else if (isPrivateCookieStoreId(details.storeId)) {
isPrivate = true;
} else if (global.isContainerCookieStoreId(details.storeId)) {
let containerId = global.getContainerForCookieStoreId(details.storeId);
} else if (isContainerCookieStoreId(details.storeId)) {
let containerId = getContainerForCookieStoreId(details.storeId);
if (containerId === null) {
return Promise.reject({message: `Illegal storeId: ${details.storeId}`});
}

Просмотреть файл

@ -1,6 +1,7 @@
# scripts
category webextension-scripts alarms chrome://extensions/content/ext-alarms.js
category webextension-scripts backgroundPage chrome://extensions/content/ext-backgroundPage.js
category webextension-scripts contextualIdentities chrome://extensions/content/ext-contextualIdentities.js
category webextension-scripts cookies chrome://extensions/content/ext-cookies.js
category webextension-scripts downloads chrome://extensions/content/ext-downloads.js
category webextension-scripts management chrome://extensions/content/ext-management.js
@ -41,6 +42,7 @@ category webextension-scripts-addon storage chrome://extensions/content/ext-c-st
# schemas
category webextension-schemas alarms chrome://extensions/content/schemas/alarms.json
category webextension-schemas contextualIdentities chrome://extensions/content/schemas/contextual_identities.json
category webextension-schemas cookies chrome://extensions/content/schemas/cookies.json
category webextension-schemas downloads chrome://extensions/content/schemas/downloads.json
category webextension-schemas events chrome://extensions/content/schemas/events.json

Просмотреть файл

@ -7,6 +7,7 @@ toolkit.jar:
content/extensions/ext-alarms.js
content/extensions/ext-backgroundPage.js
content/extensions/ext-browser-content.js
content/extensions/ext-contextualIdentities.js
content/extensions/ext-cookies.js
content/extensions/ext-downloads.js
content/extensions/ext-management.js

Просмотреть файл

@ -0,0 +1,123 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
[
{
"namespace": "manifest",
"types": [
{
"$extend": "Permission",
"choices": [{
"type": "string",
"enum": [
"contextualIdentities"
]
}]
}
]
},
{
"namespace": "contextualIdentities",
"description": "Use the <code>browser.contextualIdentities</code> 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. "
}
]
}
]
}
]

Просмотреть файл

@ -5,6 +5,7 @@
toolkit.jar:
% content extensions %content/extensions/
content/extensions/schemas/alarms.json
content/extensions/schemas/contextual_identities.json
content/extensions/schemas/cookies.json
content/extensions/schemas/downloads.json
content/extensions/schemas/events.json

Просмотреть файл

@ -0,0 +1,124 @@
"use strict";
do_get_profile();
add_task(function* test_contextualIdentities_without_permissions() {
function backgroundScript() {
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})()`,
manifest: {
permissions: [],
},
});
yield extension.startup();
yield extension.awaitFinish("contextualIdentities_permission");
yield extension.unload();
});
add_task(function* test_contextualIdentity_no_containers() {
function backgroundScript() {
browser.test.assertTrue(!browser.contextualIdentities,
"contextualIdentities API is not available when the containers are disabled");
browser.test.notifyPass("contextualIdentities_pref");
}
let extension = ExtensionTestUtils.loadExtension({
background: `(${backgroundScript})()`,
manifest: {
permissions: ["contextualIdentities"],
},
});
Services.prefs.setBoolPref("privacy.userContext.enabled", false);
yield extension.startup();
yield extension.awaitFinish("contextualIdentities_pref");
yield extension.unload();
Services.prefs.clearUserPref("privacy.userContext.enabled");
});
add_task(function* test_contextualIdentity_with_permissions() {
async function backgroundScript() {
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");
browser.test.assertEq("Personal", ci.name, "identity.name is correct");
browser.test.assertEq("firefox-container-1", ci.cookieStoreId, "identity.cookieStoreId is correct");
let cis = await browser.contextualIdentities.query({});
browser.test.assertEq(4, cis.length, "by default we should have 4 containers");
cis = await browser.contextualIdentities.query({name: "Personal"});
browser.test.assertEq(1, cis.length, "by default we should have 1 container called Personal");
cis = await browser.contextualIdentities.query({name: "foobar"});
browser.test.assertEq(0, cis.length, "by default we should have 0 container called foobar");
ci = await browser.contextualIdentities.create({name: "foobar", color: "red", icon: "icon"});
browser.test.assertTrue(!!ci, "We have an identity");
browser.test.assertEq("foobar", ci.name, "identity.name is correct");
browser.test.assertEq("red", ci.color, "identity.color is correct");
browser.test.assertEq("icon", ci.icon, "identity.icon is correct");
browser.test.assertTrue(!!ci.cookieStoreId, "identity.cookieStoreId is correct");
ci = await browser.contextualIdentities.get(ci.cookieStoreId);
browser.test.assertTrue(!!ci, "We have an identity");
browser.test.assertEq("foobar", ci.name, "identity.name is correct");
browser.test.assertEq("red", ci.color, "identity.color is correct");
browser.test.assertEq("icon", ci.icon, "identity.icon is correct");
cis = await browser.contextualIdentities.query({});
browser.test.assertEq(5, cis.length, "now we have 5 identities");
ci = await browser.contextualIdentities.update(ci.cookieStoreId, {name: "barfoo", color: "blue", icon: "icon icon"});
browser.test.assertTrue(!!ci, "We have an identity");
browser.test.assertEq("barfoo", ci.name, "identity.name is correct");
browser.test.assertEq("blue", ci.color, "identity.color is correct");
browser.test.assertEq("icon icon", ci.icon, "identity.icon is correct");
ci = await browser.contextualIdentities.get(ci.cookieStoreId);
browser.test.assertTrue(!!ci, "We have an identity");
browser.test.assertEq("barfoo", ci.name, "identity.name is correct");
browser.test.assertEq("blue", ci.color, "identity.color is correct");
browser.test.assertEq("icon icon", ci.icon, "identity.icon is correct");
ci = await browser.contextualIdentities.remove(ci.cookieStoreId);
browser.test.assertTrue(!!ci, "We have an identity");
browser.test.assertEq("barfoo", ci.name, "identity.name is correct");
browser.test.assertEq("blue", ci.color, "identity.color is correct");
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");
}
let extension = ExtensionTestUtils.loadExtension({
background: `(${backgroundScript})()`,
manifest: {
permissions: ["contextualIdentities"],
},
});
Services.prefs.setBoolPref("privacy.userContext.enabled", true);
yield extension.startup();
yield extension.awaitFinish("contextualIdentities");
yield extension.unload();
Services.prefs.clearUserPref("privacy.userContext.enabled");
});

Просмотреть файл

@ -25,6 +25,8 @@ skip-if = os == "android" # Android does not use Places for history.
[test_ext_background_window_properties.js]
skip-if = os == "android"
[test_ext_contexts.js]
[test_ext_contextual_identities.js]
skip-if = os == "android" # Containers are not exposed to android.
[test_ext_downloads.js]
[test_ext_downloads_download.js]
skip-if = os == "android"