summaryrefslogtreecommitdiffstats
path: root/mobile/android/actors/GeckoViewPermissionProcessChild.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/actors/GeckoViewPermissionProcessChild.sys.mjs')
-rw-r--r--mobile/android/actors/GeckoViewPermissionProcessChild.sys.mjs191
1 files changed, 191 insertions, 0 deletions
diff --git a/mobile/android/actors/GeckoViewPermissionProcessChild.sys.mjs b/mobile/android/actors/GeckoViewPermissionProcessChild.sys.mjs
new file mode 100644
index 0000000000..2c9d271bbd
--- /dev/null
+++ b/mobile/android/actors/GeckoViewPermissionProcessChild.sys.mjs
@@ -0,0 +1,191 @@
+/* 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"
+);