tenfourfox/mobile/android/modules/MulticastDNS.jsm
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

245 lines
6.4 KiB
JavaScript

// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["MulticastDNS"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "MulticastDNS");
const FAILURE_INTERNAL_ERROR = -65537;
// Helper function for sending commands to Java.
function send(type, data, callback) {
let msg = {
type: type
};
for (let i in data) {
try {
msg[i] = data[i];
} catch (e) {
}
}
Messaging.sendRequestForResult(msg)
.then(result => callback(result, null),
err => callback(null, typeof err === "number" ? err : FAILURE_INTERNAL_ERROR));
}
// Receives service found/lost event from NsdManager
function ServiceManager() {
}
ServiceManager.prototype = {
listeners: {},
numListeners: 0,
registerEvent: function() {
log("registerEvent");
Messaging.addListener(this.onServiceFound.bind(this), "NsdManager:ServiceFound");
Messaging.addListener(this.onServiceLost.bind(this), "NsdManager:ServiceLost");
},
unregisterEvent: function() {
log("unregisterEvent");
Messaging.removeListener("NsdManager:ServiceFound");
Messaging.removeListener("NsdManager:ServiceLost");
},
addListener: function(aServiceType, aListener) {
log("addListener: " + aServiceType + ", " + aListener);
if (!this.listeners[aServiceType]) {
this.listeners[aServiceType] = [];
}
if (this.listeners[aServiceType].includes(aListener)) {
log("listener already exists");
return;
}
this.listeners[aServiceType].push(aListener);
++this.numListeners;
if (this.numListeners === 1) {
this.registerEvent();
}
log("listener added: " + this);
},
removeListener: function(aServiceType, aListener) {
log("removeListener: " + aServiceType + ", " + aListener);
if (!this.listeners[aServiceType]) {
log("listener doesn't exist");
return;
}
let index = this.listeners[aServiceType].indexOf(aListener);
if (index < 0) {
log("listener doesn't exist");
return;
}
this.listeners[aServiceType].splice(index, 1);
--this.numListeners;
if (this.numListeners === 0) {
this.unregisterEvent();
}
log("listener removed" + this);
},
onServiceFound: function(aServiceInfo) {
let listeners = this.listeners[aServiceInfo.serviceType];
if (listeners) {
for (let listener of listeners) {
listener.onServiceFound(aServiceInfo);
}
} else {
log("no listener");
}
return {};
},
onServiceLost: function(aServiceInfo) {
let listeners = this.listeners[aServiceInfo.serviceType];
if (listeners) {
for (let listener of listeners) {
listener.onServiceLost(aServiceInfo);
}
} else {
log("no listener");
}
return {};
}
};
// make an object from nsIPropertyBag2
function parsePropertyBag2(bag) {
if (!bag || !(bag instanceof Ci.nsIPropertyBag2)) {
throw new TypeError("Not a property bag");
}
let attributes = [];
let enumerator = bag.enumerator;
while (enumerator.hasMoreElements()) {
let name = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
let value = bag.getPropertyAsACString(name);
attributes.push({
"name": name,
"value": value
});
}
return attributes;
}
function MulticastDNS() {
this.serviceManager = new ServiceManager();
}
MulticastDNS.prototype = {
startDiscovery: function(aServiceType, aListener) {
this.serviceManager.addListener(aServiceType, aListener);
let serviceInfo = {
serviceType: aServiceType,
uniqueId: aListener.uuid
};
send("NsdManager:DiscoverServices", serviceInfo, (result, err) => {
if (err) {
log("onStartDiscoveryFailed: " + aServiceType + " (" + err + ")");
this.serviceManager.removeListener(aServiceType, aListener);
aListener.onStartDiscoveryFailed(aServiceType, err);
} else {
aListener.onDiscoveryStarted(result);
}
});
},
stopDiscovery: function(aServiceType, aListener) {
this.serviceManager.removeListener(aServiceType, aListener);
let serviceInfo = {
uniqueId: aListener.uuid
};
send("NsdManager:StopServiceDiscovery", serviceInfo, (result, err) => {
if (err) {
log("onStopDiscoveryFailed: " + aServiceType + " (" + err + ")");
aListener.onStopDiscoveryFailed(aServiceType, err);
} else {
aListener.onDiscoveryStopped(aServiceType);
}
});
},
registerService: function(aServiceInfo, aListener) {
let serviceInfo = {
port: aServiceInfo.port,
serviceType: aServiceInfo.serviceType,
uniqueId: aListener.uuid
};
try {
serviceInfo.host = aServiceInfo.host;
} catch(e) {
// host unspecified
}
try {
serviceInfo.serviceName = aServiceInfo.serviceName;
} catch(e) {
// serviceName unspecified
}
try {
serviceInfo.attributes = parsePropertyBag2(aServiceInfo.attributes);
} catch(e) {
// attributes unspecified
}
send("NsdManager:RegisterService", serviceInfo, (result, err) => {
if (err) {
log("onRegistrationFailed: (" + err + ")");
aListener.onRegistrationFailed(aServiceInfo, err);
} else {
aListener.onServiceRegistered(result);
}
});
},
unregisterService: function(aServiceInfo, aListener) {
let serviceInfo = {
uniqueId: aListener.uuid
};
send("NsdManager:UnregisterService", serviceInfo, (result, err) => {
if (err) {
log("onUnregistrationFailed: (" + err + ")");
aListener.onUnregistrationFailed(aServiceInfo, err);
} else {
aListener.onServiceUnregistered(aServiceInfo);
}
});
},
resolveService: function(aServiceInfo, aListener) {
send("NsdManager:ResolveService", aServiceInfo, (result, err) => {
if (err) {
log("onResolveFailed: (" + err + ")");
aListener.onResolveFailed(aServiceInfo, err);
} else {
aListener.onServiceResolved(result);
}
});
}
};