Backing out bug 893276 for causing bug 1058840.

This commit is contained in:
Dave Townsend 2014-09-19 11:28:55 -07:00
parent c78265ca7e
commit 5eaf6a5f85
2 changed files with 40 additions and 287 deletions

View File

@ -8,16 +8,13 @@ module.metadata = {
"stability": "stable"
};
const { Cc, Ci, Cu } = require("chrome");
const { Cc, Ci } = require("chrome");
const file = require("./io/file");
const prefs = require("./preferences/service");
const jpSelf = require("./self");
const timer = require("./timers");
const unload = require("./system/unload");
const { emit, on, off } = require("./event/core");
const { defer } = require('./core/promise');
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod";
const WRITE_PERIOD_DEFAULT = 300000; // 5 minutes
@ -38,57 +35,6 @@ Object.defineProperties(exports, {
}
});
function getHash(data) {
let { promise, resolve } = defer();
let crypto = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
crypto.init(crypto.MD5);
let listener = {
onStartRequest: function() { },
onDataAvailable: function(request, context, inputStream, offset, count) {
crypto.updateFromStream(inputStream, count);
},
onStopRequest: function(request, context, status) {
resolve(crypto.finish(false));
}
};
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let stream = converter.convertToInputStream(data);
let pump = Cc["@mozilla.org/network/input-stream-pump;1"].
createInstance(Ci.nsIInputStreamPump);
pump.init(stream, -1, -1, 0, 0, true);
pump.asyncRead(listener, null);
return promise;
}
function writeData(filename, data) {
let { promise, resolve, reject } = defer();
let stream = file.open(filename, "w");
try {
stream.writeAsync(data, err => {
if (err)
reject(err);
else
resolve();
});
}
catch (err) {
// writeAsync closes the stream after it's done, so only close on error.
stream.close();
reject(err);
}
return promise;
}
// A generic JSON store backed by a file on disk. This should be isolated
// enough to move to its own module if need be...
function JsonStore(options) {
@ -97,9 +43,11 @@ function JsonStore(options) {
this.writePeriod = options.writePeriod;
this.onOverQuota = options.onOverQuota;
this.onWrite = options.onWrite;
this.hash = null;
unload.ensure(this);
this.startTimer();
this.writeTimer = timer.setInterval(this.write.bind(this),
this.writePeriod);
}
JsonStore.prototype = {
@ -133,18 +81,11 @@ JsonStore.prototype = {
undefined;
},
startTimer: function JsonStore_startTimer() {
timer.setTimeout(() => {
this.write().then(this.startTimer.bind(this));
}, this.writePeriod);
},
// Removes the backing file and all empty subdirectories.
purge: function JsonStore_purge() {
try {
// This'll throw if the file doesn't exist.
file.remove(this.filename);
this.hash = null;
let parentPath = this.filename;
do {
parentPath = file.dirname(parentPath);
@ -164,25 +105,31 @@ JsonStore.prototype = {
// errors cause tests to fail. Supporting "known" errors in the test
// harness appears to be non-trivial. Maybe later.
this.root = JSON.parse(str);
let self = this;
getHash(str).then(hash => this.hash = hash);
}
catch (err) {
this.root = {};
this.hash = null;
}
},
// If the store is under quota, writes the root to the backing file.
// Otherwise quota observers are notified and nothing is written.
write: function JsonStore_write() {
if (this.quotaUsage > 1)
this.onOverQuota(this);
else
this._write();
},
// Cleans up on unload. If unloading because of uninstall, the store is
// purged; otherwise it's written.
unload: function JsonStore_unload(reason) {
timer.clearTimeout(this.writeTimer);
timer.clearInterval(this.writeTimer);
this.writeTimer = null;
if (reason === "uninstall")
this.purge();
else
this.write();
this._write();
},
// True if the root is an empty object.
@ -201,40 +148,32 @@ JsonStore.prototype = {
// Writes the root to the backing file, notifying write observers when
// complete. If the store is over quota or if it's empty and the store has
// never been written, nothing is written and write observers aren't notified.
write: Task.async(function JsonStore_write() {
_write: function JsonStore__write() {
// Don't write if the root is uninitialized or if the store is empty and the
// backing file doesn't yet exist.
if (!this.isRootInited || (this._isEmpty && !file.exists(this.filename)))
return;
let data = JSON.stringify(this.root);
// If the store is over quota, don't write. The current under-quota state
// should persist.
if ((this.quota > 0) && (data.length > this.quota)) {
this.onOverQuota(this);
return;
}
// Hash the data to compare it to any previously written data
let hash = yield getHash(data);
if (hash == this.hash)
if (this.quotaUsage > 1)
return;
// Finally, write.
let stream = file.open(this.filename, "w");
try {
yield writeData(this.filename, data);
this.hash = hash;
if (this.onWrite)
this.onWrite(this);
stream.writeAsync(JSON.stringify(this.root), function writeAsync(err) {
if (err)
console.error("Error writing simple storage file: " + this.filename);
else if (this.onWrite)
this.onWrite(this);
}.bind(this));
}
catch (err) {
console.error("Error writing simple storage file: " + this.filename);
console.error(err);
// writeAsync closes the stream after it's done, so only close on error.
stream.close();
}
})
}
};

View File

@ -6,7 +6,6 @@ const file = require("sdk/io/file");
const prefs = require("sdk/preferences/service");
const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota";
const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod";
let {Cc,Ci} = require("chrome");
@ -35,13 +34,12 @@ exports.testSetGet = function (assert, done) {
// Load the module again and make sure the value stuck.
loader = Loader(module);
ss = loader.require("sdk/simple-storage");
assert.equal(ss.storage.foo, val, "Value should persist");
manager(loader).jsonStore.onWrite = function (storage) {
assert.fail("Nothing should be written since `storage` was not changed.");
file.remove(storeFilename);
done();
};
assert.equal(ss.storage.foo, val, "Value should persist");
loader.unload();
file.remove(storeFilename);
done();
};
let val = "foo";
ss.storage.foo = val;
@ -162,11 +160,10 @@ exports.testQuotaExceededHandle = function (assert, done) {
assert.equal(ss.storage.x, 4, "x value should be correct");
assert.equal(ss.storage.y, 5, "y value should be correct");
manager(loader).jsonStore.onWrite = function (storage) {
assert.fail("Nothing should be written since `storage` was not changed.");
prefs.reset(QUOTA_PREF);
done();
};
loader.unload();
prefs.reset(QUOTA_PREF);
done();
};
loader.unload();
});
@ -200,9 +197,6 @@ exports.testQuotaExceededNoHandle = function (assert, done) {
assert.equal(ss.storage, val,
"Over-quota value should not have been written, " +
"old value should have persisted: " + ss.storage);
manager(loader).jsonStore.onWrite = function (storage) {
assert.fail("Nothing should be written since `storage` was not changed.");
};
loader.unload();
prefs.reset(QUOTA_PREF);
done();
@ -257,184 +251,6 @@ exports.testUninstall = function (assert, done) {
loader.unload();
};
exports.testChangeInnerArray = function(assert, done) {
prefs.set(WRITE_PERIOD_PREF, 10);
let expected = {
x: [5, 7],
y: [7, 28],
z: [6, 2]
};
// Load the module, set a value.
let loader = Loader(module);
let ss = loader.require("sdk/simple-storage");
manager(loader).jsonStore.onWrite = function (storage) {
assert.ok(file.exists(storeFilename), "Store file should exist");
// Load the module again and check the result
loader = Loader(module);
ss = loader.require("sdk/simple-storage");
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Add a property
ss.storage.x.push(["bar"]);
expected.x.push(["bar"]);
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Modify a property
ss.storage.y[0] = 42;
expected.y[0] = 42;
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Delete a property
delete ss.storage.z[1];
delete expected.z[1];
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Modify the new inner-object
ss.storage.x[2][0] = "baz";
expected.x[2][0] = "baz";
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
manager(loader).jsonStore.onWrite = function (storage) {
assert.fail("Nothing should be written since `storage` was not changed.");
};
loader.unload();
// Load the module again and check the result
loader = Loader(module);
ss = loader.require("sdk/simple-storage");
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
loader.unload();
file.remove(storeFilename);
prefs.reset(WRITE_PERIOD_PREF);
done();
};
};
};
};
};
ss.storage = {
x: [5, 7],
y: [7, 28],
z: [6, 2]
};
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
loader.unload();
};
exports.testChangeInnerObject = function(assert, done) {
prefs.set(WRITE_PERIOD_PREF, 10);
let expected = {
x: {
a: 5,
b: 7
},
y: {
c: 7,
d: 28
},
z: {
e: 6,
f: 2
}
};
// Load the module, set a value.
let loader = Loader(module);
let ss = loader.require("sdk/simple-storage");
manager(loader).jsonStore.onWrite = function (storage) {
assert.ok(file.exists(storeFilename), "Store file should exist");
// Load the module again and check the result
loader = Loader(module);
ss = loader.require("sdk/simple-storage");
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Add a property
ss.storage.x.g = {foo: "bar"};
expected.x.g = {foo: "bar"};
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Modify a property
ss.storage.y.c = 42;
expected.y.c = 42;
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Delete a property
delete ss.storage.z.f;
delete expected.z.f;
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
// Modify the new inner-object
ss.storage.x.g.foo = "baz";
expected.x.g.foo = "baz";
manager(loader).jsonStore.onWrite = function (storage) {
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
manager(loader).jsonStore.onWrite = function (storage) {
assert.fail("Nothing should be written since `storage` was not changed.");
};
loader.unload();
// Load the module again and check the result
loader = Loader(module);
ss = loader.require("sdk/simple-storage");
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
loader.unload();
file.remove(storeFilename);
prefs.reset(WRITE_PERIOD_PREF);
done();
};
};
};
};
};
ss.storage = {
x: {
a: 5,
b: 7
},
y: {
c: 7,
d: 28
},
z: {
e: 6,
f: 2
}
};
assert.equal(JSON.stringify(ss.storage),
JSON.stringify(expected), "Should see the expected object");
loader.unload();
};
exports.testSetNoSetRead = function (assert, done) {
// Load the module, set a value.
let loader = Loader(module);
@ -453,13 +269,12 @@ exports.testSetNoSetRead = function (assert, done) {
// Load the module a third time and make sure the value stuck.
loader = Loader(module);
ss = loader.require("sdk/simple-storage");
assert.equal(ss.storage.foo, val, "Value should persist");
manager(loader).jsonStore.onWrite = function (storage) {
assert.fail("Nothing should be written since `storage` was not changed.");
file.remove(storeFilename);
done();
};
assert.equal(ss.storage.foo, val, "Value should persist");
loader.unload();
file.remove(storeFilename);
done();
};
let val = "foo";
ss.storage.foo = val;
@ -480,13 +295,12 @@ function setGetRoot(assert, done, val, compare) {
// Load the module again and make sure the value stuck.
loader = Loader(module);
ss = loader.require("sdk/simple-storage");
assert.ok(compare(ss.storage, val), "Value should persist");
manager(loader).jsonStore.onWrite = function (storage) {
assert.fail("Nothing should be written since `storage` was not changed.");
manager(loader).jsonStore.onWrite = function () {
file.remove(storeFilename);
done();
};
assert.ok(compare(ss.storage, val), "Value should persist");
loader.unload();
file.remove(storeFilename);
done();
};
ss.storage = val;
assert.ok(compare(ss.storage, val), "Value read should be value set");