Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  HiddenFrame.sys.mjs   Sprache: unbekannt

 
/* 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/. */

/**
 * This module contains `HiddenFrame`, a class which creates a windowless browser,
 * and `HiddenBrowserManager` which is a singleton that can be used to manage
 * creating and using multiple hidden frames.
 */

const XUL_PAGE = Services.io.newURI("chrome://global/content/win.xhtml");

const gAllHiddenFrames = new Set();

// The screen sizes to use for the background browser created by
// `HiddenBrowserManager`.
const BACKGROUND_WIDTH = 1024;
const BACKGROUND_HEIGHT = 768;

let cleanupRegistered = false;
function ensureCleanupRegistered() {
  if (!cleanupRegistered) {
    cleanupRegistered = true;
    Services.obs.addObserver(function () {
      for (let hiddenFrame of gAllHiddenFrames) {
        hiddenFrame.destroy();
      }
    }, "xpcom-shutdown");
  }
}

/**
 * A hidden frame class. It takes care of creating a windowless browser and
 * passing the window containing a blank XUL <window> back.
 */
export class HiddenFrame {
  #frame = null;
  #browser = null;
  #listener = null;
  #webProgress = null;
  #deferred = null;

  /**
   * Gets the |contentWindow| of the hidden frame. Creates the frame if needed.
   *
   * @returns {Promise} Returns a promise which is resolved when the hidden frame has finished
   *          loading.
   */
  get() {
    if (!this.#deferred) {
      this.#deferred = Promise.withResolvers();
      this.#create();
    }

    return this.#deferred.promise;
  }

  /**
   * Fetch a sync ref to the window inside the frame (needed for the add-on SDK).
   *
   * @returns {DOMWindow}
   */
  getWindow() {
    this.get();
    return this.#browser.document.ownerGlobal;
  }

  /**
   * Destroys the browser, freeing resources.
   */
  destroy() {
    if (this.#browser) {
      if (this.#listener) {
        this.#webProgress.removeProgressListener(this.#listener);
        this.#listener = null;
        this.#webProgress = null;
      }
      this.#frame = null;
      this.#deferred = null;

      gAllHiddenFrames.delete(this);
      this.#browser.close();
      this.#browser = null;
    }
  }

  #create() {
    ensureCleanupRegistered();
    let chromeFlags = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
    if (Services.appinfo.fissionAutostart) {
      chromeFlags |= Ci.nsIWebBrowserChrome.CHROME_FISSION_WINDOW;
    }
    this.#browser = Services.appShell.createWindowlessBrowser(
      true,
      chromeFlags
    );
    this.#browser.QueryInterface(Ci.nsIInterfaceRequestor);
    gAllHiddenFrames.add(this);
    this.#webProgress = this.#browser.getInterface(Ci.nsIWebProgress);
    this.#listener = {
      QueryInterface: ChromeUtils.generateQI([
        "nsIWebProgressListener",
        "nsIWebProgressListener2",
        "nsISupportsWeakReference",
      ]),
    };
    this.#listener.onStateChange = (wbp, request, stateFlags) => {
      if (!request) {
        return;
      }
      if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
        this.#webProgress.removeProgressListener(this.#listener);
        this.#listener = null;
        this.#webProgress = null;
        // Get the window reference via the document.
        this.#frame = this.#browser.document.ownerGlobal;
        this.#deferred.resolve(this.#frame);
      }
    };
    this.#webProgress.addProgressListener(
      this.#listener,
      Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT
    );
    let docShell = this.#browser.docShell;
    let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
    docShell.createAboutBlankDocumentViewer(systemPrincipal, systemPrincipal);
    let browsingContext = this.#browser.browsingContext;
    browsingContext.useGlobalHistory = false;
    let loadURIOptions = {
      triggeringPrincipal: systemPrincipal,
    };
    this.#browser.loadURI(XUL_PAGE, loadURIOptions);
  }
}

/**
 * A manager for hidden browsers. Responsible for creating and destroying a
 * hidden frame to hold them.
 */
export const HiddenBrowserManager = new (class HiddenBrowserManager {
  /**
   * The hidden frame if one has been created.
   *
   * @type {HiddenFrame | null}
   */
  #frame = null;
  /**
   * The number of hidden browser elements currently in use.
   *
   * @type {number}
   */
  #browsers = 0;

  /**
   * Creates and returns a new hidden browser.
   *
   * @returns {Browser}
   */
  async #acquireBrowser() {
    this.#browsers++;
    if (!this.#frame) {
      this.#frame = new HiddenFrame();
    }

    let frame = await this.#frame.get();
    let doc = frame.document;
    let browser = doc.createXULElement("browser");
    browser.setAttribute("remote", "true");
    browser.setAttribute("type", "content");
    browser.setAttribute(
      "style",
      `
        width: ${BACKGROUND_WIDTH}px;
        min-width: ${BACKGROUND_WIDTH}px;
        height: ${BACKGROUND_HEIGHT}px;
        min-height: ${BACKGROUND_HEIGHT}px;
      `
    );
    browser.setAttribute("maychangeremoteness", "true");
    doc.documentElement.appendChild(browser);

    return browser;
  }

  /**
   * Releases the given hidden browser.
   *
   * @param {Browser} browser
   *   The hidden browser element.
   */
  #releaseBrowser(browser) {
    browser.remove();

    this.#browsers--;
    if (this.#browsers == 0) {
      this.#frame.destroy();
      this.#frame = null;
    }
  }

  /**
   * Calls a callback function with a new hidden browser.
   * This function will return whatever the callback function returns.
   *
   * @param {Callback} callback
   *   The callback function will be called with the browser element and may
   *   be asynchronous.
   * @returns {T}
   */
  async withHiddenBrowser(callback) {
    let browser = await this.#acquireBrowser();
    try {
      return await callback(browser);
    } finally {
      this.#releaseBrowser(browser);
    }
  }
})();

[ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge