Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / toolkit / modules / HiddenFrame.sys.mjs
blob74fe755796b229d3c1e06487ebacbafa4cdc9cf9
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const XUL_PAGE = Services.io.newURI("chrome://global/content/win.xhtml");
7 const gAllHiddenFrames = new Set();
9 let cleanupRegistered = false;
10 function ensureCleanupRegistered() {
11   if (!cleanupRegistered) {
12     cleanupRegistered = true;
13     Services.obs.addObserver(function () {
14       for (let hiddenFrame of gAllHiddenFrames) {
15         hiddenFrame.destroy();
16       }
17     }, "xpcom-shutdown");
18   }
21 /**
22  * An hidden frame object. It takes care of creating a windowless browser and
23  * passing the window containing a blank XUL <window> back.
24  */
25 export function HiddenFrame() {}
27 HiddenFrame.prototype = {
28   _frame: null,
29   _browser: null,
30   _listener: null,
31   _webProgress: null,
32   _deferred: null,
34   /**
35    * Gets the |contentWindow| of the hidden frame. Creates the frame if needed.
36    * @returns Promise Returns a promise which is resolved when the hidden frame has finished
37    *          loading.
38    */
39   get() {
40     if (!this._deferred) {
41       this._deferred = Promise.withResolvers();
42       this._create();
43     }
45     return this._deferred.promise;
46   },
48   /**
49    * Fetch a sync ref to the window inside the frame (needed for the add-on SDK).
50    */
51   getWindow() {
52     this.get();
53     return this._browser.document.ownerGlobal;
54   },
56   destroy() {
57     if (this._browser) {
58       if (this._listener) {
59         this._webProgress.removeProgressListener(this._listener);
60         this._listener = null;
61         this._webProgress = null;
62       }
63       this._frame = null;
64       this._deferred = null;
66       gAllHiddenFrames.delete(this);
67       this._browser.close();
68       this._browser = null;
69     }
70   },
72   _create() {
73     ensureCleanupRegistered();
74     let chromeFlags = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
75     if (Services.appinfo.fissionAutostart) {
76       chromeFlags |= Ci.nsIWebBrowserChrome.CHROME_FISSION_WINDOW;
77     }
78     this._browser = Services.appShell.createWindowlessBrowser(
79       true,
80       chromeFlags
81     );
82     this._browser.QueryInterface(Ci.nsIInterfaceRequestor);
83     gAllHiddenFrames.add(this);
84     this._webProgress = this._browser.getInterface(Ci.nsIWebProgress);
85     this._listener = {
86       QueryInterface: ChromeUtils.generateQI([
87         "nsIWebProgressListener",
88         "nsIWebProgressListener2",
89         "nsISupportsWeakReference",
90       ]),
91     };
92     this._listener.onStateChange = (wbp, request, stateFlags) => {
93       if (!request) {
94         return;
95       }
96       if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
97         this._webProgress.removeProgressListener(this._listener);
98         this._listener = null;
99         this._webProgress = null;
100         // Get the window reference via the document.
101         this._frame = this._browser.document.ownerGlobal;
102         this._deferred.resolve(this._frame);
103       }
104     };
105     this._webProgress.addProgressListener(
106       this._listener,
107       Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT
108     );
109     let docShell = this._browser.docShell;
110     let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
111     docShell.createAboutBlankDocumentViewer(systemPrincipal, systemPrincipal);
112     let browsingContext = this._browser.browsingContext;
113     browsingContext.useGlobalHistory = false;
114     let loadURIOptions = {
115       triggeringPrincipal: systemPrincipal,
116     };
117     this._browser.loadURI(XUL_PAGE, loadURIOptions);
118   },