EncryptedMediaChild.sys.mjs (4337B)
1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /** 7 * GlobalCaptureListener is a class that listens for changes to the global 8 * capture state of windows and screens. It uses this information to notify 9 * observers if it's possible that media is being shared by these captures. 10 * You probably only want one instance of this class per content process. 11 */ 12 class GlobalCaptureListener { 13 constructor() { 14 Services.cpmm.sharedData.addEventListener("change", this); 15 // Tracks if screen capture is taking place based on shared data. Default 16 // to true for safety. 17 this._isScreenCaptured = true; 18 // Tracks if any windows are being captured. Default to true for safety. 19 this._isAnyWindowCaptured = true; 20 } 21 22 /** 23 * Updates the capture state and forces that the state is notified to 24 * observers even if it hasn't changed since the last update. 25 */ 26 requestUpdateAndNotify() { 27 this._updateCaptureState({ forceNotify: true }); 28 } 29 30 /** 31 * Handle changes in shared data that may alter the capture state. 32 * 33 * @param event a notification that sharedData has changed. If this includes 34 * changes to screen or window sharing state then we'll update the capture 35 * state. 36 */ 37 handleEvent(event) { 38 if ( 39 event.changedKeys.includes("webrtcUI:isSharingScreen") || 40 event.changedKeys.includes("webrtcUI:sharedTopInnerWindowIds") 41 ) { 42 this._updateCaptureState(); 43 } 44 } 45 46 /** 47 * Updates the capture state and notifies the state to observers if the 48 * state has changed since last update, or if forced. 49 * 50 * @param forceNotify if true then the capture state will be sent to 51 * observers even if it didn't change since the last update. 52 */ 53 _updateCaptureState({ forceNotify = false } = {}) { 54 const previousCaptureState = 55 this._isScreenCaptured || this._isAnyWindowCaptured; 56 57 this._isScreenCaptured = Boolean( 58 Services.cpmm.sharedData.get("webrtcUI:isSharingScreen") 59 ); 60 61 const capturedTopInnerWindowIds = Services.cpmm.sharedData.get( 62 "webrtcUI:sharedTopInnerWindowIds" 63 ); 64 if (capturedTopInnerWindowIds && capturedTopInnerWindowIds.size > 0) { 65 this._isAnyWindowCaptured = true; 66 } else { 67 this._isAnyWindowCaptured = false; 68 } 69 const newCaptureState = this._isScreenCaptured || this._isAnyWindowCaptured; 70 71 const captureStateChanged = previousCaptureState != newCaptureState; 72 73 if (forceNotify || captureStateChanged) { 74 // Notify the state if the caller forces it, or if the state changed. 75 this._notifyCaptureState(); 76 } 77 } 78 79 /** 80 * Notifies observers of the current capture state. Notifies observers 81 * with a null subject, "mediakeys-response" topic, and data that is either 82 * "capture-possible" or "capture-not-possible", depending on if capture is 83 * possible or not. 84 */ 85 _notifyCaptureState() { 86 const isCapturePossible = 87 this._isScreenCaptured || this._isAnyWindowCaptured; 88 const isCapturePossibleString = isCapturePossible 89 ? "capture-possible" 90 : "capture-not-possible"; 91 Services.obs.notifyObservers( 92 null, 93 "mediakeys-response", 94 isCapturePossibleString 95 ); 96 } 97 } 98 99 const gGlobalCaptureListener = new GlobalCaptureListener(); 100 101 export class EncryptedMediaChild extends JSWindowActorChild { 102 // Expected to observe 'mediakeys-request' as notified from MediaKeySystemAccess. 103 // @param aSubject the nsPIDOMWindowInner associated with the notifying MediaKeySystemAccess. 104 // @param aTopic should be "mediakeys-request". 105 // @param aData json containing a `status` and a `keysystem`. 106 observe(aSubject, aTopic, aData) { 107 let parsedData; 108 try { 109 parsedData = JSON.parse(aData); 110 } catch (ex) { 111 console.error("Malformed EME video message with data: ", aData); 112 return; 113 } 114 const { status } = parsedData; 115 if (status == "is-capture-possible") { 116 // We handle this status in process -- don't send a message to the parent. 117 gGlobalCaptureListener.requestUpdateAndNotify(); 118 return; 119 } 120 121 this.sendAsyncMessage("EMEVideo:ContentMediaKeysRequest", aData); 122 } 123 }