зеркало из https://github.com/mozilla/gecko-dev.git
423 строки
12 KiB
JavaScript
423 строки
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]);
|
|
|