WebRTC datachannel wont send? - javascript

Lately I've been trying to implement WebRTC datachannels in Haxe, but come across a great deal of difficulty. When I use
dataChannel.send();
there appears to be no effect, despite the data channel supposedly being successfully opened.
The (extremely inefficient and messy) code I'm using looks like this:
package arm;
import haxe.Json;
import js.html.rtc.*;
import js.html.Document;
import js.html.WebSocket;
import js.html.DataElement;
#:expose
class DataChannelManager extends iron.Trait {
var user = "gobbledygook";
var first = false;
var initiator = true;
public function new() {
super();
var document = new Document();
var ws:js.html.WebSocket;
var config = {"iceServers":[{"url":"stun:stun.l.google.com:19302"}]};//temporary arrangement
//var optional:Array<Dynamic> = [{'DtlsSrtpKeyAgreement': true}, {'RtcDataChannels': true }];
// var connection:Dynamic = {
// 'optional'://try changing this to mandatory some time
// optional
// };
var peerConnection = new PeerConnection(config);
var dataChannel:js.html.rtc.DataChannel;
var ready = false;
function sendNegotiation(type, sdp) {
var json = {user:user/*, theloc:myloc*/, action: type, data: sdp};
ws.send(Json.stringify(json));
trace("Negotiation of type "+json.action);
}
var sdpConstraints = {
offerToReceiveAudio: false,
offerToReceiveVideo: false
};
var dcOpen=false;
notifyOnInit(function() {
var optionalStruct:Dynamic = {reliable: true}
dataChannel = peerConnection.createDataChannel("datachannel", optionalStruct);
dataChannel.onmessage = function(e){trace("DC message:" +e.data);};
dataChannel.onopen = function(){trace("-DC OPENED");dcOpen=true;};
dataChannel.onclose = function(){trace("-DC closed!");};
dataChannel.onerror = function(){trace("DC ERROR");};
trace("intialization!");
});
var firstfirst=true;
notifyOnUpdate(function() {
if (dcOpen) {
dcOpen=false;
trace("sending...");
dataChannel.send("stuff!");
}
if (firstfirst&&object.properties['go']) {
user=object.properties['string'];
first=true;
firstfirst=false;
// if (initiator) {
// peerConnection.createOffer(sdpConstraints).then(function (sdp) {
// peerConnection.setLocalDescription(sdp);
// sendNegotiation("offer", sdp);
// trace("SEND OFFER");
// }, function (data) {
// trace("Offer creation failure,", data);
// });
// } else {
// peerConnection.createAnswer(sdpConstraints).then(function (sdp) {
// trace("Answer made.");
// peerConnection.setLocalDescription(sdp);
// sendNegotiation("answer", sdp);
// });
// }
}
if (first) {
first=false;
ws = new WebSocket("ws://----------/*yes, there's an ip here*/:8080");
ws.onopen = function() {
trace("ws opened!");
peerConnection.onicecandidate = function(event) {
trace("ICE offer ready");
if (peerConnection==null || event ==null || event.candidate == null) return;
sendNegotiation("candidate", event.candidate);
}
if (initiator) {
trace("initiating");
// var optionalStruct:Dynamic = {reliable: true}
// dataChannel = peerConnection.createDataChannel("datachannel", optionalStruct);
// dataChannel.onmessage = function(e){trace("DC message:" +e.data);};
// dataChannel.onopen = function(){trace("-DC OPENED");dcOpen=true;};
// dataChannel.onclose = function(){trace("-DC closed!");};
// dataChannel.onerror = function(){trace("DC ERROR");};
peerConnection.createOffer(/*sdpConstraints*/).then(function (sdp) {
peerConnection.setLocalDescription(sdp);
sendNegotiation("offer", sdp);
trace("SEND OFFER");
}, function (data) {
trace("Offer creation failure,", data);
});
}
ws.onmessage = function (data) {
//var info=data.data.split()
if (data.data=="connected!") {return;}
var adata = Json.parse(data.data.substring(5));
if (adata.action=="offer") {
trace("Offer recieved.");
// var optionalStruct:Dynamic = {reliable: true}
// dataChannel = peerConnection.createDataChannel("datachannel", optionalStruct);
// dataChannel.onmessage = function(e){trace("DC message:" +e.data);};
// dataChannel.onopen = function(){trace("DC OPENED");dcOpen=true;};
// dataChannel.onclose = function(){trace("DC CLOSED");};
// dataChannel.onerror = function(){trace("DC ERROR");};
peerConnection.setRemoteDescription(/*try variations here*/ adata.data);
peerConnection.createAnswer(sdpConstraints).then(function (sdp) {
trace("Answer made.");
peerConnection.setLocalDescription(sdp);
sendNegotiation("answer", sdp);
});
}
if (adata.action=="answer") {
trace("Answer recieved.");
peerConnection.setRemoteDescription(/*try variations here*/ adata.data);
}
if (adata.action=="candidate") {
trace("ICE candidate recieved, looks like:",adata);
var soItDoesntComplain:Dynamic = adata.data;
peerConnection.addIceCandidate(soItDoesntComplain);
}
}
}
}
if (ready) {
trace("connected to net");
}
});
// notifyOnRemove(function() {
// });
}
}
You will notice a great deal of code is commented out -- I was expirementing with moving the dataChannel creation around.
For a better idea of what the issue is, here is the console output for the recieving and initiating clients, respectively:
In case you are wondering, notifyOnInit gets a function that is executed once at the beginning, and notifyOnUpdate gets a function called at a regular interval. object.properties['go'] is set by a different class when the username is given.
The JS api is basically the same (as far as I can tell, I haven't used WebRTC at all in the past), I haven't noticed any differences yet and I'm very sure that my issue is my fault and not Haxe's.
Thank you to those who answer.

this is not answer.
Anxious point.
peerCoonection.createOffer()
peerCoonection.createAnswer()
peerCoonection.setLocalDescription()
peerCoonection.setRemoteDescription()
peerCoonection.addIceCandidate()
are await is required.

Related

JS too much recursion on setInterval ajax

Greets everyone,
Update: Solved it by a workaround, changing:
$numcflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numcflinfparr['moveable_id']);
while($numcflsarr = mysqli_fetch_assoc($numcflsinfpres)) {
array_push($numcflinfparr['moveable_subs'],$numcflsarr);
}
to a function and used:
array_push($numcflinfparr[‘moveable_subs’], get_babylon_sub_object_data($numcflinfparr[‘moveable_id’]));
Old Text:
hope you can help me out with this:
I wanted to update JS object data by jquery (via $.ajax) by receiving PHP's mysqli_fetch_assoc array. It worked until I started using setInterval to update data every 15 minutes. Since then I get the following error: Uncaught (in promise) InternalError: too much recursion.
Also, tried only calling one object update (replacing for..of for a certain objectID), but I had no success. The error occurs on the line of $.ajax statement.
Edit: I could find that it occurs when an associative is pushed to array in this code:
function get_babylon_object_data($user,$id) {
$envobjects = array();
$envobjectsarr = mysqli_fetch_assoc(dbquery('SELECT object_id,object_user_id FROM '.$db_table['objects'].' WHERE object_id='.$id));
if(($envobjectsarr['object_id'] ?? 0)>0) {
$envobjectsarr['object_moveables'] = array();
if($envobjectsarr['object_user_id']==$user['id']) {
$numcflinfpres = dbquery('SELECT moveable_id,moveable_user_id,moveable_object_id,moveable_subobject_id FROM '.$db_table['moveable'].' WHERE moveable_object_id='.$envobjectsarr['object_id']);
while($numcflinfparr = mysqli_fetch_assoc($numcflinfpres)) {
$numcflinfparr['moveable_subs'] = array();
$numcflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numcflinfparr['moveable_id']);
while($numcflsarr = mysqli_fetch_assoc($numcflsinfpres)) {
array_push($numcflinfparr['moveable_subs'],$numcflsarr);
}
array_push($envobjectsarr['object_moveables'],$numcflinfparr);
}
}
$envobjectsarr['object_subobjects'] = array();
$envsubobjectsres = dbquery('SELECT * FROM '.$db_table['subobjects'].' WHERE subobject_object_id='.$envobjectsarr['object_id']);
while($envsubobjectsarr=mysqli_fetch_assoc($envsubobjectsres)) {
$envsubobjectsarr['subobject_moveables'] = array();
if($envsubobjectsarr['subobject_user_id']==$client['id']) {
$numflinfpres = dbquery('SELECT moveable_id,moveable_user_id,moveable_object_id,moveable_subobject_id FROM '.$db_table['moveable'].' WHERE moveable_subobject_id='.$envsubobjectsarr['subobject_id']);
while($numflinfparr = mysqli_fetch_assoc($numflinfpres)) {
$numflinfparr['moveable_subs'] = array();
$numflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numflinfparr['moveable_id']);
while($numflsinfparr = mysqli_fetch_assoc($numflsinfpres)) {
array_push($numflinfparr['moveable_subs'], $numflsinfparr);
}
array_push($envsubobjectsarr['subobject_moveables'], $numflinfparr);
}
}
$envobjectsarr['object_subobjects'][$envsubobjectsarr['subobject_object_pos']] = $envsubobjectsarr;
}
$envobjects[$id] = $envobjectsarr;
}
return $envobjects[$id] ?? array('object_id'=>$id);
}
Error: Uncaught (in promise) InternalError: too much recursion
aUpdateObject Updater.js:15
updateObject Updater.js:8
interval Updater.js:35
(Async: setTimeout handler)
interval Updater.js:32
(Async: setInterval handler)
initUpdater Updater.js:30
setup Central.js:29
onFinish Central.js:28
_decreaseWaitingTasksCount assetsManager.ts:1143
done assetsManager.ts:1176
_onDoneCallback assetsManager.ts:168
run assetsManager.ts:117
runTask assetsManager.ts:389
successHandler sceneLoader.ts:756
ImportMesh sceneLoader.ts:792
(Async: promise callback)
ImportMesh sceneLoader.ts:789
dataCallback sceneLoader.ts:552
GLTFFileLoader glTFFileLoader.ts:652
LoadFile fileTools.ts:343
onReadyStateChange fileTools.ts:457
(Async: EventListener.handleEvent)
addEventListener webRequest.ts:138
retryLoop fileTools.ts:482
requestFile fileTools.ts:485
RequestFile fileTools.ts:519
LoadFile fileTools.ts:342
_loadFile scene.ts:4416
_loadFile glTFFileLoader.ts:866
loadFile glTFFileLoader.ts:647
manifestChecked sceneLoader.ts:582
Database database.ts:69
OfflineProviderFactory database.ts:11
_LoadData sceneLoader.ts:603
ImportMesh sceneLoader.ts:765
runTask assetsManager.ts:379
run assetsManager.ts:114
_runTask assetsManager.ts:1194
load assetsManager.ts:1248
createScene Central.js:36
default Central.js:10
<anonym> index.js:5
InnerModuleEvaluation self-hosted:2325
evaluation self-hosted:2286
>jquery 128
isPlainObject
... (127x?) extend
File: Updater.js
export class Updater {
constructor(main) {
this.objects = {};
this.upobjects = {}; // update-requests-array
}
updateObject(id) {
if(!this.upobjects[id]) { // If no update-request ongoing
this.aUpdateObject(id);
this.upobjects[id] = id; // save update-request
}
}
async aUpdateObject(id) {
const aUpdater = this; // set to use in success function
$.ajax({
type: 'post',
url: 'path/to/updateData.php',
dataType:'json',
data: {user:aUpdater.data, id:id},
success : function (result) {
// Update Data, then Models and then UI
},
error: function(data) {
console.log('error');
console.log(data);
}
});
}
initUpdater() {
const updater = this;
this.interval = window.setInterval(function(){ // For every 15 minutes
let count = 1;
for(const object of Object.values(updater.objects)) { // For every object
setTimeout(function(){
updater.updateObject(object.id); // init single object update
},count*1000);
count++;
}
}, 900000);
}
}
File: Central.js
// Import BABYLON
// Import Updater.js
export default class {
constructor() {
this.canvas = document.getElementById('renderCanvas'); // Canvas
this.engine = new BABYLON.Engine(this.canvas, true, { stencil: true }); // prepare engine
this.scene = new BABYLON.Scene(this.engine); // init scene
this.createScene();
}
createScene() {
this.assetManager = new BABYLON.AssetsManager(this.scene); // Model Loader
this.updater = new Updater(this); // manage object updates
// this.updater.objects will be filled here and models loading is prepared
const central = this;
window.addEventListener('resize', function() {
central.engine.resize()
});
this.assetManager.onProgress = (remainingCount, totalCount, task) => {
console.log(remainingCount, totalCount, task.name);
}
this.assetManager.onFinish = (tasks) => { // If all 3d models are loaded
this.setup();
this.engine.runRenderLoop(function() {
central.scene.render(); // simulate scene
});
};
this.assetManager.load(); // Load 3d models
}
setup() {
this.updater.initUpdater();
}
}
File: index.js
// Import BABYLON
// Import Central.js
if (BABYLON.Engine.isSupported()) {
window.central = new Central(); // create Application
}
File: updateData.php
<?PHP
ini_set('MAX_EXECUTION_TIME', 1800);
define('CENTRAL_ROOT_DIR','..');
include_once(CENTRAL_ROOT_DIR.'/path/to/funcs.php'); // includes get_babylon_object_data()
include_once(CENTRAL_ROOT_DIR.'/path/to/config.php'); // includes DB config
dbconnect(); // connect to mysql DB
$conf = get_all_config(); // get app configuration
include_once(CENTRAL_ROOT_DIR.'/path/to/definitions.php'); // includes definitions
function get_babylon_object($user,$id) {
return json_encode(get_babylon_object_data($user,$id)); // get_babylon_object_data updates and returns $user (array) using mysqli_fetch_assoc to get DB data
}
echo get_babylon_object($_POST['user'] ?? array(),$_POST['id'] ?? 0);
dbclose(); // close DB
?>

