tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }