зеркало из https://github.com/mozilla/gecko-dev.git
Bug 871445 - patch 6 - DataStore: getLength and arrays in get(), r=ehsan
This commit is contained in:
Родитель
37fce0cb23
Коммит
6d4224069a
|
@ -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>
|
Загрузка…
Ссылка в новой задаче