Working example for signal protocol in js

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.

Decoding a string to valid JSON

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.

Global variable only accessible to first function?

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, ...);
}
});
}

JavaScript Metro app background task error

I'm trying to create a background task for my JavaScript Metro app. I added these to the default.js file:
function RegisterBackgroundTask(taskEntryPoint, taskName, trigger, condition) {
// Check for existing registrations of this background task.
var taskRegistered = false;
var background = Windows.ApplicationModel.Background;
var iter = background.BackgroundTaskRegistration.allTasks.first();
var hascur = iter.hasCurrent;
while (hascur) {
var cur = iter.current.value;
if (cur.name === taskName) {
taskRegistered = true;
break;
}
hascur = iter.moveNext();
}
// If the task is already registered, return the registration object.
if (taskRegistered == true) {
return iter.current;
}
// Register the background task.
var builder = new background.BackgroundTaskBuilder();
builder.Name = taskName;
builder.TaskEntryPoint = taskEntryPoint;
builder.setTrigger(trigger);
if (condition != null) {
builder.addCondition(condition);
}
var task = builder.register();
return task;
}
var trigger = new Windows.ApplicationModel.Background.SystemTrigger(Windows.ApplicationModel.Background.SystemTriggerType.timeZoneChange, false);
RegisterBackgroundTask("js\\bgtask.js", "test", trigger);
This is my bgtask.js:
(function () {
"use strict";
var backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
function doWork() {
// Write JavaScript code here to do work in the background.
console.log("task done");
close();
}
doWork();
})();
This is my app manifest:
When I change the timezone, nothing happened. I checked the event log and there is an error:
The background task with entry point and name failed to activate with error code 0x80040154.
What did I do wrong?
builder.Name = taskName;
builder.TaskEntryPoint = taskEntryPoint;
should be
builder.name = taskName;
builder.taskEntryPoint = taskEntryPoint;

Categories