tor-browser

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

DOMFullscreenChild.sys.mjs (6508B)


      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 export class DOMFullscreenChild extends JSWindowActorChild {
      7  receiveMessage(aMessage) {
      8    let window = this.contentWindow;
      9    let windowUtils = window?.windowUtils;
     10 
     11    switch (aMessage.name) {
     12      case "DOMFullscreen:Entered": {
     13        if (!windowUtils) {
     14          // If we are not able to enter fullscreen, tell the parent to just
     15          // exit.
     16          this.sendAsyncMessage("DOMFullscreen:Exit", {});
     17          break;
     18        }
     19 
     20        let remoteFrameBC = aMessage.data.remoteFrameBC;
     21        if (remoteFrameBC) {
     22          let remoteFrame = remoteFrameBC.embedderElement;
     23          if (!remoteFrame) {
     24            // This could happen when the page navigate away and trigger a
     25            // process switching during fullscreen transition, tell the parent
     26            // to just exit.
     27            this.sendAsyncMessage("DOMFullscreen:Exit", {});
     28            break;
     29          }
     30          this._isNotTheRequestSource = true;
     31          windowUtils.remoteFrameFullscreenChanged(remoteFrame);
     32        } else {
     33          this._waitForMozAfterPaint = true;
     34          this._lastTransactionId = windowUtils.lastTransactionId;
     35          if (
     36            !windowUtils.handleFullscreenRequests() &&
     37            !this.document.fullscreenElement
     38          ) {
     39            // If we don't actually have any pending fullscreen request
     40            // to handle, neither we have been in fullscreen, tell the
     41            // parent to just exit.
     42            this.sendAsyncMessage("DOMFullscreen:Exit", {});
     43          }
     44        }
     45        break;
     46      }
     47      case "DOMFullscreen:CleanUp": {
     48        let isNotTheRequestSource = !!aMessage.data.remoteFrameBC;
     49        // If we've exited fullscreen at this point, no need to record
     50        // transaction id or call exit fullscreen. This is especially
     51        // important for pre-e10s, since in that case, it is possible
     52        // that no more paint would be triggered after this point.
     53        if (this.document.fullscreenElement) {
     54          this._isNotTheRequestSource = isNotTheRequestSource;
     55          // Need to wait for the MozAfterPaint after exiting fullscreen if
     56          // this is the request source.
     57          this._waitForMozAfterPaint = !this._isNotTheRequestSource;
     58          // windowUtils could be null if the associated window is not current
     59          // active window. In this case, document must be in the process of
     60          // exiting fullscreen, it is okay to not ask it to exit fullscreen.
     61          if (windowUtils) {
     62            this._lastTransactionId = windowUtils.lastTransactionId;
     63            windowUtils.exitFullscreen();
     64          }
     65        } else if (isNotTheRequestSource) {
     66          // If we are not the request source and have exited fullscreen, reply
     67          // Exited to parent as parent is waiting for our reply.
     68          this.sendAsyncMessage("DOMFullscreen:Exited", {});
     69        } else {
     70          // If we've already exited fullscreen, it is possible that no more
     71          // paint would be triggered, so don't wait for MozAfterPaint.
     72          // TODO: There might be some way to move this code around a bit to
     73          //       make it easier to follow. Somehow handle the "local" case in
     74          //       one place and the isNotTheRequestSource case after that.
     75          this.sendAsyncMessage("DOMFullscreen:Painted", {});
     76        }
     77        break;
     78      }
     79      case "DOMFullscreen:Painted": {
     80        Services.obs.notifyObservers(window, "fullscreen-painted");
     81        break;
     82      }
     83    }
     84  }
     85 
     86  handleEvent(aEvent) {
     87    if (this.hasBeenDestroyed()) {
     88      // Make sure that this actor is alive before going further because
     89      // if it's not the case, any attempt to send a message or access
     90      // objects such as 'contentWindow' will fail. (See bug 1590138)
     91      return;
     92    }
     93 
     94    switch (aEvent.type) {
     95      case "MozDOMFullscreen:Request": {
     96        this.sendAsyncMessage("DOMFullscreen:Request", {});
     97        break;
     98      }
     99      case "MozDOMFullscreen:NewOrigin": {
    100        this.sendAsyncMessage("DOMFullscreen:NewOrigin", {
    101          originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix,
    102        });
    103        break;
    104      }
    105      case "MozDOMFullscreen:Exit": {
    106        this.sendAsyncMessage("DOMFullscreen:Exit", {});
    107        break;
    108      }
    109      case "MozDOMFullscreen:Entered":
    110      case "MozDOMFullscreen:Exited": {
    111        if (this._isNotTheRequestSource) {
    112          // Fullscreen change event for a frame in the
    113          // middle (content frame embedding the oop frame where the
    114          // request comes from)
    115 
    116          delete this._isNotTheRequestSource;
    117          this.sendAsyncMessage(aEvent.type.replace("Moz", ""), {});
    118          break;
    119        }
    120 
    121        if (this._waitForMozAfterPaint) {
    122          delete this._waitForMozAfterPaint;
    123          this._listeningWindow = this.contentWindow.windowRoot;
    124          this._listeningWindow.addEventListener("MozAfterPaint", this);
    125        }
    126 
    127        if (!this.document || !this.document.fullscreenElement) {
    128          // If we receive any fullscreen change event, and find we are
    129          // actually not in fullscreen, also ask the parent to exit to
    130          // ensure that the parent always exits fullscreen when we do.
    131          this.sendAsyncMessage("DOMFullscreen:Exit", {});
    132        }
    133        break;
    134      }
    135      case "MozAfterPaint": {
    136        // Only send Painted signal after we actually finish painting
    137        // the transition for the fullscreen change.
    138        // Note that this._lastTransactionId is not set when in pre-e10s
    139        // mode, so we need to check that explicitly.
    140        if (
    141          !this._lastTransactionId ||
    142          aEvent.transactionId > this._lastTransactionId
    143        ) {
    144          this._listeningWindow.removeEventListener("MozAfterPaint", this);
    145          delete this._listeningWindow;
    146          this.sendAsyncMessage("DOMFullscreen:Painted", {});
    147        }
    148        break;
    149      }
    150    }
    151  }
    152 
    153  hasBeenDestroyed() {
    154    // The 'didDestroy' callback is not always getting called.
    155    // So we can't rely on it here. Instead, we will try to access
    156    // the browsing context to judge wether the actor has
    157    // been destroyed or not.
    158    try {
    159      return !this.browsingContext;
    160    } catch {
    161      return true;
    162    }
    163  }
    164 }