gecko-dev/browser/actors/EncryptedMediaChild.sys.mjs

122 lines
4.2 KiB
JavaScript

/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* 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/. */
/**
* GlobalCaptureListener is a class that listens for changes to the global
* capture state of windows and screens. It uses this information to notify
* observers if it's possible that media is being shared by these captures.
* You probably only want one instance of this class per content process.
*/
class GlobalCaptureListener {
constructor() {
Services.cpmm.sharedData.addEventListener("change", this);
// Tracks if screen capture is taking place based on shared data. Default
// to true for safety.
this._isScreenCaptured = true;
// Tracks if any windows are being captured. Default to true for safety.
this._isAnyWindowCaptured = true;
}
/**
* Updates the capture state and forces that the state is notified to
* observers even if it hasn't changed since the last update.
*/
requestUpdateAndNotify() {
this._updateCaptureState({ forceNotify: true });
}
/**
* Handle changes in shared data that may alter the capture state.
* @param event a notification that sharedData has changed. If this includes
* changes to screen or window sharing state then we'll update the capture
* state.
*/
handleEvent(event) {
if (
event.changedKeys.includes("webrtcUI:isSharingScreen") ||
event.changedKeys.includes("webrtcUI:sharedTopInnerWindowIds")
) {
this._updateCaptureState();
}
}
/**
* Updates the capture state and notifies the state to observers if the
* state has changed since last update, or if forced.
* @param forceNotify if true then the capture state will be sent to
* observers even if it didn't change since the last update.
*/
_updateCaptureState({ forceNotify = false } = {}) {
const previousCaptureState =
this._isScreenCaptured || this._isAnyWindowCaptured;
this._isScreenCaptured = Boolean(
Services.cpmm.sharedData.get("webrtcUI:isSharingScreen")
);
const capturedTopInnerWindowIds = Services.cpmm.sharedData.get(
"webrtcUI:sharedTopInnerWindowIds"
);
if (capturedTopInnerWindowIds && capturedTopInnerWindowIds.size > 0) {
this._isAnyWindowCaptured = true;
} else {
this._isAnyWindowCaptured = false;
}
const newCaptureState = this._isScreenCaptured || this._isAnyWindowCaptured;
const captureStateChanged = previousCaptureState != newCaptureState;
if (forceNotify || captureStateChanged) {
// Notify the state if the caller forces it, or if the state changed.
this._notifyCaptureState();
}
}
/**
* Notifies observers of the current capture state. Notifies observers
* with a null subject, "mediakeys-response" topic, and data that is either
* "capture-possible" or "capture-not-possible", depending on if capture is
* possible or not.
*/
_notifyCaptureState() {
const isCapturePossible =
this._isScreenCaptured || this._isAnyWindowCaptured;
const isCapturePossibleString = isCapturePossible
? "capture-possible"
: "capture-not-possible";
Services.obs.notifyObservers(
null,
"mediakeys-response",
isCapturePossibleString
);
}
}
const gGlobalCaptureListener = new GlobalCaptureListener();
export class EncryptedMediaChild extends JSWindowActorChild {
// Expected to observe 'mediakeys-request' as notified from MediaKeySystemAccess.
// @param aSubject the nsPIDOMWindowInner associated with the notifying MediaKeySystemAccess.
// @param aTopic should be "mediakeys-request".
// @param aData json containing a `status` and a `keysystem`.
observe(aSubject, aTopic, aData) {
let parsedData;
try {
parsedData = JSON.parse(aData);
} catch (ex) {
console.error("Malformed EME video message with data: ", aData);
return;
}
const { status } = parsedData;
if (status == "is-capture-possible") {
// We handle this status in process -- don't send a message to the parent.
gGlobalCaptureListener.requestUpdateAndNotify();
return;
}
this.sendAsyncMessage("EMEVideo:ContentMediaKeysRequest", aData);
}
}