Bug 1331618 - allow persistent indexedDB on unlimitedStorage permission. r=aswan

MozReview-Commit-ID: 6VYqywMgSoU

--HG--
extra : rebase_source : 39cd28f5085335e8e1eb6ff39a4f5af2c0d9c2c7
This commit is contained in:
Luca Greco 2017-06-16 18:26:50 +02:00
parent 93e3a7df66
commit c9af23586a
7 changed files with 300 additions and 3 deletions

View File

@ -111,6 +111,7 @@ webextPerms.description.privacy=Read and modify privacy settings
webextPerms.description.sessions=Access recently closed tabs
webextPerms.description.tabs=Access browser tabs
webextPerms.description.topSites=Access browsing history
webextPerms.description.unlimitedStorage=Store unlimited amount of client-side data
webextPerms.description.webNavigation=Access browser activity during navigation
webextPerms.hostDescription.allUrls=Access your data for all websites

View File

@ -282,6 +282,12 @@ var UninstallObserver = {
if (storage) {
storage.clear();
}
// Remove any permissions related to the unlimitedStorage permission
// if we are also removing all the data stored by the extension.
Services.perms.removeFromPrincipal(principal, "WebExtensions-unlimitedStorage");
Services.perms.removeFromPrincipal(principal, "indexedDB");
Services.perms.removeFromPrincipal(principal, "persistent-storage");
}
if (!this.leaveUuid) {
@ -996,6 +1002,31 @@ this.Extension = class extends ExtensionData {
return super.initLocale(locale);
}
initUnlimitedStoragePermission() {
const principal = this.principal;
// Check if the site permission has already been set for the extension by the WebExtensions
// internals (instead of being manually allowed by the user).
const hasSitePermission = Services.perms.testPermissionFromPrincipal(
principal, "WebExtensions-unlimitedStorage"
);
if (this.hasPermission("unlimitedStorage")) {
// Set the indexedDB permission and a custom "WebExtensions-unlimitedStorage" to remember
// that the permission hasn't been selected manually by the user.
Services.perms.addFromPrincipal(principal, "WebExtensions-unlimitedStorage",
Services.perms.ALLOW_ACTION);
Services.perms.addFromPrincipal(principal, "indexedDB", Services.perms.ALLOW_ACTION);
Services.perms.addFromPrincipal(principal, "persistent-storage", Services.perms.ALLOW_ACTION);
} else if (hasSitePermission) {
// Remove the indexedDB permission if it has been enabled using the
// unlimitedStorage WebExtensions permissions.
Services.perms.removeFromPrincipal(principal, "WebExtensions-unlimitedStorage");
Services.perms.removeFromPrincipal(principal, "indexedDB");
Services.perms.removeFromPrincipal(principal, "persistent-storage");
}
}
startup() {
this.startupPromise = this._startup();
return this.startupPromise;
@ -1058,6 +1089,8 @@ this.Extension = class extends ExtensionData {
this.policy.active = false;
this.policy = processScript.initExtension(this.serialize(), this);
this.initUnlimitedStoragePermission();
// The "startup" Management event sent on the extension instance itself
// is emitted just before the Management "startup" event,
// and it is used to run code that needs to be executed before

View File

@ -245,7 +245,8 @@
"type": "string",
"enum": [
"alarms",
"storage"
"storage",
"unlimitedStorage"
]
}
]

View File

@ -0,0 +1,34 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported checkSitePermissions */
const {Services, Cu} = SpecialPowers;
const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
function checkSitePermissions(uuid, expectedPermAction, assertMessage) {
if (!uuid) {
throw new Error("checkSitePermissions should not be called with an undefined uuid");
}
const baseURI = NetUtil.newURI(`moz-extension://${uuid}/`);
const principal = Services.scriptSecurityManager.createCodebasePrincipal(baseURI, {});
const sitePermissions = {
webextUnlimitedStorage: Services.perms.testPermissionFromPrincipal(
principal, "WebExtensions-unlimitedStorage"
),
indexedDB: Services.perms.testPermissionFromPrincipal(
principal, "indexedDB"
),
persistentStorage: Services.perms.testPermissionFromPrincipal(
principal, "WebExtensions-unlimitedStorage"
),
};
for (const [sitePermissionName, actualPermAction] of Object.entries(sitePermissions)) {
is(actualPermAction, expectedPermAction,
`The extension "${sitePermissionName}" SitePermission ${assertMessage} as expected`);
}
}

View File

@ -2,8 +2,9 @@
support-files =
chrome_cleanup_script.js
head.js
file_mixed.html
head_unlimitedStorage.js
head_webrequest.js
file_mixed.html
file_csp.html
file_csp.html^headers^
file_to_drawWindow.html
@ -102,6 +103,11 @@ scheme=https
[test_ext_background_teardown.html]
[test_ext_tab_teardown.html]
skip-if = os == 'android' # Bug 1258975 on android.
[test_ext_unlimitedStorage.html]
[test_ext_unlimitedStorage_legacy_persistent_indexedDB.html]
# IndexedDB persistent storage mode is not allowed on Fennec from a non-chrome privileged code
# (it has only been enabled for apps and privileged code). See Bug 1119462 for additional info.
skip-if = os == 'android'
[test_ext_unload_frame.html]
[test_ext_listener_proxies.html]
[test_ext_web_accessible_resources.html]
@ -120,4 +126,4 @@ skip-if = os == 'android' # Currently fails in emulator tests
[test_ext_webnavigation_filters.html]
[test_ext_window_postMessage.html]
[test_ext_subframes_privileges.html]
[test_ext_xhr_capabilities.html]
[test_ext_xhr_capabilities.html]

View File

@ -0,0 +1,140 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for simple WebExtension</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<script type="text/javascript" src="head_unlimitedStorage.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
add_task(async function test_background_storagePersist() {
await SpecialPowers.pushPrefEnv({
"set": [
["dom.storageManager.enabled", true],
// Enable the storageManager permission prompt.
["browser.storageManager.enabled", true],
["dom.storageManager.prompt.testing", false],
["dom.storageManager.prompt.testing.allow", false],
],
});
const EXTENSION_ID = "test-storagePersist@mozilla";
const extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
permissions: ["unlimitedStorage"],
applications: {
gecko: {
id: EXTENSION_ID,
},
},
},
background: async function() {
const PROMISE_RACE_TIMEOUT = 2000;
browser.test.sendMessage("extension-uuid", window.location.host);
const requestStoragePersist = async () => {
const persistAllowed = await navigator.storage.persist();
if (!persistAllowed) {
throw new Error("navigator.storage.persist() has been denied");
}
};
await Promise.race([
requestStoragePersist(),
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Timeout opening persistent db from background page"));
}, PROMISE_RACE_TIMEOUT);
}),
]).then(
() => {
browser.test.notifyPass("indexeddb-storagePersistent-unlimitedStorage-done");
},
(error) => {
browser.test.fail(`error while testing persistent IndexedDB storage: ${error}`);
browser.test.notifyFail("indexeddb-storagePersistent-unlimitedStorage-done");
}
);
},
});
await extension.startup();
const uuid = await extension.awaitMessage("extension-uuid");
await extension.awaitFinish("indexeddb-storagePersistent-unlimitedStorage-done");
await extension.unload();
checkSitePermissions(uuid, Services.perms.UNKNOWN_ACTION, "has been cleared");
});
add_task(async function test_unlimitedStorage_removed_on_update() {
const EXTENSION_ID = "test-unlimitedStorage-removed-on-update@mozilla";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["unlimitedStorage"],
applications: {
gecko: {
id: EXTENSION_ID,
},
},
},
background: async function() {
browser.test.sendMessage("extension-uuid", window.location.host);
},
});
await extension.startup();
const uuid = await extension.awaitMessage("extension-uuid");
checkSitePermissions(uuid, Services.perms.ALLOW_ACTION, "has been allowed");
await extension.unload();
// Simulate an update which do not require the unlimitedStorage permission.
let updatedExtension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {
gecko: {
id: EXTENSION_ID,
},
},
},
background: async function() {
browser.test.sendMessage("updated-extension-uuid", window.location.host);
},
});
updatedExtension.startup();
const updatedExtensionUUID = await updatedExtension.awaitMessage("updated-extension-uuid");
is(uuid, updatedExtensionUUID, "The updated extension has the same uuid");
checkSitePermissions(uuid, Services.perms.UNKNOWN_ACTION, "has been cleared");
await updatedExtension.unload();
});
</script>
</body>
</html>

