Bug 1197475 - [webext] Add test for browser.storage.local, fix a few bugs in it (r=gabor)

This commit is contained in:
Bill McCloskey 2015-08-30 20:38:22 -07:00
parent 0c1c783bf4
commit cac9f0ed40
4 changed files with 208 additions and 4 deletions

View File

@ -2099,6 +2099,10 @@ SpecialPowersAPI.prototype = {
this._addMessageListener("SPExtensionMessage", listener);
return extension;
},
invalidateExtensionStorageCache: function() {
this.notifyObserversInParentProcess(null, "extension-invalidate-storage-cache", "");
},
};
this.SpecialPowersAPI = SpecialPowersAPI;

View File

@ -95,7 +95,13 @@ this.ExtensionStorage = {
remove(extensionId, items) {
return this.read(extensionId).then(extData => {
let changes = {};
for (let prop in items) {
if (Array.isArray(items)) {
for (let prop of items) {
changes[prop] = {oldValue: extData[prop]};
delete extData[prop];
}
} else {
let prop = items;
changes[prop] = {oldValue: extData[prop]};
delete extData[prop];
}
@ -116,7 +122,7 @@ this.ExtensionStorage = {
let result = {};
if (keys === null) {
Object.assign(result, extData);
} else if (typeof(keys) == "object") {
} else if (typeof(keys) == "object" && !Array.isArray(keys)) {
for (let prop in keys) {
if (prop in extData) {
result[prop] = extData[prop];
@ -125,10 +131,15 @@ this.ExtensionStorage = {
}
}
} else if (typeof(keys) == "string") {
result[prop] = extData[prop] || undefined;
let prop = keys;
if (prop in extData) {
result[prop] = extData[prop];
}
} else {
for (let prop of keys) {
result[prop] = extData[prop] || undefined;
if (prop in extData) {
result[prop] = extData[prop];
}
}
}
@ -146,4 +157,20 @@ this.ExtensionStorage = {
let listeners = this.listeners.get(extensionId);
listeners.delete(listener);
},
init() {
Services.obs.addObserver(this, "extension-invalidate-storage-cache", false);
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
observe(subject, topic, data) {
if (topic == "xpcom-shutdown") {
Services.obs.removeObserver(this, "extension-invalidate-storage-cache");
Services.obs.removeObserver(this, "xpcom-shutdown");
} else if (topic == "extension-invalidate-storage-cache") {
this.cache.clear();
}
},
};
ExtensionStorage.init();

View File

@ -22,3 +22,4 @@ support-files =
[test_ext_webrequest.html]
[test_ext_generate.html]
[test_ext_sandbox_var.html]
[test_ext_storage.html]

View File

@ -0,0 +1,172 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</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="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
function backgroundScript() {
function set(items) {
return new Promise(resolve => {
browser.storage.local.set(items, resolve);
});
}
function get(items) {
return new Promise(resolve => {
browser.storage.local.get(items, resolve);
});
}
function remove(items) {
return new Promise(resolve => {
browser.storage.local.remove(items, resolve);
});
}
function check(prop, value) {
return get(null).then(data => {
browser.test.assertEq(data[prop], value, "null getter worked for " + prop);
return get(prop);
}).then(data => {
browser.test.assertEq(data[prop], value, "string getter worked for " + prop);
return get([prop]);
}).then(data => {
browser.test.assertEq(data[prop], value, "array getter worked for " + prop);
return get({[prop]: undefined});
}).then(data => {
browser.test.assertEq(data[prop], value, "object getter worked for " + prop);
});
}
var globalChanges = {};
browser.storage.onChanged.addListener((changes, storage) => {
browser.test.assertEq(storage, "local", "storage is local");
Object.assign(globalChanges, changes);
});
function checkChanges(changes) {
function checkSub(obj1, obj2) {
for (var prop in obj1) {
browser.test.assertEq(obj1[prop].oldValue, obj2[prop].oldValue);
browser.test.assertEq(obj1[prop].newValue, obj2[prop].newValue);
}
}
checkSub(changes, globalChanges);
checkSub(globalChanges, changes);
globalChanges = {};
}
// Set some data and then test getters.
set({"test-prop1": "value1", "test-prop2": "value2"}).then(() => {
checkChanges({"test-prop1": {newValue: "value1"}, "test-prop2": {newValue: "value2"}});
return check("test-prop1", "value1");
}).then(() => {
return check("test-prop2", "value2");
}).then(() => {
return get({"test-prop1": undefined, "test-prop2": undefined, "other": "default"});
}).then(data => {
browser.test.assertEq(data["test-prop1"], "value1", "prop1 correct");
browser.test.assertEq(data["test-prop2"], "value2", "prop2 correct");
browser.test.assertEq(data["other"], "default", "other correct");
return get(["test-prop1", "test-prop2", "other"]);
}).then(data => {
browser.test.assertEq(data["test-prop1"], "value1", "prop1 correct");
browser.test.assertEq(data["test-prop2"], "value2", "prop2 correct");
browser.test.assertFalse("other" in data, "other correct");
// Remove data in various ways.
}).then(() => {
return remove("test-prop1");
}).then(() => {
checkChanges({"test-prop1": {oldValue: "value1"}});
return get(["test-prop1", "test-prop2"]);
}).then(data => {
browser.test.assertFalse("test-prop1" in data, "prop1 absent");
browser.test.assertTrue("test-prop2" in data, "prop2 present");
return set({"test-prop1": "value1"});
}).then(() => {
checkChanges({"test-prop1": {newValue: "value1"}});
return get(["test-prop1", "test-prop2"]);
}).then(data => {
browser.test.assertEq(data["test-prop1"], "value1", "prop1 correct");
browser.test.assertEq(data["test-prop2"], "value2", "prop2 correct");
}).then(() => {
return remove(["test-prop1", "test-prop2"]);
}).then(() => {
checkChanges({"test-prop1": {oldValue: "value1"}, "test-prop2": {oldValue: "value2"}});
return get(["test-prop1", "test-prop2"]);
}).then(data => {
browser.test.assertFalse("test-prop1" in data, "prop1 absent");
browser.test.assertFalse("test-prop2" in data, "prop2 absent");
// Test cache invalidation.
}).then(() => {
return set({"test-prop1": "value1", "test-prop2": "value2"});
}).then(() => {
globalChanges = {};
browser.test.sendMessage("invalidate");
return new Promise(resolve => browser.test.onMessage.addListener(resolve));
}).then(() => {
return check("test-prop1", "value1");
}).then(() => {
return check("test-prop2", "value2");
// Make sure we can store complex JSON data.
}).then(() => {
return set({"test-prop1": {str: "hello", bool: true, undef: undefined, obj: {}, arr: [1, 2]}});
}).then(() => {
browser.test.assertEq(globalChanges["test-prop1"].oldValue, "value1", "oldValue correct");
browser.test.assertEq(typeof(globalChanges["test-prop1"].newValue), "object", "newValue is obj");
globalChanges = {};
return get({"test-prop1": undefined});
}).then(data => {
var obj = data["test-prop1"];
browser.test.assertEq(obj.str, "hello", "string part correct");
browser.test.assertEq(obj.bool, true, "bool part correct");
browser.test.assertEq(obj.undef, undefined, "undefined part correct");
browser.test.assertEq(typeof(obj.obj), "object", "object part correct");
browser.test.assertTrue(Array.isArray(obj.arr), "array part present");
browser.test.assertEq(obj.arr[0], 1, "arr[0] part correct");
browser.test.assertEq(obj.arr[1], 2, "arr[1] part correct");
browser.test.assertEq(obj.arr.length, 2, "arr.length part correct");
}).then(() => {
browser.test.notifyPass("storage");
});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
permissions: ["storage"]
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
yield extension.awaitMessage("invalidate");
SpecialPowers.invalidateExtensionStorageCache();
extension.sendMessage("invalidated");
yield extension.awaitFinish("storage");
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>