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/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
9 Log: "chrome://remote/content/shared/Log.sys.mjs",
12 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
14 const COMMON_DIALOG = "chrome://global/content/commonDialog.xhtml";
17 export const modal = {
18 ACTION_CLOSED: "closed",
19 ACTION_OPENED: "opened",
23 * Check for already existing modal or tab modal dialogs and
24 * return the first one.
26 * @param {browser.Context} context
27 * Reference to the browser context to check for existent dialogs.
29 * @returns {modal.Dialog}
30 * Returns instance of the Dialog class, or `null` if no modal dialog
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
40 win.document.documentURI === COMMON_DIALOG &&
42 win.opener === context.window
44 lazy.logger.trace("Found open window modal prompt");
45 return new modal.Dialog(() => context, win);
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(() => context, prompt);
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.
63 // TODO: Find an adequate implementation for Firefox on Android (bug 1708105)
64 if (contentBrowser?.tabDialogBox) {
65 let dialogs = contentBrowser.tabDialogBox.getTabDialogManager().dialogs;
67 lazy.logger.trace("Found open tab modal prompt");
68 return new modal.Dialog(() => context, dialogs[0].frameContentWindow);
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(() => context, dialogs[0].frameContentWindow);
81 // If no modal dialog has been found yet, check for old non SubDialog based
82 // content modal dialogs. Even with those deprecated in Firefox 89 we should
83 // keep supporting applications that don't have them implemented yet.
84 if (contentBrowser?.tabModalPromptBox) {
85 const prompts = contentBrowser.tabModalPromptBox.listPrompts();
87 lazy.logger.trace("Found open old-style content prompt");
88 return new modal.Dialog(() => context, null);
96 * Represents a modal dialog.
98 * @param {function(): browser.Context} curBrowserFn
99 * Function that returns the current |browser.Context|.
100 * @param {DOMWindow} dialog
101 * DOMWindow of the dialog.
103 modal.Dialog = class {
104 constructor(curBrowserFn, dialog) {
105 this.curBrowserFn_ = curBrowserFn;
106 this.win_ = Cu.getWeakReference(dialog);
110 if (lazy.AppInfo.isAndroid) {
111 return this.window.args;
113 let tm = this.tabModal;
114 return tm ? tm.args : null;
118 return this.curBrowserFn_();
122 if (lazy.AppInfo.isAndroid) {
123 return this.window !== null;
131 get isWindowModal() {
133 Services.prompt.MODAL_TYPE_WINDOW,
134 Services.prompt.MODAL_TYPE_INTERNAL_WINDOW,
135 ].includes(this.args.modalType);
139 let win = this.window;
143 return this.curBrowser_.getTabModal();
147 return this.args.promptType;
151 let tm = this.tabModal;
152 return tm ? tm.ui : null;
156 * For Android, this returns a GeckoViewPrompter, which can be used to control prompts.
157 * Otherwise, this returns the ChromeWindow associated with an open dialog window if
158 * it is currently attached to the DOM.
162 let win = this.win_.get();
163 if (win && (lazy.AppInfo.isAndroid || win.parent)) {
170 set text(inputText) {
171 if (lazy.AppInfo.isAndroid) {
172 this.window.setInputText(inputText);
174 // see toolkit/components/prompts/content/commonDialog.js
175 let { loginTextbox } = this.ui;
176 loginTextbox.value = inputText;
181 if (lazy.AppInfo.isAndroid) {
182 // GeckoView does not have a UI, so the methods are called directly
183 this.window.acceptPrompt();
185 const { button0 } = this.ui;
191 if (lazy.AppInfo.isAndroid) {
192 // GeckoView does not have a UI, so the methods are called directly
193 this.window.dismissPrompt();
195 const { button0, button1 } = this.ui;
196 (button1 ? button1 : button0).click();
201 * Returns text of the prompt.
203 * @returns {string | Promise}
204 * Returns string on desktop and Promise on Android.
207 if (lazy.AppInfo.isAndroid) {
208 const textPromise = await this.window.getPromptText();
211 return this.ui.infoBody.textContent;
215 * Returns text of the prompt input.
218 * Returns string on desktop and Promise on Android.
220 async getInputText() {
221 if (lazy.AppInfo.isAndroid) {
222 const textPromise = await this.window.getInputText();
225 return this.ui.loginTextbox.value;