Bug 946316 - Allow the use of strings as DataStore IDs, r=ehsan

This commit is contained in:
Andrea Marchesini 2013-12-19 09:07:21 +00:00
parent 4802a84a12
commit 1200c39f8e
10 changed files with 471 additions and 120 deletions

View File

@ -49,24 +49,14 @@ function throwReadOnly(aWindow) {
new aWindow.DOMError("ReadOnlyError", "DataStore in readonly mode"));
}
function parseIds(aId) {
function parseId(aId) {
aId = parseInt(aId);
return (isNaN(aId) || aId <= 0) ? null : aId;
function validateId(aId) {
// If string, it cannot be empty.
if (typeof(aId) == 'string') {
return aId.length;
}
if (!Array.isArray(aId)) {
return parseId(aId);
}
for (let i = 0; i < aId.length; ++i) {
aId[i] = parseId(aId[i]);
if (aId[i] === null) {
return null;
}
}
return aId;
aId = parseInt(aId);
return (!isNaN(aId) && aId > 0);
}
/* DataStore object */
@ -365,10 +355,12 @@ this.DataStore.prototype = {
return this._readOnly;
},
get: function(aId) {
aId = parseIds(aId);
if (aId === null) {
return throwInvalidArg(this._window);
get: function() {
let ids = Array.prototype.slice.call(arguments);
for (let i = 0; i < ids.length; ++i) {
if (!validateId(ids[i])) {
return throwInvalidArg(this._window);
}
}
let self = this;
@ -376,18 +368,16 @@ this.DataStore.prototype = {
// Promise<Object>
return this.newDBPromise("readonly",
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
self.getInternal(aStore,
Array.isArray(aId) ? aId : [ aId ],
self.getInternal(aStore, ids,
function(aResults) {
aResolve(Array.isArray(aId) ? aResults : aResults[0]);
aResolve(ids.length > 1 ? aResults : aResults[0]);
});
}
);
},
put: function(aObj, aId) {
aId = parseInt(aId);
if (isNaN(aId) || aId <= 0) {
if (!validateId(aId)) {
return throwInvalidArg(this._window);
}
@ -407,8 +397,7 @@ this.DataStore.prototype = {
add: function(aObj, aId) {
if (aId) {
aId = parseInt(aId);
if (isNaN(aId) || aId <= 0) {
if (!validateId(aId)) {
return throwInvalidArg(this._window);
}
}
@ -428,8 +417,7 @@ this.DataStore.prototype = {
},
remove: function(aId) {
aId = parseInt(aId);
if (isNaN(aId) || aId <= 0) {
if (!validateId(aId)) {
return throwInvalidArg(this._window);
}

View File

@ -34,13 +34,12 @@ Cu.import('resource://gre/modules/XPCOMUtils.jsm');
* legend:
* - RID = revision ID
* - R = revision object (with the internalRevisionId that is a number)
* - X = current object ID. Default value is 0
* - MX = max known object ID
* - X = current object ID.
* - L = the list of revisions that we have to send
*
* State: init: do you have RID ?
* YES: state->initRevision; loop
* NO: get R; get MX; state->sendAll; send a 'clear'
* NO: get R; X=0; state->sendAll; send a 'clear'
*
* State: initRevision. Get R from RID. Done?
* YES: state->revisionCheck; loop
@ -49,12 +48,13 @@ Cu.import('resource://gre/modules/XPCOMUtils.jsm');
* State: revisionCheck: get all the revisions between R and NOW. Done?
* YES and R == NOW: state->done; loop
* YES and R != NOW: Store this revisions in L; state->revisionSend; loop
* NO: R = NOW; get MX; state->sendAll; send a 'clear';
* NO: R = NOW; X=0; state->sendAll; send a 'clear'
*
* State: sendAll: get the first object with id > X. Done?
* YES and object.id > MX: state->revisionCheck; loop
* YES and object.id <= MX: X = object.id; send 'add'
* NO: state->revisionCheck; loop
* State: sendAll: is R still the last revision?
* YES get the first object with id > X. Done?
* YES: X = object.id; send 'add'
* NO: state->revisionCheck; loop
* NO: R = NOW; X=0; send a 'clear'
*
* State: revisionSend: do you have something from L to send?
* YES and L[0] == 'removed': R=L[0]; send 'remove' with ID
@ -93,7 +93,6 @@ this.DataStoreCursor.prototype = {
_revision: null,
_revisionsList: null,
_objectId: 0,
_maxObjectId: 0,
_state: STATE_INIT,
@ -150,12 +149,9 @@ this.DataStoreCursor.prototype = {
let request = aRevisionStore.openCursor(null, 'prev');
request.onsuccess = function(aEvent) {
self._revision = aEvent.target.result.value;
self.getMaxObjectId(aStore,
function() {
self._state = STATE_SEND_ALL;
aResolve(ObjectWrapper.wrap({ operation: 'clear' }, self._window));
}
);
self._objectId = 0;
self._state = STATE_SEND_ALL;
aResolve(ObjectWrapper.wrap({ operation: 'clear' }, self._window));
}
},
@ -170,6 +166,7 @@ this.DataStoreCursor.prototype = {
// This revision doesn't exist.
if (aInternalRevisionId == undefined) {
self._revisionId = null;
self._objectId = 0;
self._state = STATE_INIT;
self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
return;
@ -238,13 +235,10 @@ this.DataStoreCursor.prototype = {
return;
}
self.getMaxObjectId(aStore,
function() {
self._revisionId = null;
self._state = STATE_INIT;
self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
}
);
self._revisionId = null;
self._objectId = 0;
self._state = STATE_INIT;
self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
return;
}
}
@ -292,18 +286,28 @@ this.DataStoreCursor.prototype = {
debug('StateMachineSendAll');
let self = this;
let request = aStore.openCursor(self._window.IDBKeyRange.lowerBound(this._objectId, true));
let request = aRevisionStore.openCursor(null, 'prev');
request.onsuccess = function(aEvent) {
let cursor = aEvent.target.result;
if (!cursor || cursor.key > self._maxObjectId) {
self._state = STATE_REVISION_CHECK;
self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
if (self._revision.revisionId != aEvent.target.result.value.revisionId) {
self._revision = aEvent.target.result.value;
self._objectId = 0;
aResolve(ObjectWrapper.wrap({ operation: 'clear' }, self._window));
return;
}
self._objectId = cursor.key;
aResolve(ObjectWrapper.wrap({ operation: 'add', id: self._objectId,
data: cursor.value }, self._window));
let request = aStore.openCursor(self._window.IDBKeyRange.lowerBound(self._objectId, true));
request.onsuccess = function(aEvent) {
let cursor = aEvent.target.result;
if (!cursor) {
self._state = STATE_REVISION_CHECK;
self.stateMachine(aStore, aRevisionStore, aResolve, aReject);
return;
}
self._objectId = cursor.key;
aResolve(ObjectWrapper.wrap({ operation: 'add', id: self._objectId,
data: cursor.value }, self._window));
};
};
},
@ -377,17 +381,6 @@ this.DataStoreCursor.prototype = {
operation: 'done' }, this._window));
},
getMaxObjectId: function(aStore, aCallback) {
let self = this;
let request = aStore.openCursor(null, 'prev');
request.onsuccess = function(aEvent) {
if (aEvent.target.result) {
self._maxObjectId = aEvent.target.result.key;
}
aCallback();
}
},
// public interface
get store() {

View File

@ -76,7 +76,7 @@
objects.push(i);
}
gStore.get(objects).then(function(data) {
gStore.get.apply(gStore, objects).then(function(data) {
is(data.length, objects.length, "Get - Data matches");
for (var i = 0; i < data.length; ++i) {
is(data[i], objects[i] - 1, "Get - Data matches: " + i + " " + data[i] + " == " + objects[i]);

View File

@ -101,7 +101,6 @@
function() { testStoreAdd(42).then(function(id) {
gId = id; runTest(); }, cbError); },
function() { testStoreGet(gId, 42); },
function() { testStoreGet(gId + "", 42); },
// Add + Get - boolean
function() { testStoreAdd(true).then(function(id) {

View File

@ -0,0 +1,161 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for DataStore - string or unsigned long keys</title>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
var gStore;
var gEvent;
var gChangeId;
function is(a, b, msg) {
alert((a === b ? 'OK' : 'KO') + ' ' + msg)
}
function ok(a, msg) {
alert((a ? 'OK' : 'KO')+ ' ' + msg)
}
function cbError() {
alert('KO error');
}
function finish() {
alert('DONE');
}
function testGetDataStores() {
navigator.getDataStores('foo').then(function(stores) {
gStore = stores[0];
runTest();
}, cbError);
}
function testAdd_noKey(key) {
gEvent = 'added';
gChangeId = key;
gStore.add({ a: 42 }).then(function(id) {
is(id, key, "Id must be " + key + " received: " + id);
});
}
function testAdd_withKey(key) {
gEvent = 'added';
gChangeId = key;
gStore.add({ a: 42 }, key).then(function(id) {
is(id, key, "Id must be " + key + " received: " + id);
});
}
function testPut(key) {
gEvent = 'updated';
gChangeId = key;
gStore.put({ a: 42 }, key).then(function(id) {
is(id, key, "Id must be " + key + " received: " + id);
});
}
function testGet(key) {
gStore.get(key).then(function(value) {
ok(value, "Object received!");
is(value.a, 42, "Object received with right value!");
runTest();
});
}
function testArrayGet(key) {
gStore.get.apply(gStore, key).then(function(values) {
is(values.length, key.length, "Object received!");
for (var i = 0; i < values.length; ++i) {
is(values[i].a, 42, "Object received with right value!");
}
runTest();
});
}
function testRemove(key, success) {
gEvent = 'removed';
gChangeId = key;
gStore.remove(key).then(function(value) {
is(value, success, "Status must be " + success + " received: " + value);
if (value == false) {
runTest();
return;
}
});
}
function eventListener() {
gStore.onchange = function(e) {
is(e.operation, gEvent, "Operation matches: " + e.operation + " " + gEvent);
ok(e.id === gChangeId, "Operation id matches");
runTest();
};
runTest();
}
var tests = [
// Test for GetDataStore
testGetDataStores,
// Event listener
eventListener,
// add
function() { testAdd_noKey(1); },
function() { testAdd_withKey(123); },
function() { testAdd_noKey(124); },
function() { testAdd_withKey('foobar'); },
function() { testAdd_noKey(125); },
function() { testAdd_withKey('125'); },
function() { testAdd_withKey('126'); },
function() { testAdd_noKey(126); },
// put
function() { testPut(42); },
function() { testPut('42'); },
// get
function() { testGet('42'); },
function() { testGet(42); },
function() { testGet(1); },
function() { testGet(123); },
function() { testGet(124); },
function() { testGet('foobar'); },
function() { testGet(125); },
function() { testGet('125'); },
function() { testGet('126'); },
function() { testGet(126); },
function() { testArrayGet(['42', 42, 1, 123, 124, 'foobar', 125, '125', '126', 126]); },
// remove
function() { testRemove(42, true); },
function() { testRemove('42', true); },
function() { testRemove('43', false); },
function() { testRemove(43, false); },
];
function runTest() {
if (!tests.length) {
finish();
return;
}
var test = tests.shift();
test();
}
runTest();
</script>
</body>
</html>

View File

@ -51,6 +51,9 @@
is(cursor.store, gStore, "Cursor.store is the store");
ok("next" in cursor, "Cursor.next exists");
ok("close" in cursor, "Cursor.close exists");
cursor.close();
runTest();
}
@ -133,16 +136,23 @@
function() {
gExpectedEvents = true;
gStore.add(1,2).then(function(id) {
gStore.add(1).then(function(id) {
gRevisions.push(gStore.revisionId);
ok(true, "Iteme: " + id + " added");
ok(true, "Item: " + id + " added");
});
},
function() {
gStore.add(2,3).then(function(id) {
gStore.add(2,"foobar").then(function(id) {
gRevisions.push(gStore.revisionId);
ok(true, "Iteme: " + id + " added");
ok(true, "Item: " + id + " added");
});
},
function() {
gStore.add(3,3).then(function(id) {
gRevisions.push(gStore.revisionId);
ok(true, "Item: " + id + " added");
});
},
@ -150,8 +160,9 @@
gExpectedEvents = false;
var cursor = gStore.sync();
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 2, data: 1 },
{ operation: 'add', id: 3, data: 2 },
{ operation: 'add', id: 1, data: 1 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
@ -159,29 +170,39 @@
function() {
var cursor = gStore.sync('wrong revision ID');
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 2, data: 1 },
{ operation: 'add', id: 3, data: 2 },
{ operation: 'add', id: 1, data: 1 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[0]);
var steps = [ { operation: 'add', id: 2, data: 1 },
{ operation: 'add', id: 3, data: 2 },
var steps = [ { operation: 'add', id: 1, data: 1 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[1]);
var steps = [ { operation: 'add', id: 3, data: 2 },
var steps = [ { operation: 'add', id: 'foobar', data: 2 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[2]);
var steps = [ { operation: 'add', id: 3, data: 3 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[3]);
var steps = [ { operation: 'done' }];
testCursor(cursor, steps);
},
@ -189,7 +210,7 @@
// Test after an update
function() {
gExpectedEvents = true;
gStore.put(3, 2).then(function() {
gStore.put(123, 1).then(function() {
gRevisions.push(gStore.revisionId);
});
},
@ -198,8 +219,9 @@
gExpectedEvents = false;
var cursor = gStore.sync();
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 2, data: 3 },
{ operation: 'add', id: 3, data: 2 },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
@ -207,37 +229,48 @@
function() {
var cursor = gStore.sync('wrong revision ID');
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 2, data: 3 },
{ operation: 'add', id: 3, data: 2 },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[0]);
var steps = [ { operation: 'add', id: 2, data: 3 },
{ operation: 'add', id: 3, data: 2 },
var steps = [ { operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[1]);
var steps = [ { operation: 'add', id: 3, data: 2 },
{ operation: 'update', id: 2, data: 3 },
var steps = [ { operation: 'add', id: 'foobar', data: 2 },
{ operation: 'add', id: 3, data: 3 },
{ operation: 'update', id: 1, data: 123 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[2]);
var steps = [ { operation: 'update', id: 2, data: 3 },
var steps = [ { operation: 'add', id: 3, data: 3 },
{ operation: 'update', id: 1, data: 123 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[3]);
var steps = [ { operation: 'update', id: 1, data: 123 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[4]);
var steps = [ { operation: 'done' }];
testCursor(cursor, steps);
},
@ -254,7 +287,8 @@
gExpectedEvents = false;
var cursor = gStore.sync();
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 2, data: 3 },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
@ -262,42 +296,52 @@
function() {
var cursor = gStore.sync('wrong revision ID');
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 2, data: 3 },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[0]);
var steps = [ { operation: 'add', id: 2, data: 3 },
var steps = [ { operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 'foobar', data: 2 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[1]);
var steps = [ { operation: 'update', id: 2, data: 3 },
var steps = [ { operation: 'add', id: 'foobar', data: 2 },
{ operation: 'update', id: 1, data: 123 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[2]);
var steps = [ { operation: 'update', id: 2, data: 3 },
{ operation: 'remove', id: 3 },
var steps = [ { operation: 'update', id: 1, data: 123 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[3]);
var steps = [ { operation: 'remove', id: 3 },
var steps = [ { operation: 'update', id: 1, data: 123 },
{ operation: 'remove', id: 3 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[4]);
var steps = [ { operation: 'remove', id: 3 },
{ operation: 'done' }];
testCursor(cursor, steps);
},
function() {
var cursor = gStore.sync(gRevisions[5]);
var steps = [ { operation: 'done' }];
testCursor(cursor, steps);
},
@ -306,33 +350,39 @@
function() {
gCursor = gStore.sync();
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 2, data: 3 } ];
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 'foobar', data: 2 } ];
testCursor(gCursor, steps);
},
function() {
gStore.add(42).then(function(id) {
gStore.add(42, 2).then(function(id) {
ok(true, "Item: " + id + " added");
gRevisions.push(gStore.revisionId);
runTest();
});
},
// New events when the cursor is active
function() {
var steps = [ { operation: 'add', id: 4, data: 42 } ];
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 2, data: 42 },
{ operation: 'add', id: 'foobar', data: 2 } ]
testCursor(gCursor, steps);
},
function() {
gStore.put(42, 2).then(function(id) {
gStore.put(43, 2).then(function(id) {
gRevisions.push(gStore.revisionId);
runTest();
});
},
function() {
var steps = [ { operation: 'update', id: 2, data: 42 } ];
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 2, data: 43 },
{ operation: 'add', id: 'foobar', data: 2 } ]
testCursor(gCursor, steps);
},
@ -344,7 +394,9 @@
},
function() {
var steps = [ { operation: 'remove', id: 2 } ];
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 'foobar', data: 2 } ]
testCursor(gCursor, steps);
},
@ -357,7 +409,36 @@
},
function() {
var steps = [ { operation: 'add', id: 5, data: 42 } ];
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 1, data: 123 },
{ operation: 'add', id: 4, data: 42 },
{ operation: 'add', id: 'foobar', data: 2 } ]
testCursor(gCursor, steps);
},
function() {
gStore.clear().then(function() {
gRevisions.push(gStore.revisionId);
runTest();
});
},
function() {
var steps = [ { operation: 'clear' } ];
testCursor(gCursor, steps);
},
function() {
gStore.add(42).then(function(id) {
ok(true, "Item: " + id + " added");
gRevisions.push(gStore.revisionId);
runTest();
});
},
function() {
var steps = [ { operation: 'clear', },
{ operation: 'add', id: 5, data: 42 } ];
testCursor(gCursor, steps);
},
@ -379,7 +460,7 @@
function() {
var steps = [ { operation: 'clear' },
{ operation: 'add', id: 6, data: 42 },
{ operation: 'done' } ];
{ operation: 'done'} ];
testCursor(gCursor, steps);
},

View File

@ -12,6 +12,7 @@ support-files =
file_sync.html
file_bug924104.html
file_certifiedApp.html
file_keys.html
[test_app_install.html]
[test_readonly.html]
@ -22,3 +23,4 @@ support-files =
[test_sync.html]
[test_bug924104.html]
[test_certifiedApp.html]
[test_keys.html]

View File

@ -0,0 +1,128 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for DataStore - string or unsigned long keys</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_keys.html';
var gApp;
function cbError() {
ok(false, "Error callback invoked");
finish();
}
function installApp() {
var request = navigator.mozApps.install(gHostedManifestURL);
request.onerror = cbError;
request.onsuccess = function() {
gApp = request.result;
runTest();
}
}
function uninstallApp() {
// Uninstall the app.
var request = navigator.mozApps.mgmt.uninstall(gApp);
request.onerror = cbError;
request.onsuccess = function() {
// All done.
info("All done");
runTest();
}
}
function testApp() {
var ifr = document.createElement('iframe');
ifr.setAttribute('mozbrowser', 'true');
ifr.setAttribute('mozapp', gApp.manifestURL);
ifr.setAttribute('src', gApp.manifest.launch_path);
var domParent = document.getElementById('container');
// Set us up to listen for messages from the app.
var listener = function(e) {
var message = e.detail.message;
if (/^OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/DONE/.exec(message)) {
ok(true, "Messaging from app complete");
ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
domParent.removeChild(ifr);
runTest();
}
}
// This event is triggered when the app calls "alert".
ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
domParent.appendChild(ifr);
}
var tests = [
// Permissions
function() {
SpecialPowers.pushPermissions(
[{ "type": "browser", "allow": 1, "context": document },
{ "type": "embed-apps", "allow": 1, "context": document },
{ "type": "webapps-manage", "allow": 1, "context": document }], runTest);
},
// Preferences
function() {
SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true],
["dom.datastore.enabled", true],
["dom.testing.ignore_ipc_principal", true],
["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
},
function() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
runTest();
},
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
},
// Installing the app
installApp,
// Run tests in app
testApp,
// Uninstall the app
uninstallApp
];
function runTest() {
if (!tests.length) {
finish();
return;
}
var test = tests.shift();
test();
}
function finish() {
SimpleTest.finish();
}
if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</body>
</html>

View File

@ -4,6 +4,8 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
typedef (DOMString or unsigned long) DataStoreKey;
[Pref="dom.datastore.enabled",
JSImplementation="@mozilla.org/dom/datastore;1"]
interface DataStore : EventTarget {
@ -18,19 +20,16 @@ interface DataStore : EventTarget {
readonly attribute boolean readOnly;
// Promise<any>
Promise get(unsigned long id);
// Promise<any>
Promise get(sequence<unsigned long> id);
Promise get(DataStoreKey... id);
// Promise<void>
Promise put(any obj, unsigned long id);
Promise put(any obj, DataStoreKey id);
// Promise<unsigned long>
Promise add(any obj, optional unsigned long id);
// Promise<DataStoreKey>
Promise add(any obj, optional DataStoreKey id);
// Promise<boolean>
Promise remove(unsigned long id);
Promise remove(DataStoreKey id);
// Promise<void>
Promise clear();
@ -70,6 +69,6 @@ dictionary DataStoreTask {
DOMString revisionId;
DataStoreOperation operation;
unsigned long id;
DataStoreKey id;
any data;
};

View File

@ -6,7 +6,7 @@
dictionary DataStoreChangeEventInit : EventInit {
DOMString revisionId = "";
unsigned long id = 0;
DataStoreKey id = 0;
DOMString operation = "";
};
@ -14,6 +14,6 @@ dictionary DataStoreChangeEventInit : EventInit {
Constructor(DOMString type, optional DataStoreChangeEventInit eventInitDict)]
interface DataStoreChangeEvent : Event {
readonly attribute DOMString revisionId;
readonly attribute unsigned long id;
readonly attribute DataStoreKey id;
readonly attribute DOMString operation;
};