gecko/dom/fm/DOMFMRadioChild.js
Randy Lin ext:(%20and%20Chris%20Jones%20%3Cjones.chris.g%40gmail.com%3E) ec98b2cc5e Bug 815452: Hook up FM radio to the audio manager. r=sicking a=blocking-basecamp
2012-12-11 01:13:08 -08:00

423 lines
12 KiB
JavaScript

/* 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"
let DEBUG = 0;
if (DEBUG)
debug = function (s) { dump("-*- DOMFMRadioChild: " + s + "\n"); };
else
debug = function (s) { };
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
const DOMFMMANAGER_CONTRACTID = "@mozilla.org/domfmradio;1";
const DOMFMMANAGER_CID = Components.ID("{901f8a83-03a6-4be9-bb8f-35387d3849da}");
XPCOMUtils.defineLazyGetter(Services, "DOMRequest", function() {
return Cc["@mozilla.org/dom/dom-request-service;1"]
.getService(Ci.nsIDOMRequestService);
});
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
function DOMFMRadioChild() { }
DOMFMRadioChild.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
classID: DOMFMMANAGER_CID,
classInfo: XPCOMUtils.generateCI({
classID: DOMFMMANAGER_CID,
contractID: DOMFMMANAGER_CONTRACTID,
classDescription: "DOMFMRadio",
interfaces: [Ci.nsIDOMFMRadio],
flags: Ci.nsIClassInfo.DOM_OBJECT
}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMFMRadio,
Ci.nsIDOMGlobalPropertyInitializer]),
// nsIDOMGlobalPropertyInitializer implementation
init: function(aWindow) {
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
let perm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "fmradio");
this._hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
if (!this._hasPrivileges) {
Cu.reportError("NO FMRADIO PERMISSION FOR: " + aWindow.document.nodePrincipal.origin + "\n");
return null;
}
const messages = ["DOMFMRadio:enable:Return:OK",
"DOMFMRadio:enable:Return:NO",
"DOMFMRadio:disable:Return:OK",
"DOMFMRadio:disable:Return:NO",
"DOMFMRadio:setFrequency:Return:OK",
"DOMFMRadio:setFrequency:Return:NO",
"DOMFMRadio:seekUp:Return:OK",
"DOMFMRadio:seekUp:Return:NO",
"DOMFMRadio:seekDown:Return:OK",
"DOMFMRadio:seekDown:Return:NO",
"DOMFMRadio:cancelSeek:Return:OK",
"DOMFMRadio:cancelSeek:Return:NO",
"DOMFMRadio:frequencyChange",
"DOMFMRadio:powerStateChange",
"DOMFMRadio:antennaChange"];
this.initHelper(aWindow, messages);
let els = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
els.addSystemEventListener(aWindow, "visibilitychange",
this._updateVisibility.bind(this),
/* useCapture = */ true);
this._visibility = aWindow.document.visibilityState;
// Unlike the |enabled| getter, this is true if *this* DOM window
// has successfully enabled the FM radio more recently than
// disabling it.
this._haveEnabledRadio = false;
},
// Called from DOMRequestIpcHelper
uninit: function() {
this._onFrequencyChange = null;
this._onAntennaChange = null;
this._onDisabled = null;
this._onEnabled = null;
},
_createEvent: function(name) {
return new this._window.Event(name);
},
_sendMessageForRequest: function(name, data, request) {
let id = this.getRequestId(request);
cpmm.sendAsyncMessage(name, {
data: data,
rid: id,
mid: this._id
});
},
_fireFrequencyChangeEvent: function() {
let e = this._createEvent("frequencychange");
if (this._onFrequencyChange) {
this._onFrequencyChange.handleEvent(e);
}
this.dispatchEvent(e);
},
_firePowerStateChangeEvent: function() {
let _enabled = this.enabled;
debug("Current power state: " + _enabled);
if (_enabled) {
let e = this._createEvent("enabled");
if (this._onEnabled) {
this._onEnabled.handleEvent(e);
}
this.dispatchEvent(e);
} else {
let e = this._createEvent("disabled");
if (this._onDisabled) {
this._onDisabled.handleEvent(e);
}
this.dispatchEvent(e);
}
},
_fireAntennaAvailableChangeEvent: function() {
let e = this._createEvent("antennaavailablechange");
if (this._onAntennaChange) {
this._onAntennaChange.handleEvent(e);
}
this.dispatchEvent(e);
},
_updateVisibility: function(evt) {
this._visibility = evt.target.visibilityState;
// Only notify visibility state when we "own" the radio stream.
if (this._haveEnabledRadio) {
this._notifyVisibility();
}
},
_notifyVisibility: function() {
cpmm.sendAsyncMessage("DOMFMRadio:updateVisibility", this._visibility);
},
receiveMessage: function(aMessage) {
let msg = aMessage.json;
if (msg.mid && msg.mid != this._id) {
return;
}
let request;
switch (aMessage.name) {
case "DOMFMRadio:enable:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:enable:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "Failed to turn on the FM radio");
break;
case "DOMFMRadio:disable:Return:OK":
this._haveEnabledRadio = false;
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:disable:Return:NO":
// If disabling the radio failed, but the hardware is still
// on, this DOM window is still responsible for the continued
// playback.
this._haveEnabledRadio = this.enabled;
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request,
"Failed to turn off the FM radio");
break;
case "DOMFMRadio:setFrequency:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:setFrequency:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request,
"Failed to set the FM radio frequency");
break;
case "DOMFMRadio:seekUp:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:seekUp:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "FM radio seek-up failed");
break;
case "DOMFMRadio:seekDown:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:seekDown:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "FM radio seek-down failed");
break;
case "DOMFMRadio:cancelSeek:Return:OK":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireSuccess(request, null);
break;
case "DOMFMRadio:cancelSeek:Return:NO":
request = this.takeRequest(msg.rid);
if (!request) {
return;
}
Services.DOMRequest.fireError(request, "Failed to cancel seek");
break;
case "DOMFMRadio:powerStateChange":
this._firePowerStateChangeEvent();
break;
case "DOMFMRadio:frequencyChange":
this._fireFrequencyChangeEvent();
break;
case "DOMFMRadio:antennaChange":
this._fireAntennaAvailableChangeEvent();
break;
}
},
_call: function(name, arg) {
var request = this.createRequest();
this._sendMessageForRequest("DOMFMRadio:" + name, arg, request);
return request;
},
// nsIDOMFMRadio
get enabled() {
return cpmm.sendSyncMessage("DOMFMRadio:getPowerState")[0];
},
get antennaAvailable() {
return cpmm.sendSyncMessage("DOMFMRadio:getAntennaState")[0];
},
get frequency() {
return cpmm.sendSyncMessage("DOMFMRadio:getFrequency")[0];
},
get frequencyUpperBound() {
let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
return range.upper;
},
get frequencyLowerBound() {
let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
return range.lower;
},
get channelWidth() {
let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
return range.channelWidth;
},
set onantennaavailablechange(callback) {
this._onAntennaChange = callback;
},
set onenabled(callback) {
this._onEnabled = callback;
},
set ondisabled(callback) {
this._onDisabled = callback;
},
set onfrequencychange(callback) {
this._onFrequencyChange = callback;
},
disable: function nsIDOMFMRadio_disable() {
return this._call("disable", null);
},
enable: function nsIDOMFMRadio_enable(frequency) {
// FMRadio::Enable() needs the most recent visibility state
// synchronously.
this._haveEnabledRadio = true;
this._notifyVisibility();
return this._call("enable", frequency);
},
setFrequency: function nsIDOMFMRadio_setFreq(frequency) {
return this._call("setFrequency", frequency);
},
seekDown: function nsIDOMFMRadio_seekDown() {
return this._call("seekDown", null);
},
seekUp: function nsIDOMFMRadio_seekUp() {
return this._call("seekUp", null);
},
cancelSeek: function nsIDOMFMRadio_cancelSeek() {
return this._call("cancelSeek", null);
},
// These are fake implementations, will be replaced by using
// nsJSDOMEventTargetHelper, see bug 731746
addEventListener: function(type, listener, useCapture) {
if (!this._eventListenersByType) {
this._eventListenersByType = {};
}
if (!listener) {
return;
}
var listeners = this._eventListenersByType[type];
if (!listeners) {
listeners = this._eventListenersByType[type] = [];
}
useCapture = !!useCapture;
for (let i = 0, len = listeners.length; i < len; i++) {
let l = listeners[i];
if (l && l.listener === listener && l.useCapture === useCapture) {
return;
}
}
listeners.push({
listener: listener,
useCapture: useCapture
});
},
removeEventListener: function(type, listener, useCapture) {
if (!this._eventListenersByType) {
return;
}
useCapture = !!useCapture;
var listeners = this._eventListenersByType[type];
if (listeners) {
for (let i = 0, len = listeners.length; i < len; i++) {
let l = listeners[i];
if (l && l.listener === listener && l.useCapture === useCapture) {
listeners.splice(i, 1);
}
}
}
},
dispatchEvent: function(evt) {
if (!this._eventListenersByType) {
return;
}
let type = evt.type;
var listeners = this._eventListenersByType[type];
if (listeners) {
for (let i = 0, len = listeners.length; i < len; i++) {
let listener = listeners[i].listener;
try {
if (typeof listener == "function") {
listener.call(this, evt);
} else if (listener && listener.handleEvent &&
typeof listener.handleEvent == "function") {
listener.handleEvent(evt);
}
} catch (e) {
debug("Exception is caught: " + e);
}
}
}
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFMRadioChild]);