Bug 871445 - patch 6 - DataStore: getLength and arrays in get(), r=ehsan

This commit is contained in:
Andrea Marchesini 2013-10-02 13:27:15 -04:00
Родитель 37fce0cb23
Коммит 6d4224069a
5 изменённых файлов: 343 добавлений и 30 удалений

Просмотреть файл

@ -19,6 +19,10 @@ const REVISION_UPDATED = "updated";
const REVISION_REMOVED = "removed";
const REVISION_VOID = "void";
// This value has to be tuned a bit. Currently it's just a guess
// and yet we don't know if it's too low or too high.
const MAX_REQUESTS = 25;
Cu.import("resource://gre/modules/DataStoreDB.jsm");
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
Cu.import('resource://gre/modules/Services.jsm');
@ -70,14 +74,42 @@ DataStore.prototype = {
});
},
getInternal: function(aWindow, aResolve, aStore, aId) {
debug("GetInternal " + aId);
getInternal: function(aStore, aIds, aCallback) {
debug("GetInternal: " + aIds.toSource());
let request = aStore.get(aId);
request.onsuccess = function(aEvent) {
// Creation of the results array.
let results = new Array(aIds.length);
// We're going to create this amount of requests.
let pendingIds = aIds.length;
let indexPos = 0;
function getInternalSuccess(aEvent, aPos) {
debug("GetInternal success. Record: " + aEvent.target.result);
aResolve(ObjectWrapper.wrap(aEvent.target.result, aWindow));
};
results[aPos] = aEvent.target.result;
if (!--pendingIds) {
aCallback(results);
return;
}
if (indexPos < aIds.length) {
// Just MAX_REQUESTS requests at the same time.
let count = 0;
while (indexPos < aIds.length && ++count < MAX_REQUESTS) {
getInternalRequest();
}
}
}
function getInternalRequest() {
let currentPos = indexPos++;
let request = aStore.get(aIds[currentPos]);
request.onsuccess = function(aEvent) {
getInternalSuccess(aEvent, currentPos);
}
}
getInternalRequest();
},
updateInternal: function(aResolve, aStore, aRevisionStore, aId, aObj) {
@ -161,6 +193,17 @@ DataStore.prototype = {
};
},
getLengthInternal: function(aResolve, aStore) {
debug("GetLengthInternal");
let request = aStore.count();
request.onsuccess = function(aEvent) {
debug("GetLengthInternal success: " + aEvent.target.result);
// No wrap here because the result is always a int.
aResolve(aEvent.target.result);
};
},
addRevision: function(aRevisionStore, aId, aType, aSuccessCb) {
let self = this;
this.db.addRevision(aRevisionStore, aId, aType,
@ -205,16 +248,6 @@ DataStore.prototype = {
);
},
throwInvalidArg: function(aWindow) {
return aWindow.Promise.reject(
new aWindow.DOMError("SyntaxError", "Non-numeric or invalid id"));
},
throwReadOnly: function(aWindow) {
return aWindow.Promise.reject(
new aWindow.DOMError("ReadOnlyError", "DataStore in readonly mode"));
},
exposeObject: function(aWindow, aReadOnly) {
let self = this;
let object = {
@ -235,15 +268,18 @@ DataStore.prototype = {
},
get: function DS_get(aId) {
aId = parseInt(aId);
if (isNaN(aId) || aId <= 0) {
return self.throwInvalidArg(aWindow);
aId = this.parseIds(aId);
if (aId === null) {
return this.throwInvalidArg(aWindow);
}
// Promise<Object>
return self.newDBPromise(aWindow, "readonly",
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
self.getInternal(aWindow, aResolve, aStore, aId);
self.getInternal(aStore, Array.isArray(aId) ? aId : [ aId ],
function(aResults) {
aResolve(Array.isArray(aId) ? aResults : aResults[0]);
});
}
);
},
@ -251,11 +287,11 @@ DataStore.prototype = {
update: function DS_update(aId, aObj) {
aId = parseInt(aId);
if (isNaN(aId) || aId <= 0) {
return self.throwInvalidArg(aWindow);
return this.throwInvalidArg(aWindow);
}
if (aReadOnly) {
return self.throwReadOnly(aWindow);
return this.throwReadOnly(aWindow);
}
// Promise<void>
@ -268,7 +304,7 @@ DataStore.prototype = {
add: function DS_add(aObj) {
if (aReadOnly) {
return self.throwReadOnly(aWindow);
return this.throwReadOnly(aWindow);
}
// Promise<int>
@ -282,11 +318,11 @@ DataStore.prototype = {
remove: function DS_remove(aId) {
aId = parseInt(aId);
if (isNaN(aId) || aId <= 0) {
return self.throwInvalidArg(aWindow);
return this.throwInvalidArg(aWindow);
}
if (aReadOnly) {
return self.throwReadOnly(aWindow);
return this.throwReadOnly(aWindow);
}
// Promise<void>
@ -299,7 +335,7 @@ DataStore.prototype = {
clear: function DS_clear() {
if (aReadOnly) {
return self.throwReadOnly(aWindow);
return this.throwReadOnly(aWindow);
}
// Promise<void>
@ -410,6 +446,15 @@ DataStore.prototype = {
});
},
getLength: function DS_getLength() {
// Promise<int>
return self.newDBPromise(aWindow, "readonly",
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
self.getLengthInternal(aResolve, aStore);
}
);
},
set onchange(aCallback) {
debug("Set OnChange");
this.onchangeCb = aCallback;
@ -437,10 +482,6 @@ DataStore.prototype = {
}
},
/* TODO:
getAll(), getLength()
*/
__exposedProps__: {
name: 'r',
owner: 'r',
@ -452,11 +493,43 @@ DataStore.prototype = {
clear: 'r',
revisionId: 'r',
getChanges: 'r',
getLength: 'r',
onchange: 'rw',
addEventListener: 'r',
removeEventListener: 'r'
},
throwInvalidArg: function(aWindow) {
return aWindow.Promise.reject(
new aWindow.DOMError("SyntaxError", "Non-numeric or invalid id"));
},
throwReadOnly: function(aWindow) {
return aWindow.Promise.reject(
new aWindow.DOMError("ReadOnlyError", "DataStore in readonly mode"));
},
parseIds: function(aId) {
function parseId(aId) {
aId = parseInt(aId);
return (isNaN(aId) || aId <= 0) ? null : aId;
}
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;
},
receiveMessage: function(aMessage) {
debug("receiveMessage");

Просмотреть файл

@ -26,6 +26,8 @@ MOCHITEST_FILES = \
file_app.sjs \
file_app.template.webapp \
file_app2.template.webapp \
test_arrays.html \
file_arrays.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,108 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for DataStore - add([array]) remove([array])</title>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
var gStore;
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) {
is(stores.length, 1, "getDataStores('foo') returns 1 element");
is(stores[0].name, 'foo', 'The dataStore.name is foo');
is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
var store = stores[0];
ok("get" in store, "store.get exists");
ok("update" in store, "store.update exists");
ok("add" in store, "store.add exists");
ok("remove" in store, "store.remove exists");
ok("clear" in store, "store.clear exists");
gStore = stores[0];
runTest();
}, cbError);
}
function testStoreAdd() {
var objects = [];
for (var i = 0; i < 300; ++i) {
objects.push(i);
}
function testStoreAddInternal() {
if (!objects.length) {
ok(true, "We inserted 300 items");
runTest();
return;
}
var obj = objects.shift();
gStore.add(obj).then(function() {
ok(true, "We inserted a new item!");
testStoreAddInternal();
}, cbError);
}
testStoreAddInternal();
}
function testStoreGet() {
var objects = [];
for (var i = 1; i <= 300; ++i) {
objects.push(i);
}
gStore.get(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]);
}
runTest();
}, cbError);
}
var tests = [
// Test for GetDataStore
testGetDataStores,
// Add many items
function() { testStoreAdd() },
function() { testStoreGet() },
];
function runTest() {
if (!tests.length) {
finish();
return;
}
var test = tests.shift();
test();
}
runTest();
</script>
</body>
</html>

Просмотреть файл

@ -98,6 +98,12 @@
}, cbError);
}
function testStoreGetLength(number) {
return gStore.getLength().then(function(n) {
is(number, n, "store.getLength() returns the right number");
}, cbError);
}
function testStoreRemove(id) {
return gStore.remove(id).then(function() {
ok(true, "store.remove() is called");
@ -149,6 +155,9 @@
runTest(); }, cbError); },
function() { testStoreGet(gId, "hello world 2"); },
// getLength
function() { testStoreGetLength(3).then(function() { runTest(); }, cbError); },
// Broken remove
function() { testStoreErrorRemove('hello world'); },
function() { testStoreErrorRemove(true); },

Просмотреть файл

@ -0,0 +1,121 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for DataStore - add([array]) remove([array])</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_arrays.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]]}, runTest);
},
function() {
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();
}
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</body>
</html>