Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / remote / shared / Prompt.sys.mjs
blob39292d4bfd399ac2c9487246287764a2c5b6cd2f
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 lazy = {};
7 ChromeUtils.defineESModuleGetters(lazy, {
8   AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
9   Log: "chrome://remote/content/shared/Log.sys.mjs",
10 });
12 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
14 const COMMON_DIALOG = "chrome://global/content/commonDialog.xhtml";
16 /** @namespace */
17 export const modal = {
18   ACTION_CLOSED: "closed",
19   ACTION_OPENED: "opened",
22 /**
23  * Check for already existing modal or tab modal dialogs and
24  * return the first one.
25  *
26  * @param {browser.Context} context
27  *     Reference to the browser context to check for existent dialogs.
28  *
29  * @returns {modal.Dialog}
30  *     Returns instance of the Dialog class, or `null` if no modal dialog
31  *     is present.
32  */
33 modal.findPrompt = function (context) {
34   // First check if there is a modal dialog already present for the
35   // current browser window.
36   for (let win of Services.wm.getEnumerator(null)) {
37     // TODO: Use BrowserWindowTracker.getTopWindow for modal dialogs without
38     // an opener.
39     if (
40       win.document.documentURI === COMMON_DIALOG &&
41       win.opener &&
42       win.opener === context.window
43     ) {
44       lazy.logger.trace("Found open window modal prompt");
45       return new modal.Dialog(win);
46     }
47   }
49   if (lazy.AppInfo.isAndroid) {
50     const geckoViewPrompts = context.window.prompts();
51     if (geckoViewPrompts.length) {
52       lazy.logger.trace("Found open GeckoView prompt");
53       const prompt = geckoViewPrompts[0];
54       return new modal.Dialog(prompt);
55     }
56   }
58   const contentBrowser = context.contentBrowser;
60   // If no modal dialog has been found yet, also check for tab and content modal
61   // dialogs for the current tab.
62   //
63   // TODO: Find an adequate implementation for Firefox on Android (bug 1708105)
64   if (contentBrowser?.tabDialogBox) {
65     let dialogs = contentBrowser.tabDialogBox.getTabDialogManager().dialogs;
66     if (dialogs.length) {
67       lazy.logger.trace("Found open tab modal prompt");
68       return new modal.Dialog(dialogs[0].frameContentWindow);
69     }
71     dialogs = contentBrowser.tabDialogBox.getContentDialogManager().dialogs;
73     // Even with the dialog manager handing back a dialog, the `Dialog` property
74     // gets lazily added. If it's not set yet, ignore the dialog for now.
75     if (dialogs.length && dialogs[0].frameContentWindow.Dialog) {
76       lazy.logger.trace("Found open content prompt");
77       return new modal.Dialog(dialogs[0].frameContentWindow);
78     }
79   }
80   return null;
83 /**
84  * Represents a modal dialog.
85  *
86  * @param {DOMWindow} dialog
87  *     DOMWindow of the dialog.
88  */
89 modal.Dialog = class {
90   #win;
92   constructor(dialog) {
93     this.#win = Cu.getWeakReference(dialog);
94   }
96   get args() {
97     if (lazy.AppInfo.isAndroid) {
98       return this.window.args;
99     }
100     let tm = this.tabModal;
101     return tm ? tm.args : null;
102   }
104   get isOpen() {
105     if (lazy.AppInfo.isAndroid) {
106       return this.window !== null;
107     }
108     if (!this.ui) {
109       return false;
110     }
111     return true;
112   }
114   get isWindowModal() {
115     return [
116       Services.prompt.MODAL_TYPE_WINDOW,
117       Services.prompt.MODAL_TYPE_INTERNAL_WINDOW,
118     ].includes(this.args.modalType);
119   }
121   get tabModal() {
122     return this.window?.Dialog;
123   }
125   get promptType() {
126     const promptType = this.args.promptType;
128     if (promptType === "confirmEx" && this.args.inPermitUnload) {
129       return "beforeunload";
130     }
132     return promptType;
133   }
135   get ui() {
136     let tm = this.tabModal;
137     return tm ? tm.ui : null;
138   }
140   /**
141    * For Android, this returns a GeckoViewPrompter, which can be used to control prompts.
142    * Otherwise, this returns the ChromeWindow associated with an open dialog window if
143    * it is currently attached to the DOM.
144    */
145   get window() {
146     if (this.#win) {
147       let win = this.#win.get();
148       if (win && (lazy.AppInfo.isAndroid || win.parent)) {
149         return win;
150       }
151     }
152     return null;
153   }
155   set text(inputText) {
156     if (lazy.AppInfo.isAndroid) {
157       this.window.setInputText(inputText);
158     } else {
159       // see toolkit/components/prompts/content/commonDialog.js
160       let { loginTextbox } = this.ui;
161       loginTextbox.value = inputText;
162     }
163   }
165   accept() {
166     if (lazy.AppInfo.isAndroid) {
167       // GeckoView does not have a UI, so the methods are called directly
168       this.window.acceptPrompt();
169     } else {
170       const { button0 } = this.ui;
171       button0.click();
172     }
173   }
175   dismiss() {
176     if (lazy.AppInfo.isAndroid) {
177       // GeckoView does not have a UI, so the methods are called directly
178       this.window.dismissPrompt();
179     } else {
180       const { button0, button1 } = this.ui;
181       (button1 ? button1 : button0).click();
182     }
183   }
185   /**
186    * Returns text of the prompt.
187    *
188    * @returns {string | Promise}
189    *     Returns string on desktop and Promise on Android.
190    */
191   async getText() {
192     if (lazy.AppInfo.isAndroid) {
193       const textPromise = await this.window.getPromptText();
194       return textPromise;
195     }
196     return this.ui.infoBody.textContent;
197   }
199   /**
200    * Returns text of the prompt input.
201    *
202    * @returns {string}
203    *     Returns string on desktop and Promise on Android.
204    */
205   async getInputText() {
206     if (lazy.AppInfo.isAndroid) {
207       const textPromise = await this.window.getInputText();
208       return textPromise;
209     }
210     return this.ui.loginTextbox.value;
211   }