Bug 1869043 add a main thread record of track audio outputs r=padenot
[gecko.git] / mobile / android / actors / GeckoViewPermissionProcessChild.sys.mjs
blob2c9d271bbd091f334b5833761784880b68e8fdca
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 import { GeckoViewUtils } from "resource://gre/modules/GeckoViewUtils.sys.mjs";
6 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
8 const lazy = {};
10 XPCOMUtils.defineLazyServiceGetter(
11   lazy,
12   "MediaManagerService",
13   "@mozilla.org/mediaManagerService;1",
14   "nsIMediaManagerService"
17 const STATUS_RECORDING = "recording";
18 const STATUS_INACTIVE = "inactive";
19 const TYPE_CAMERA = "camera";
20 const TYPE_MICROPHONE = "microphone";
22 export class GeckoViewPermissionProcessChild extends JSProcessActorChild {
23   getActor(window) {
24     return window.windowGlobalChild.getActor("GeckoViewPermission");
25   }
27   /* ----------  nsIObserver  ---------- */
28   async observe(aSubject, aTopic, aData) {
29     switch (aTopic) {
30       case "getUserMedia:ask-device-permission": {
31         await this.sendQuery("AskDevicePermission", aData);
32         Services.obs.notifyObservers(
33           aSubject,
34           "getUserMedia:got-device-permission"
35         );
36         break;
37       }
38       case "getUserMedia:request": {
39         const { callID } = aSubject;
40         const allowedDevices = await this.handleMediaRequest(aSubject);
41         Services.obs.notifyObservers(
42           allowedDevices,
43           allowedDevices
44             ? "getUserMedia:response:allow"
45             : "getUserMedia:response:deny",
46           callID
47         );
48         break;
49       }
50       case "PeerConnection:request": {
51         Services.obs.notifyObservers(
52           null,
53           "PeerConnection:response:allow",
54           aSubject.callID
55         );
56         break;
57       }
58       case "recording-device-events": {
59         this.handleRecordingDeviceEvents(aSubject);
60         break;
61       }
62     }
63   }
65   handleRecordingDeviceEvents(aRequest) {
66     aRequest.QueryInterface(Ci.nsIPropertyBag2);
67     const contentWindow = aRequest.getProperty("window");
68     const devices = [];
70     const getStatusString = function (activityStatus) {
71       switch (activityStatus) {
72         case lazy.MediaManagerService.STATE_CAPTURE_ENABLED:
73         case lazy.MediaManagerService.STATE_CAPTURE_DISABLED:
74           return STATUS_RECORDING;
75         case lazy.MediaManagerService.STATE_NOCAPTURE:
76           return STATUS_INACTIVE;
77         default:
78           throw new Error("Unexpected activityStatus value");
79       }
80     };
82     const hasCamera = {};
83     const hasMicrophone = {};
84     const screen = {};
85     const window = {};
86     const browser = {};
87     const mediaDevices = {};
88     lazy.MediaManagerService.mediaCaptureWindowState(
89       contentWindow,
90       hasCamera,
91       hasMicrophone,
92       screen,
93       window,
94       browser,
95       mediaDevices
96     );
97     var cameraStatus = getStatusString(hasCamera.value);
98     var microphoneStatus = getStatusString(hasMicrophone.value);
99     if (hasCamera.value != lazy.MediaManagerService.STATE_NOCAPTURE) {
100       devices.push({
101         type: TYPE_CAMERA,
102         status: cameraStatus,
103       });
104     }
105     if (hasMicrophone.value != lazy.MediaManagerService.STATE_NOCAPTURE) {
106       devices.push({
107         type: TYPE_MICROPHONE,
108         status: microphoneStatus,
109       });
110     }
111     this.getActor(contentWindow).mediaRecordingStatusChanged(devices);
112   }
114   async handleMediaRequest(aRequest) {
115     const constraints = aRequest.getConstraints();
116     const { devices, windowID } = aRequest;
117     const window = Services.wm.getOuterWindowWithId(windowID);
118     if (window.closed) {
119       return null;
120     }
122     // Release the request first
123     aRequest = undefined;
125     const sources = devices.map(device => {
126       device = device.QueryInterface(Ci.nsIMediaDevice);
127       return {
128         type: device.type,
129         id: device.rawId,
130         rawId: device.rawId,
131         name: device.rawName, // unfiltered device name to show to the user
132         mediaSource: device.mediaSource,
133       };
134     });
136     if (
137       constraints.video &&
138       !sources.some(source => source.type === "videoinput")
139     ) {
140       console.error("Media device error: no video source");
141       return null;
142     } else if (
143       constraints.audio &&
144       !sources.some(source => source.type === "audioinput")
145     ) {
146       console.error("Media device error: no audio source");
147       return null;
148     }
150     const response = await this.getActor(window).getMediaPermission({
151       uri: window.document.documentURI,
152       video: constraints.video
153         ? sources.filter(source => source.type === "videoinput")
154         : null,
155       audio: constraints.audio
156         ? sources.filter(source => source.type === "audioinput")
157         : null,
158     });
160     if (!response) {
161       // Rejected.
162       return null;
163     }
165     const allowedDevices = Cc["@mozilla.org/array;1"].createInstance(
166       Ci.nsIMutableArray
167     );
168     if (constraints.video) {
169       const video = devices.find(device => response.video === device.rawId);
170       if (!video) {
171         console.error("Media device error: invalid video id");
172         return null;
173       }
174       await this.getActor(window).addCameraPermission();
175       allowedDevices.appendElement(video);
176     }
177     if (constraints.audio) {
178       const audio = devices.find(device => response.audio === device.rawId);
179       if (!audio) {
180         console.error("Media device error: invalid audio id");
181         return null;
182       }
183       allowedDevices.appendElement(audio);
184     }
185     return allowedDevices;
186   }
189 const { debug, warn } = GeckoViewUtils.initLogging(
190   "GeckoViewPermissionProcessChild"