Bug 821814 - Settings: upgrade settings DB after settings.json changed. r=bent

This commit is contained in:
Gregor Wagner 2013-01-04 16:04:28 -08:00
parent ca96b0b3fd
commit b18e38dae1
3 changed files with 195 additions and 78 deletions

View File

@ -2,19 +2,21 @@
* 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/. */
"use strict";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
this.EXPORTED_SYMBOLS = ["SettingsDB", "SETTINGSDB_NAME", "SETTINGSSTORE_NAME"];
const DEBUG = false;
const DEBUG = true;
function debug(s) {
if (DEBUG) dump("-*- SettingsDB: " + s + "\n");
}
this.SETTINGSDB_NAME = "settings";
this.SETTINGSDB_VERSION = 1;
this.SETTINGSDB_VERSION = 2;
this.SETTINGSSTORE_NAME = "settings";
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
@ -28,13 +30,17 @@ SettingsDB.prototype = {
__proto__: IndexedDBHelper.prototype,
upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
let objectStore = aDb.createObjectStore(SETTINGSSTORE_NAME,
{ keyPath: "settingName" });
objectStore.createIndex("settingValue", "settingValue", { unique: false });
if (DEBUG) debug("Created object stores and indexes");
if (aOldVersion != 0) {
return;
let objectStore;
if (aOldVersion == 0) {
objectStore = aDb.createObjectStore(SETTINGSSTORE_NAME, { keyPath: "settingName" });
if (DEBUG) debug("Created object stores");
} else if (aOldVersion == 1) {
if (DEBUG) debug("Get object store for upgrade and remove old index");
objectStore = aTransaction.objectStore(SETTINGSSTORE_NAME);
objectStore.deleteIndex("settingValue");
} else {
if (DEBUG) debug("Get object store for upgrade");
objectStore = aTransaction.objectStore(SETTINGSSTORE_NAME);
}
// Loading resource://app/defaults/settings.json doesn't work because
@ -66,12 +72,39 @@ SettingsDB.prototype = {
if (DEBUG) debug("Error parsing " + settingsFile.path + " : " + e);
return;
}
stream.close();
for (let setting in settings) {
if (DEBUG) debug("Adding setting " + setting);
objectStore.put({ settingName: setting,
settingValue: settings[setting] });
}
objectStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
let value = cursor.value;
if (value.settingName in settings) {
if (DEBUG) debug("Upgrade " +settings[value.settingName]);
value.defaultValue = settings[value.settingName];
delete settings[value.settingName];
if ("settingValue" in value) {
value.userValue = value.settingValue;
delete value.settingValue;
}
cursor.update(value);
} else if ("userValue" in value || "settingValue" in value) {
value.defaultValue = undefined;
if (aOldVersion == 1 && value.settingValue) {
value.userValue = value.settingValue;
delete value.settingValue;
}
cursor.update(value);
} else {
cursor.delete();
}
cursor.continue();
} else {
for (let name in settings) {
if (DEBUG) debug("Set new:" + name +", " + settings[name]);
objectStore.add({ settingName: name, defaultValue: settings[name], userValue: undefined });
}
}
};
},
init: function init(aGlobal) {

View File

@ -4,7 +4,7 @@
"use strict";
const DEBUG = false;
const DEBUG = true;
function debug(s) {
if (DEBUG) dump("-*- SettingsManager: " + s + "\n");
}
@ -30,6 +30,7 @@ const nsIDOMSettingsLock = Ci.nsIDOMSettingsLock;
function SettingsLock(aSettingsManager)
{
this._open = true;
this._isBusy = false;
this._requests = new Queue();
this._settingsManager = aSettingsManager;
this._transaction = null;
@ -48,11 +49,15 @@ SettingsLock.prototype = {
let request = info.request;
switch (info.intent) {
case "clear":
let req = store.clear();
req.onsuccess = function() { this._open = true;
Services.DOMRequest.fireSuccess(request, 0);
this._open = false; }.bind(lock);
req.onerror = function() { Services.DOMRequest.fireError(request, 0) };
let clearReq = store.clear();
clearReq.onsuccess = function() {
this._open = true;
Services.DOMRequest.fireSuccess(request, 0);
this._open = false;
}.bind(lock);
clearReq.onerror = function() {
Services.DOMRequest.fireError(request, 0)
};
break;
case "set":
let keys = Object.getOwnPropertyNames(info.settings);
@ -60,49 +65,64 @@ SettingsLock.prototype = {
let key = keys[i];
let last = i === keys.length - 1;
if (DEBUG) debug("key: " + key + ", val: " + JSON.stringify(info.settings[key]) + ", type: " + typeof(info.settings[key]));
lock._isBusy = true;
let checkKeyRequest = store.get(key);
checkKeyRequest.onsuccess = function (event) {
if (!event.target.result) {
if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + key + " is not in the database. Please add it to build/settings.js\n");
let defaultValue;
let userValue = info.settings[key];
if (event.target.result) {
defaultValue = event.target.result.defaultValue;
} else {
defaultValue = null;
if (DEBUG) debug("MOZSETTINGS-SET-WARNING: " + key + " is not in the database.\n");
}
let setReq;
if (typeof(info.settings[key]) != 'object') {
let obj = {settingName: key, defaultValue: defaultValue, userValue: userValue};
if (DEBUG) debug("store1: " + JSON.stringify(obj));
setReq = store.put(obj);
} else {
//Workaround for cloning issues
let defaultVal = JSON.parse(JSON.stringify(defaultValue));
let userVal = JSON.parse(JSON.stringify(userValue));
let obj = {settingName: key, defaultValue: defaultVal, userValue: userVal};
if (DEBUG) debug("store2: " + JSON.stringify(obj));
setReq = store.put(obj);
}
setReq.onsuccess = function() {
lock._isBusy = false;
cpmm.sendAsyncMessage("Settings:Changed", { key: key, value: userValue });
if (last && !request.error) {
lock._open = true;
Services.DOMRequest.fireSuccess(request, 0);
lock._open = false;
if (!lock._requests.isEmpty()) {
lock.process();
}
}
};
setReq.onerror = function() {
if (!request.error) {
Services.DOMRequest.fireError(request, setReq.error.name)
}
};
}
if(typeof(info.settings[key]) != 'object') {
req = store.put({settingName: key, settingValue: info.settings[key]});
} else {
//Workaround for cloning issues
let obj = JSON.parse(JSON.stringify(info.settings[key]));
req = store.put({settingName: key, settingValue: obj});
}
req.onsuccess = function() {
if (last && !request.error) {
lock._open = true;
Services.DOMRequest.fireSuccess(request, 0);
lock._open = false;
}
cpmm.sendAsyncMessage("Settings:Changed", { key: key, value: info.settings[key] });
};
req.onerror = function() {
if (!request.error) {
Services.DOMRequest.fireError(request, req.error.name)
}
};
}
break;
case "get":
req = (info.name === "*") ? store.mozGetAll()
: store.mozGetAll(info.name);
let getReq = (info.name === "*") ? store.mozGetAll()
: store.mozGetAll(info.name);
req.onsuccess = function(event) {
getReq.onsuccess = function(event) {
if (DEBUG) debug("Request for '" + info.name + "' successful. " +
"Record count: " + event.target.result.length);
if (DEBUG) debug("result: " + JSON.stringify(event.target.result));
if (event.target.result.length == 0) {
if (DEBUG) debug("MOZSETTINGS-GET-WARNING: " + info.name + " is not in the database. Please add it to build/settings.js\n");
if (DEBUG) debug("MOZSETTINGS-GET-WARNING: " + info.name + " is not in the database.\n");
}
let results = {
@ -113,7 +133,8 @@ SettingsLock.prototype = {
for (var i in event.target.result) {
let result = event.target.result[i];
var name = result.settingName;
var value = result.settingValue;
if (DEBUG) debug("VAL: " + result.userValue +", " + result.defaultValue + "\n");
var value = result.userValue !== undefined ? result.userValue : result.defaultValue;
results[name] = value;
results.__exposedProps__[name] = "r";
// If the value itself is an object, expose the properties.
@ -129,14 +150,12 @@ SettingsLock.prototype = {
this._open = false;
}.bind(lock);
req.onerror = function() {
getReq.onerror = function() {
Services.DOMRequest.fireError(request, 0)
};
break;
}
}
if (!lock._requests.isEmpty())
throw Components.results.NS_ERROR_ABORT;
lock._open = true;
},
@ -148,10 +167,15 @@ SettingsLock.prototype = {
let transactionType = this._settingsManager.hasWritePrivileges ? "readwrite" : "readonly";
lock._transaction = lock._settingsManager._settingsDB._db.transaction(SETTINGSSTORE_NAME, transactionType);
}
lock.process();
if (!lock._isBusy) {
lock.process();
} else {
this._settingsManager._locks.enqueue(lock);
}
}
if (!this._requests.isEmpty())
if (!this._requests.isEmpty() && !this._isBusy) {
this.process();
}
}
},
@ -181,7 +205,8 @@ SettingsLock.prototype = {
if (this._settingsManager.hasWritePrivileges) {
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
if (DEBUG) debug("send: " + JSON.stringify(aSettings));
this._requests.enqueue({request: req, intent: "set", settings: aSettings});
let settings = JSON.parse(JSON.stringify(aSettings));
this._requests.enqueue({request: req, intent: "set", settings: settings});
this.createTransactionAndProcess();
return req;
} else {
@ -266,7 +291,7 @@ SettingsManager.prototype = {
this._locks.enqueue(lock);
this._settingsDB.ensureDB(
function() { lock.createTransactionAndProcess(); },
function() { dump("ensureDB error cb!\n"); },
function() { dump("Cannot open Settings DB. Trying to open an old version?\n"); },
myGlobal );
this.nextTick(function() { this._open = false; }, lock);
return lock;

View File

@ -35,11 +35,13 @@ function onFailure() {
ok(false, "in on Failure!");
}
var wifi = {"net3g.apn": "internet.mnc012.mcc345.gprs"};
var wifi2 = {"net3g.apn": "internet.mnc012.mcc345.test"};
const wifi = {"net3g.apn": "internet.mnc012.mcc345.gprs"};
const wifi2 = {"net3g.apn": "internet.mnc012.mcc345.test"};
var wifi3 = {"net3g.apn2": "internet.mnc012.mcc345.test3"};
var wifiEnabled = {"wifi.enabled": true};
var wifiDisabled = {"wifi.enabled": false};
var screenBright = {"screen.brightness": 0.7};
var screenBright2 = {"screen.brightness": 0.1};
var wifiNetworks0 = { "wifi.networks[0]": { ssid: "myfreenetwork", mac: "01:23:45:67:89:ab", passwd: "secret"}};
var wifiNetworks1 = { "wifi.networks[1]": { ssid: "myfreenetwork2", mac: "01:23:45:67:89:ab", passwd: "secret2"}};
@ -47,7 +49,8 @@ var combination = {
"wifi.enabled": false,
"screen.brightness": 0.7,
"wifi.networks[0]": { ssid: "myfreenetwork", mac: "01:23:45:67:89:ab", passwd: "secret" },
"test.test": true
"test.test": true,
"net3g.apn2": "internet.mnc012.mcc345.gprs"
}
function equals(o1, o2) {
@ -101,14 +104,70 @@ var steps = [
req = lock.clear();
req.onsuccess = function () {
ok(true, "Deleted the database");
next();
};
},
function () {
ok(true, "Setting wifi");
var lock = mozSettings.createLock();
req = lock.set(wifi);
req.onsuccess = function () {
ok(true, "set done");
}
req.onerror = onFailure;
var lock2 = mozSettings.createLock();
req2 = lock2.get("net3g.apn");
req2.onsuccess = function () {
is(Object.keys(req2.result).length, 1, "length 1");
check(wifi, req2.result);
ok(true, "Get net3g.apn Done");
next();
};
req2.onerror = onFailure;
},
function () {
ok(true, "Change wifi1");
var lock = mozSettings.createLock();
req = lock.set(wifi2);
req.onsuccess = function () {
ok(true, "Set Done");
};
req.onerror = onFailure;
req2 = lock.set(screenBright);
ok(true, "Get changed net3g.apn");
req2 = lock.get("net3g.apn");
req2.onsuccess = function () {
is(Object.keys(req2.result).length, 1, "length 1");
check(wifi2, req2.result);
ok(true, "Get net3g.apn Done");
next();
};
req2.onerror = onFailure;
},
function () {
ok(true, "Set Combination");
var lock = mozSettings.createLock();
req3 = lock.set(combination);
req3.onsuccess = function () {
ok(true, "set done");
req4 = lock.get("net3g.apn2");
req4.onsuccess = function() {
ok(true, "Done");
check(combination["net3g.apn2"], req4.result["net3g.apn2"]);
next();
}
}
req3.onerror = onFailure;
},
function() {
var lock = mozSettings.createLock();
req4 = lock.get("net3g.apn2");
req4.onsuccess = function() {
ok(true, "Done");
check(combination["net3g.apn2"], req4.result["net3g.apn2"]);
next();
}
req2.onerror = onFailure;
req4.onerror = onFailure;
},
function() {
ok(true, "Get unknown key");
@ -293,7 +352,7 @@ var steps = [
req2.onerror = onFailure;
},
function () {
ok(true, "Change wifi");
ok(true, "Change wifi1");
var lock = mozSettings.createLock();
req = lock.set(wifi2);
req.onsuccess = function () {
@ -354,34 +413,34 @@ var steps = [
};
{
var lock2 = mozSettings.createLock();
req = lock2.get("*");
req.onsuccess = function () {
is(Object.keys(req.result).length, 32, "length 12");
ok(true, JSON.stringify(req.result));
req2 = lock2.get("*");
req2.onsuccess = function () {
is(Object.keys(req2.result).length, 32, "length 12");
ok(true, JSON.stringify(req2.result));
ok(true, "Get all settings Done");
};
req.onerror = onFailure;
req2.onerror = onFailure;
}
var lock2 = mozSettings.createLock();
var obj = {};
obj["wifi.enabled" + 30] = true;
req2 = lock2.set( obj );
req2.onsuccess = function () {
req3 = lock2.set( obj );
req3.onsuccess = function () {
ok(true, "Set12 Done");
};
req2.onerror = onFailure;
req3.onerror = onFailure;
var lock3 = mozSettings.createLock();
// with one lock
for (var i = 0; i < 30; i++) {
req3 = lock3.get("wifi.enabled" + i);
req4 = lock3.get("wifi.enabled" + i);
var testObj = {};
testObj["wifi.enabled" + i] = true;
req3.onsuccess = function () {
req4.onsuccess = function () {
check(this.request.result, this.testObj);
ok(true, "Get1 Done");
}.bind({testObj: testObj, request: req3});
req3.onerror = onFailure;
}.bind({testObj: testObj, request: req4});
req4.onerror = onFailure;
}
ok(true, "start next2!");
@ -483,7 +542,7 @@ var steps = [
req.onerror = onFailure;
},
function () {
ok(true, "Change wifi");
ok(true, "Change wifi2");
var lock = mozSettings.createLock();
req = lock.set(wifi2);
req.onsuccess = function () {