gecko-dev/browser/actors/EncryptedMediaChild.jsm
Bryce Seager van Dyk d9f34a3187 Bug 1706164 - Use webrtc screen and window sharing data in EncryptedMediaChild to determine if media may be captured. r=alwu
This expands EncryptedMediaChild to be able to query and track the information
now shared by webrtcUI around screen and window sharing. The actor will use this
information to report to observers if capture is possible or not.

For now we use a coarse approach where we only track if the screen or any window
is shared, but in future I plan to have a more granular tracking of windows.

Differential Revision: https://phabricator.services.mozilla.com/D115417
2021-05-24 16:50:19 +00:00

127 lines
4.4 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["EncryptedMediaChild"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
/**
* 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();
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) {
Cu.reportError("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);
}
}