diff options
Diffstat (limited to '')
-rw-r--r-- | browser/actors/EncryptedMediaChild.sys.mjs | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/browser/actors/EncryptedMediaChild.sys.mjs b/browser/actors/EncryptedMediaChild.sys.mjs new file mode 100644 index 0000000000..7db643df67 --- /dev/null +++ b/browser/actors/EncryptedMediaChild.sys.mjs @@ -0,0 +1,121 @@ +/* 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); + } +} |