View File

@ -0,0 +1,82 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for simple WebExtension</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<script type="text/javascript" src="head_unlimitedStorage.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
add_task(async function test_legacy_indexedDB_storagePersistent_unlimitedStorage() {
const EXTENSION_ID = "test-idbStoragePersistent@mozilla";
const extension = ExtensionTestUtils.loadExtension({
useAddonManager: "permanent",
manifest: {
permissions: ["unlimitedStorage"],
applications: {
gecko: {
id: EXTENSION_ID,
},
},
},
background: async function() {
const PROMISE_RACE_TIMEOUT = 2000;
browser.test.sendMessage("extension-uuid", window.location.host);
try {
await Promise.race([
new Promise((resolve, reject) => {
const dbReq = indexedDB.open("test-persistent-idb", {version: 1.0, storage: "persistent"});
dbReq.onerror = evt => {
reject(evt.target.error);
};
dbReq.onsuccess = () => {
resolve();
};
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Timeout opening persistent db from background page"));
}, PROMISE_RACE_TIMEOUT);
}),
]);
browser.test.notifyPass("indexeddb-storagePersistent-unlimitedStorage-done");
} catch (error) {
const loggedError = error instanceof DOMError ? error.message : error;
browser.test.fail(`error while testing persistent IndexedDB storage: ${loggedError}`);
browser.test.notifyFail("indexeddb-storagePersistent-unlimitedStorage-done");
}
},
});
await extension.startup();
const uuid = await extension.awaitMessage("extension-uuid");
await extension.awaitFinish("indexeddb-storagePersistent-unlimitedStorage-done");
await extension.unload();
checkSitePermissions(uuid, Services.perms.UNKNOWN_ACTION, "has been cleared");
});
</script>
</body>
</html>