191 lines
5.5 KiB
JavaScript
191 lines
5.5 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/. */
|
|
|
|
import { GeckoViewUtils } from "resource://gre/modules/GeckoViewUtils.sys.mjs";
|
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(
|
|
lazy,
|
|
"MediaManagerService",
|
|
"@mozilla.org/mediaManagerService;1",
|
|
"nsIMediaManagerService"
|
|
);
|
|
|
|
const STATUS_RECORDING = "recording";
|
|
const STATUS_INACTIVE = "inactive";
|
|
const TYPE_CAMERA = "camera";
|
|
const TYPE_MICROPHONE = "microphone";
|
|
|
|
export class GeckoViewPermissionProcessChild extends JSProcessActorChild {
|
|
getActor(window) {
|
|
return window.windowGlobalChild.getActor("GeckoViewPermission");
|
|
}
|
|
|
|
/* ---------- nsIObserver ---------- */
|
|
async observe(aSubject, aTopic, aData) {
|
|
switch (aTopic) {
|
|
case "getUserMedia:ask-device-permission": {
|
|
await this.sendQuery("AskDevicePermission", aData);
|
|
Services.obs.notifyObservers(
|
|
aSubject,
|
|
"getUserMedia:got-device-permission"
|
|
);
|
|
break;
|
|
}
|
|
case "getUserMedia:request": {
|
|
const { callID } = aSubject;
|
|
const allowedDevices = await this.handleMediaRequest(aSubject);
|
|
Services.obs.notifyObservers(
|
|
allowedDevices,
|
|
allowedDevices
|
|
? "getUserMedia:response:allow"
|
|
: "getUserMedia:response:deny",
|
|
callID
|
|
);
|
|
break;
|
|
}
|
|
case "PeerConnection:request": {
|
|
Services.obs.notifyObservers(
|
|
null,
|
|
"PeerConnection:response:allow",
|
|
aSubject.callID
|
|
);
|
|
break;
|
|
}
|
|
case "recording-device-events": {
|
|
this.handleRecordingDeviceEvents(aSubject);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
handleRecordingDeviceEvents(aRequest) {
|
|
aRequest.QueryInterface(Ci.nsIPropertyBag2);
|
|
const contentWindow = aRequest.getProperty("window");
|
|
const devices = [];
|
|
|
|
const getStatusString = function (activityStatus) {
|
|
switch (activityStatus) {
|
|
case lazy.MediaManagerService.STATE_CAPTURE_ENABLED:
|
|
case lazy.MediaManagerService.STATE_CAPTURE_DISABLED:
|
|
return STATUS_RECORDING;
|
|
case lazy.MediaManagerService.STATE_NOCAPTURE:
|
|
return STATUS_INACTIVE;
|
|
default:
|
|
throw new Error("Unexpected activityStatus value");
|
|
}
|
|
};
|
|
|
|
const hasCamera = {};
|
|
const hasMicrophone = {};
|
|
const screen = {};
|
|
const window = {};
|
|
const browser = {};
|
|
const mediaDevices = {};
|
|
lazy.MediaManagerService.mediaCaptureWindowState(
|
|
contentWindow,
|
|
hasCamera,
|
|
hasMicrophone,
|
|
screen,
|
|
window,
|
|
browser,
|
|
mediaDevices
|
|
);
|
|
var cameraStatus = getStatusString(hasCamera.value);
|
|
var microphoneStatus = getStatusString(hasMicrophone.value);
|
|
if (hasCamera.value != lazy.MediaManagerService.STATE_NOCAPTURE) {
|
|
devices.push({
|
|
type: TYPE_CAMERA,
|
|
status: cameraStatus,
|
|
});
|
|
}
|
|
if (hasMicrophone.value != lazy.MediaManagerService.STATE_NOCAPTURE) {
|
|
devices.push({
|
|
type: TYPE_MICROPHONE,
|
|
status: microphoneStatus,
|
|
});
|
|
}
|
|
this.getActor(contentWindow).mediaRecordingStatusChanged(devices);
|
|
}
|
|
|
|
async handleMediaRequest(aRequest) {
|
|
const constraints = aRequest.getConstraints();
|
|
const { devices, windowID } = aRequest;
|
|
const window = Services.wm.getOuterWindowWithId(windowID);
|
|
if (window.closed) {
|
|
return null;
|
|
}
|
|
|
|
// Release the request first
|
|
aRequest = undefined;
|
|
|
|
const sources = devices.map(device => {
|
|
device = device.QueryInterface(Ci.nsIMediaDevice);
|
|
return {
|
|
type: device.type,
|
|
id: device.rawId,
|
|
rawId: device.rawId,
|
|
name: device.rawName, // unfiltered device name to show to the user
|
|
mediaSource: device.mediaSource,
|
|
};
|
|
});
|
|
|
|
if (
|
|
constraints.video &&
|
|
!sources.some(source => source.type === "videoinput")
|
|
) {
|
|
console.error("Media device error: no video source");
|
|
return null;
|
|
} else if (
|
|
constraints.audio &&
|
|
!sources.some(source => source.type === "audioinput")
|
|
) {
|
|
console.error("Media device error: no audio source");
|
|
return null;
|
|
}
|
|
|
|
const response = await this.getActor(window).getMediaPermission({
|
|
uri: window.document.documentURI,
|
|
video: constraints.video
|
|
? sources.filter(source => source.type === "videoinput")
|
|
: null,
|
|
audio: constraints.audio
|
|
? sources.filter(source => source.type === "audioinput")
|
|
: null,
|
|
});
|
|
|
|
if (!response) {
|
|
// Rejected.
|
|
return null;
|
|
}
|
|
|
|
const allowedDevices = Cc["@mozilla.org/array;1"].createInstance(
|
|
Ci.nsIMutableArray
|
|
);
|
|
if (constraints.video) {
|
|
const video = devices.find(device => response.video === device.rawId);
|
|
if (!video) {
|
|
console.error("Media device error: invalid video id");
|
|
return null;
|
|
}
|
|
await this.getActor(window).addCameraPermission();
|
|
allowedDevices.appendElement(video);
|
|
}
|
|
if (constraints.audio) {
|
|
const audio = devices.find(device => response.audio === device.rawId);
|
|
if (!audio) {
|
|
console.error("Media device error: invalid audio id");
|
|
return null;
|
|
}
|
|
allowedDevices.appendElement(audio);
|
|
}
|
|
return allowedDevices;
|
|
}
|
|
}
|
|
|
|
const { debug, warn } = GeckoViewUtils.initLogging(
|
|
"GeckoViewPermissionProcessChild"
|
|
);
|