Hi there i try to make i clientside message exchange with the signal protocol (only for tests). But i have trouble to the proccessPreKey.
Here is the test code
<script src="javascripts/libsignal-protocol.js"></script>
<script src="javascripts/InMemorySignalProtocolStore.js"></script>
<script>
var KeyHelperUser1 = libsignal.KeyHelper;
var KeyHelperUser2 = libsignal.KeyHelper;
var registrationId_User1 = KeyHelperUser1.generateRegistrationId();
var registrationId_User2 = KeyHelperUser2.generateRegistrationId();
// Store registrationId somewhere durable and safe.
var identityKeyPair_User1, identityKeyPair_User2;
var SignedPreKey_User1, SignedPreKey_User2;
// Test Store
var store_User1 = new SignalProtocolStore();
var store_User2 = new SignalProtocolStore();
var PreKey_User1, PreKey_User2;
// Build the session
var address_User1 = new libsignal.SignalProtocolAddress(1002, 0);
var address_User2 = new libsignal.SignalProtocolAddress(1001, 0);
var sessionBuilder_User1 = new libsignal.SessionBuilder(store_User1, address_User1);
var sessionBuilder_User2 = new libsignal.SessionBuilder(store_User2, address_User2);
KeyHelperUser1.generateIdentityKeyPair().then(function(identityKeyPair) {
// keyPair -> { pubKey: ArrayBuffer, privKey: ArrayBuffer }
// Store identityKeyPair somewhere durable and safe.
identityKeyPair_User1 = identityKeyPair;
KeyHelperUser1.generatePreKey(1001).then(function(preKey) {
//store.storePreKey(preKey.keyId, preKey.keyPair);
PreKey_User1 = preKey;
KeyHelperUser2.generatePreKey(1002).then(function(preKey) {
//store.storePreKey(preKey.keyId, preKey.keyPair);
PreKey_User2 = preKey;
KeyHelperUser1.generateSignedPreKey(identityKeyPair_User1, 1001).then(function(signedPreKey) {
store_User1.storeSignedPreKey(signedPreKey.keyId, signedPreKey.keyPair);
SignedPreKey_User1 = signedPreKey;
KeyHelperUser2.generateIdentityKeyPair().then(function(identityKeyPair) {
// keyPair -> { pubKey: ArrayBuffer, privKey: ArrayBuffer }
// Store identityKeyPair somewhere durable and safe.
identityKeyPair_User2 = identityKeyPair;
KeyHelperUser2.generateSignedPreKey(identityKeyPair_User2, 1002).then(function(signedPreKey) {
store_User2.storeSignedPreKey(signedPreKey.keyId, signedPreKey.keyPair);
SignedPreKey_User2 = signedPreKey;
var promise_User1 = sessionBuilder_User1.processPreKey({
registrationId: registrationId_User2,
identityKey: identityKeyPair_User2.pubKey,
signedPreKey: {
keyId : 1002,
publicKey : SignedPreKey_User2.pubKey,
signature : SignedPreKey_User2.signature
},
preKey: {
keyId : 1002,
publicKey : PreKey_User1.pubKey
}
});
promise_User1.catch(function onerror(error) {
// handle identity key conflict
//console.log(error);
});
});
});
});
});
});
});
</script>
I don't really know which parameters the processPreKey wanted. Can someone help?
Hey so this is something i put together, hope it helps.
var KeyHelper = libsignal.KeyHelper;
function generateIdentity(store) {
return Promise.all([
KeyHelper.generateIdentityKeyPair(),
KeyHelper.generateRegistrationId(),
]).then(function(result) {
store.put('identityKey', result[0]);
store.put('registrationId', result[1]);
});
}
function generatePreKeyBundle(store, preKeyId, signedPreKeyId) {
return Promise.all([
store.getIdentityKeyPair(),
store.getLocalRegistrationId()
]).then(function(result) {
var identity = result[0];
var registrationId = result[1];
return Promise.all([
KeyHelper.generatePreKey(preKeyId),
KeyHelper.generateSignedPreKey(identity, signedPreKeyId),
]).then(function(keys) {
var preKey = keys[0]
var signedPreKey = keys[1];
store.storePreKey(preKeyId, preKey.keyPair);
store.storeSignedPreKey(signedPreKeyId, signedPreKey.keyPair);
return {
identityKey: identity.pubKey,
registrationId : registrationId,
preKey: {
keyId : preKeyId,
publicKey : preKey.keyPair.pubKey
},
signedPreKey: {
keyId : signedPreKeyId,
publicKey : signedPreKey.keyPair.pubKey,
signature : signedPreKey.signature
}
};
});
});
}
var ALICE_ADDRESS = new libsignal.SignalProtocolAddress("xxxxxxxxx", 1);
var BOB_ADDRESS = new libsignal.SignalProtocolAddress("yyyyyyyyyyyyy", 1);
var aliceStore = new libsignal.SignalProtocolStore();
var bobStore = new libsignal.SignalProtocolStore();
var bobPreKeyId = 1337;
var bobSignedKeyId = 1;
var Curve = libsignal.Curve;
Promise.all([
generateIdentity(aliceStore),
generateIdentity(bobStore),
]).then(function() {
return generatePreKeyBundle(bobStore, bobPreKeyId, bobSignedKeyId);
}).then(function(preKeyBundle) {
var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS);
return builder.processPreKey(preKeyBundle).then(function() {
var originalMessage = util.toArrayBuffer("my message ......");
var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS);
var bobSessionCipher = new libsignal.SessionCipher(bobStore, ALICE_ADDRESS);
aliceSessionCipher.encrypt(originalMessage).then(function(ciphertext) {
// check for ciphertext.type to be 3 which includes the PREKEY_BUNDLE
return bobSessionCipher.decryptPreKeyWhisperMessage(ciphertext.body, 'binary');
}).then(function(plaintext) {
alert(plaintext);
});
bobSessionCipher.encrypt(originalMessage).then(function(ciphertext) {
return aliceSessionCipher.decryptWhisperMessage(ciphertext.body, 'binary');
}).then(function(plaintext) {
assertEqualArrayBuffers(plaintext, originalMessage);
});
});
});
Not enough rep to comment; just addressing the comments below the answer.
#Niczem Olaske, the util.toArrayBuffer([message]) can be removed. The returned ciphertext is already an arrayBuffer, and converting the message to an arrayBuffer prior to encryption creates the need to convert it back to a string at the end anyway. However, a global 'util' object must be declared in order for libsignal to work (at least in JavaScript). So you can just put: const util = {}; somewhere outside function bodies, or alternatively: Object.assign(window, { util: {} }); before using libsignal.
#Hasib Mahmud, this error is from Bob trying to encrypt a message to Alice when a session has not yet been established. If you use async/await instead of .then(() => etc...), you will avoid this problem. Bob must decrypt Alice's message first in order to establish a session with her, and then encrypt messages to her. Note that he needs to use the 'decryptPreKeyWhisperMessage' method in order to use her preKey to establish the session whilst calling the method. After the session has been established on both sides, he can use the 'decryptWhisperMessage' method.
I am writing an add-on for Firefox. I'm catching a JSON file which should contain general user data which I need. To clarify this, the data is accessible without any security boundaries. Sometimes the caught string is a valid JSON (without any encoding at all).
And sometimes it looks like this:
Does anyone have an idea how to decode this with JavaScript or at least an idea which kind of encoding this is?
EDIT:
The correct data would be something like:
{"type":"success","message":"OK","data":{"myPersonas":[{"picture":"","personaId":"382046730","games":{"1":"3078"},"personaName":"Feirell","updatedAt":1423573314,"userId":"2832659177456727338","clanTag":"","originalPersona":{"picture":"","userId":"2832659177456727338","user":null,"updatedAt":1423573314,"firstPartyId":"","personaId":"382046730","personaName":"Feirell","gamesLegacy":"0","namespace":"cem_ea_id","gamesJson":"{\"1\":\"3078\"}","games":{"1":"3078"},"clanTag":""},"namespace":"cem_ea_id"}],"currentRankNeeded":{"name":"WARSAW_ID_P_RANK127_NAME","level":127,"pointsNeeded":20260000,"texture":"UI\/Art\/Persistence\/Ranks\/Rank127","iconImageConfig":{"category":"rank_icon","slug":"r127","texture":"UI\/Art\/Persistence\/ranks\/IconsScoreboard\/r127","versions":{"small":{"path":"warsaw\/gamedata\/rank_icon\/small.png","isSprite":true,"height":23,"name":"small","width":29},"smallns":{"path":"warsaw\/gamedata\/rank_icon\/smallns\/r127.png","isSprite":false,"height":23,"name":"smallns","width":29}}},"guid":"384D858D-8288-4B60-9C35-234B65157873","imageConfig":{"category":"rank","slug":"r127","texture":"UI\/Art\/Persistence\/Ranks\/Rank127","versions":{"large":{"path":"warsaw\/gamedata\/rank\/large\/r127.png","isSprite":false,"height":256,"name":"large","width":256},"smallinv":{"path":"warsaw\/gamedata\/rank\/smallinv.png","isSprite":true,"height":64,"name":"smallinv","width":64},"medium":{"path":"warsaw\/gamedata\/rank\/medium.png","isSprite":true,"height":128,"name":"medium","width":128},"tiny":{"path":"warsaw\/gamedata\/rank\/tiny.png","isSprite":true,"height":29,"name":"tiny","width":29},"tinyinv":{"path":"warsaw\/gamedata\/rank\/tinyinv.png","isSprite":true,"height":29,"name":"tinyinv","width":29},"small":{"path":"warsaw\/gamedata\/rank\/small.png","isSprite":true,"height":64,"name":"small","width":64},"mediumns":{"path":"warsaw\/gamedata\/rank\/mediumns\/r127.png","isSprite":false,"height":128,"name":"mediumns","width":128},"smallns":{"path":"warsaw\/gamedata\/rank\/smallns\/r127.png","isSprite":false,"height":64,"name":"smallns","width":64}}}},"generalPersonaStats":{"PDWHeadshots":0.0,"serviceStarsProgress":{"8":9.1730769231,"1":81.6516129032,"2":13.7603053435,"2048":38.81,"32":93.2388059701},"vehiclesDestroyed":2229,"mortarUser":null,"cs_kills":null,"rsScore":0,"PDWKills":0.0,"gameModesScore":{"32":"32800","1":"5274720","2":"184170","67108864":"12635","64":"5274720","68719476736":"5274720","524288":"21675","34359738368":"21410","2097152":"21675","8":"32800","8388608":"5790","512":0,"134217728":"12635","1024":"33840","33554432":"12635","16777216":"17000","137438953472":"21675"},"sc_hotwire":null,"gameModeServiceStarsGold":{},"rank":127,"rushWins":null,"sc_enforcer":null,"sc_vehicle":3337250,"cp_kills":null,"kitMaxMeleeKillsInRound":{},"cp_deaths":null,"motionSensorSpots":null,"cp_wins":null,"sniperRifleAccuracy":0.282155948,"sc_heist":null,"sc_team":532411,"skill":248,"nemesisKills":429,"heals":13978,"sc_operator":null,"kitMeleeKills":{},"longestHeadshot":694.02,"kills_assault":12436,"conquestWlr":null,"elo":0,"longestWinStreak":null,"sc_unlock":109000,"mcomDefendKills":9,"wlRatio":0.0,"sc_award":7289700,"cp_skill":null,"vehiclesDestroyedAssists":null,"revives":3866,"kitLosses":{},"squadDMWins":null,"rushWlr":null,"LMGKills":1494,"cashEarned":null,"kitKills":{},"rocketLauncherAccuracy":0.0,"kitMaxScoreInRound":{},"kills":23207,"sc_capturetheflag":null,"kdRatio":1.5,"sc_bonus":344311,"assaultRifleAccuracy":0.1521077163,"kitDeaths":{},"squadRushLosses":null,"PDWAccuracy":0.0,"flagDefend":3535,"LMGHeadshots":332,"kitWins":{},"nemesisStreak":18,"numWins":726,"flagrunner":null,"conquestWins":null,"scorePerMinuteDelta":0,"kitHeadshots":{},"rsKills":0,"sc_objective":0,"kitDeployments":{},"rsTimePlayed":0,"vehicleScores":{"32":"206936","1":0,"2":"1957700","4":"153005","8":"20374","16":"727908"},"antiGroundSoldier":162.0,"timeDead":null,"sc_bloodmoney":null,"kdRatioDelta":null,"quitPercentage":23.0852211435,"rsNumLosses":0,"sc_squad":1855100,"vehicleDamage":2523,"rsShotsHit":0,"sc_bountyhunter":null,"spm_engineer":null,"flagCaptures":5521,"kitKillStreak":{},"serviceStars":{"8":10,"1":42,"2":6,"2048":10,"32":7},"dogtagsTaken":185,"sc_turfwar":null,"scoreMultiplier":0,"kitLongestHeadshot":{},"deaths":15470,"reDeploys":null,"sc_professional":null,"clubRepution":null,"sniperRifleHeadshots":855,"killAssists":2259,"LMGAccuracy":0.120160849,"rsDeaths":0,"maxMeleeKillsInRound":null,"tdmWins":null,"gameModeServiceStarsSilver":{},"cashPerMinute":null,"rushLosses":null,"resupplies":2476,"totalScore":20500085,"sc_hacker":null,"headshots":4532,"repairs":618,"shotsFired":1099510,"avengerKills":2075,"squadRushWlr":null,"conquestLosses":null,"cs_deaths":null,"rsNumWins":0,"gameModeServiceStarsBronze":{},"squadDmWlr":null,"spm_support":null,"maxHeadshotsInRound":null,"score":20500085,"kills_recon":2451,"kitTimes":{"8":"210746","1":"708950","2":"118946","2048":"46278","32":"171331"},"timePlayedDelta":0,"squadRushWins":null,"sc_hit":null,"kitTimesInPercentage":{"8":16.7757876412,"1":56.4337859234,"2":9.4683307715,"2048":3.6838179631,"32":13.6382777009},"rankScore":20495300,"kitMaxKillsInRound":{},"accuracy":15.6488799556,"timePlayed":2171290,"sc_squadheist":null,"assaultRifleHeadshots":2450,"kitScores":{"8":"1049540","1":"6636560","2":"804026","2048":"207762","32":"1062940"},"suppressionAssists":1660,"scoreDelta":0,"kills_support":2848,"kitMaxHeadshotsInRound":{},"kills_engineer":1562,"cs_wins":null,"killsPerMinute":0.64,"sniperRifleKills":1880,"scorePerMinute":566,"timePlayedSinceLastReset":0,"winPercentage":null,"tdmWlr":null,"rsScorePerMinute":0.0,"combatScore":13098078,"tdmLosses":null,"sc_mechanic":null,"numRounds":1854,"antiAirSoldier":10.0,"sc_hostage":null,"sc_commander":null,"maxKillsInRound":null,"killStreakBonus":58,"lastReset":0,"sc_deathmatch":null,"cs_skill":null,"spm_recon":null,"rsShotsFired":0,"mcomDestroy":0.0,"shotsHit":172061,"serviceStarsGameModes":[{"serviceStars":251,"serviceStarsProgress":17.7142857143,"progressCodeNeeded":null,"actualValue":5274720,"codeNeeded":"sc_conquest","tier":null,"valueNeeded":5292000,"name":null},{"serviceStars":12,"serviceStarsProgress":27.8,"progressCodeNeeded":null,"actualValue":184170,"codeNeeded":"sc_rush","tier":null,"valueNeeded":195000,"name":null},{"serviceStars":5,"serviceStarsProgress":46.6666666667,"progressCodeNeeded":null,"actualValue":32800,"codeNeeded":"sc_deathmatch","tier":null,"valueNeeded":36000,"name":null},{"serviceStars":3,"serviceStarsProgress":77.7777777778,"progressCodeNeeded":null,"actualValue":17000,"codeNeeded":"sc_elimination","tier":null,"valueNeeded":18000,"name":null}],"numLosses":700,"spm_assault":null,"cp_loses":null,"sc_general":4577110,"saviorKills":1813,"cs_loses":null,"squadDMLosses":null,"maxScoreInRound":null,"meleeKills":null,"assaultRifleKills":11309},"rankNeeded":{"name":"WARSAW_ID_P_RANK128_NAME","level":128,"pointsNeeded":21030000,"texture":"UI\/Art\/Persistence\/Ranks\/Rank128","iconImageConfig":{"category":"rank_icon","slug":"r128","texture":"UI\/Art\/Persistence\/ranks\/IconsScoreboard\/r128","versions":{"small":{"path":"warsaw\/gamedata\/rank_icon\/small.png","isSprite":true,"height":23,"name":"small","width":29},"smallns":{"path":"warsaw\/gamedata\/rank_icon\/smallns\/r128.png","isSprite":false,"height":23,"name":"smallns","width":29}}},"guid":"9AC7B2DF-C736-4721-BE14-61F75A4AB816","imageConfig":{"category":"rank","slug":"r128","texture":"UI\/Art\/Persistence\/Ranks\/Rank128","versions":{"large":{"path":"warsaw\/gamedata\/rank\/large\/r128.png","isSprite":false,"height":256,"name":"large","width":256},"smallinv":{"path":"warsaw\/gamedata\/rank\/smallinv.png","isSprite":true,"height":64,"name":"smallinv","width":64},"medium":{"path":"warsaw\/gamedata\/rank\/medium.png","isSprite":true,"height":128,"name":"medium","width":128},"tiny":{"path":"warsaw\/gamedata\/rank\/tiny.png","isSprite":true,"height":29,"name":"tiny","width":29},"tinyinv":{"path":"warsaw\/gamedata\/rank\/tinyinv.png","isSprite":true,"height":29,"name":"tinyinv","width":29},"small":{"path":"warsaw\/gamedata\/rank\/small.png","isSprite":true,"height":64,"name":"small","width":64},"mediumns":{"path":"warsaw\/gamedata\/rank\/mediumns\/r128.png","isSprite":false,"height":128,"name":"mediumns","width":128},"smallns":{"path":"warsaw\/gamedata\/rank\/smallns\/r128.png","isSprite":false,"height":64,"name":"smallns","width":64}}}}}}
the code for the catching:
//CODE FROM: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/NsITraceableChannel#Example
let { CC, Ci, Cu } = require("chrome");
Cu.import('resource://gre/modules/Services.jsm');
var BinaryInputStream = CC('#mozilla.org/binaryinputstream;1', 'nsIBinaryInputStream', 'setInputStream');
var BinaryOutputStream = CC('#mozilla.org/binaryoutputstream;1', 'nsIBinaryOutputStream', 'setOutputStream');
var StorageStream = CC('#mozilla.org/storagestream;1', 'nsIStorageStream', 'init');
function TracingListener() {
this.receivedChunks = []; // array for incoming data. holds chunks as they come, onStopRequest we join these junks to get the full source
this.responseBody; // we'll set this to the
this.responseStatusCode;
this.deferredDone = {
promise: null,
resolve: null,
reject: null
};
this.deferredDone.promise = new Promise(function(resolve, reject) {
this.resolve = resolve;
this.reject = reject;
}.bind(this.deferredDone));
Object.freeze(this.deferredDone);
this.promiseDone = this.deferredDone.promise;
}
TracingListener.prototype = {
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
var iStream = new BinaryInputStream(aInputStream) // binaryaInputStream
var sStream = new StorageStream(8192, aCount, null); // storageStream // not sure why its 8192 but thats how eveyrone is doing it, we should ask why
var oStream = new BinaryOutputStream(sStream.getOutputStream(0)); // binaryOutputStream
// Copy received data as they come.
var data = iStream.readBytes(aCount);
this.receivedChunks.push(data);
oStream.writeBytes(data, aCount);
this.originalListener.onDataAvailable(aRequest, aContext, sStream.newInputStream(0), aOffset, aCount);
},
onStartRequest: function(aRequest, aContext) {
this.originalListener.onStartRequest(aRequest, aContext);
},
onStopRequest: function(aRequest, aContext, aStatusCode) {
this.responseBody = this.receivedChunks.join("");
delete this.receivedChunks;
this.responseStatus = aStatusCode;
this.originalListener.onStopRequest(aRequest, aContext, aStatusCode);
this.deferredDone.resolve();
},
QueryInterface: function(aIID) {
if (aIID.equals(Ci.nsIStreamListener) || aIID.equals(Ci.nsISupports)) {
return this;
}
throw Cr.NS_NOINTERFACE;
}
};
//my Battlereport Listener
function battlereportListener(callback){
got_battlereport = callback
}
battlereportListener.prototype.httpResponseObserver = {
observe: function(aSubject, aTopic, aData) {
aSubject.QueryInterface(Ci.nsIHttpChannel);
let url = aSubject.URI.spec;
if(url.indexOf("http://battlelog.battlefield.com/bf4/indexstats") != -1){
//console.log('Found a battlereport !!')
var newListener = new TracingListener();
aSubject.QueryInterface(Ci.nsITraceableChannel);
newListener.originalListener = aSubject.setNewListener(newListener);
newListener.promiseDone.then(
function() {
// no error happened
got_battlereport(newListener.responseBody)
},
function(aReason){
// promise was rejected, right now i didnt set up rejection, but i should listen to on abort or bad status code then reject maybe
}
).catch(
function(aCatch) {
console.error('something went wrong, a typo by dev probably:', aCatch);
}
);
}
}
};
battlereportListener.prototype.state = false
battlereportListener.prototype.start = function(){
if(this.state == false){
Services.obs.addObserver(this.httpResponseObserver, 'http-on-examine-response', false);
this.state = true;
}
};
battlereportListener.prototype.stop = function(){
if(this.state == true){
Services.obs.removeObserver(this.httpResponseObserver, 'http-on-examine-response');
this.state = false;
}
};
exports.battlereportListener = battlereportListener
I just tested it with Firefox and it works fine. However, with Firefox Developer Edition it comes to this binary code.
I have an OK understanding of JS but am ultimately still learning. I'm trying to recreate a PHP/mySQL project over to IndexedDB and can't work out why I'm seeing an error here.
The IndexedDB connection works as expected. The first function (createItem) functions fine, however the second function (getItems) is returning an error claiming that the "db" variable is undefined. It's a global variable so should be accessible by the function's scope, and the createItem function has no problem using it. Can anyone help me see what I've missed here.
// GLOBAL DB VAR
var db;
// WAIT FOR DOM
document.addEventListener("DOMContentLoaded", function(){
// IF INDEXED DB CAPABLE
if("indexedDB" in window) {
// OPEN DB
var openRequest = indexedDB.open("newTabTest",1);
// CREATE NEW / UPDATE
openRequest.onupgradeneeded = function(e) {
// Notify user here: creating database (first time use)
var thisDB = e.target.result;
// Create "items" table if it doesn't already exist
if(!thisDB.objectStoreNames.contains("items")) {
var store = thisDB.createObjectStore("items", {keyPath: "id", autoIncrement: true});
store.createIndex("name","name", {unique:true});
store.createIndex("folder","folder", {unique:false});
store.createIndex("dial","dial", {unique:false});
}
}
openRequest.onsuccess = function(e) {
// Success- set db to target result.
db = e.target.result;
}
openRequest.onerror = function(e) {
// DB ERROR :-(
}
}
},false);
// CREATE ITEM FUNCTION
function createItem(n,u,f,c) {
var transaction = db.transaction(["items"],"readwrite");
var store = transaction.objectStore("items");
var item = {
name: n,
url: u,
folder: f,
colour: c,
dial: 0,
order: 100
}
var request = store.add(item);
request.onerror = function(e) {
console.log("An error occured.");
}
request.onsuccess = function(e) {
console.log("Successfully added.")
}
};
// GET ITEM(S) FUNCTION
// Specify index and key value OR omit for all
function getItems(callback, ind, key) {
var transaction = db.transaction(["items"],"readonly");
var store = transaction.objectStore("items");
var response = [];
// If args are omitted - grab all items
if(!ind | !key) {
var cursor = store.openCursor();
cursor.onsuccess = function(e) {
var res = e.target.result;
if(res) {
var r = {
"name": res.value['name'],
"url": res.value['url'],
"folder": res.value['folder'],
"colour": res.value['colour'],
"dial": res.value['dial'],
"order": res.value['order']
};
response.push(r);
res.continue();
}
}
cursor.oncomplete = function() {
callback(response);
}
} else {
// If both args are specified query specified index
store = store.index(ind);
var range = IDBKeyRange.bound(key, key);
store.openCursor(range).onsuccess = function(e) {
var res = e.target.result;
if(res) {
var r = {
"name": res.value['name'],
"url": res.value['url'],
"folder": res.value['folder'],
"colour": res.value['colour'],
"dial": res.value['dial'],
"order": res.value['order']
};
response.push(r);
res.continue();
}
}
cursor.oncomplete = function() {
callback(response);
}
}
};
As you've figured out in comments, you do have to stick the db-dependent actions inside a function called from a success handler.
This callback-based programming quickly becomes a pain, and a common solution for that is to use promises.
However, making IndexedDB work with promises is still work-in-progress (see https://github.com/inexorabletash/indexeddb-promises if you're interested).
If your goal is to get something done (and not to learn the bare IndexedDB APIs), perhaps you'd be better off finding a wrapper library for IndexedDB (can't recommend one though, since I've not tried working seriously with IDB yet).
I would not recommend using a variable like 'db' as you have in your example. If you are new to reading and writing asynchronous Javascript, you are just going to cause yourself a lot of pain. There are better ways to do it. It takes several pages to explain and is explained in many other questions on StackOverflow, so instead, very briefly, consider rewriting your code to do something like the following:
function createItem(db, ...) {
var tx = db.transaction(...);
// ...
}
function openIndexedDBThenCreateItem(...) {
var openRequest = indexedDB.open(...);
openRequest.onsuccess = function(event) {
var db = event.target.result;
createItem(db, ...);
};
}
function getItems(db, callback, ...) {
var tx = db.transaction(...);
var items = [];
tx.oncomplete = function(event) {
callback(items);
};
// ...
var request = store.openCursor(...);
request.onsuccess = function(event) {
var request = event.target;
var cursor = event.target.result;
if(cursor) {
var item = cursor.value;
items.push(item);
cursor.continue();
}
};
}
function openIndexedDBThenGetItems(db, callback, ...) {
var openRequest = indexedDB.open(...);
openRequest.onsuccess = function(event) {
var db = event.target.result;
getItems(db, callback, ...);
};
}
Also, you don't need to wait for DOMContentLoaded to start using indexedDB. It is immediately available.
If you get the above code, then you can consider a further improvement of adding a simple helper function:
function openIndexedDB(callback) {
var openRequest = indexedDB.open(...);
openRequest.onerror = callback;
openRequest.onsuccess = callback;
}
And then rewrite the examples like this:
function openIndexedDBThenCreateItem(...) {
openIndexedDB(function onOpen(event) {
if(event.type !== 'success') {
console.error(event);
} else {
var db = event.target.result;
createItem(db, ...);
}
});
}
function openIndexedDBThenGetItems(...) {
openIndexedDB(function onOpen(event) {
if(event.type !== 'success') {
console.error(event);
} else {
var db = event.target.result;
getItems(db, ...);
}
});
}