From 86da8f87a36a88eb4d6a41c405852dc09ac4693e Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Thu, 11 Feb 2021 23:57:57 +0000 Subject: [PATCH] Bug 1685313 - allow window modal dialogs to display inside windows, r=jaws,mtigley Differential Revision: https://phabricator.services.mozilla.com/D103388 --- browser/app/profile/firefox.js | 9 ++ browser/base/content/browser.css | 19 +++- browser/base/content/browser.js | 87 ++++++++++++++++++ browser/base/content/browser.xhtml | 11 +++ netwerk/base/nsIPrompt.idl | 3 + toolkit/components/prompts/src/Prompter.jsm | 97 +++++++++++++++++---- toolkit/content/widgets/dialog.js | 14 ++- 7 files changed, 218 insertions(+), 22 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 178c66a550f3..73e0f279e04b 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1370,6 +1370,15 @@ pref("prompts.tabChromePromptSubDialog", true); pref("prompts.contentPromptSubDialog", false); #endif +// Whether to show window-modal dialogs opened for browser windows +// in a SubDialog inside their parent, instead of an OS level window. +#ifdef NIGHTLY_BUILD + pref("prompts.windowPromptSubDialog", true); +#else + pref("prompts.windowPromptSubDialog", false); +#endif + + // Activates preloading of the new tab url. pref("browser.newtab.preload", true); diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index f9e4040aa143..14ad32e01528 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -1479,7 +1479,7 @@ toolbar[keyNav=true]:not([collapsed=true], [customizing=true]) toolbartabstop { } /** - * Tab Dialogs + * Dialogs */ .dialogStack { @@ -1572,6 +1572,21 @@ toolbar[keyNav=true]:not([collapsed=true], [customizing=true]) toolbartabstop { place-content: center; } +/* Override default styles */ +#window-modal-dialog { + border-width: 0; + background-color: transparent; +} + +#window-modal-dialog::backdrop { + background-color: rgba(0, 0, 0, 0.5); +} + +/* Hide tab-modal dialogs when a window-modal one is up. */ +:root[window-modal-open] .browserStack > .dialogStack { + visibility: hidden; +} + /** - * End Tab Dialogs + * End Dialogs */ diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index d04ec827af63..a34df7cfc63d 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -71,6 +71,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm", SiteDataManager: "resource:///modules/SiteDataManager.jsm", SitePermissions: "resource:///modules/SitePermissions.jsm", + SubDialog: "resource://gre/modules/SubDialog.jsm", SubDialogManager: "resource://gre/modules/SubDialog.jsm", TabModalPrompt: "chrome://global/content/tabprompts.jsm", TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm", @@ -9301,6 +9302,92 @@ TabModalPromptBox.prototype = { }, }; +// Handle window-modal prompts that we want to display with the same style as +// tab-modal prompts. +var gDialogBox = { + _dialog: null, + + get isOpen() { + return !!this._dialog; + }, + + async open(uri, args) { + try { + await this._open(uri, args); + } catch (ex) { + Cu.reportError(ex); + } finally { + let dialog = document.getElementById("window-modal-dialog"); + dialog.close(); + dialog.style.visibility = "hidden"; + dialog.style.height = "0"; + dialog.style.width = "0"; + document.documentElement.removeAttribute("window-modal-open"); + dialog.removeEventListener("dialogopen", this); + this._dialog = null; + } + return args; + }, + + handleEvent(event) { + if (event.type == "dialogopen") { + this._dialog.focus(true); + } + }, + + _open(uri, args) { + // Get this offset before we touch style below, as touching style seems + // to reset the cached layout bounds. + let offset = window.windowUtils.getBoundsWithoutFlushing( + gBrowser.selectedBrowser + ).top; + let parentElement = document.getElementById("window-modal-dialog"); + // The dialog has 1em padding; compensate for that: + parentElement.style.marginTop = `calc(${offset}px - 1em)`; + parentElement.style.removeProperty("visibility"); + parentElement.style.removeProperty("width"); + parentElement.style.removeProperty("height"); + document.documentElement.setAttribute("window-modal-open", true); + // Call this first so the contents show up and get layout, which is + // required for SubDialog to work. + parentElement.showModal(); + + // Now actually set up the dialog contents: + let template = document.getElementById("window-modal-dialog-template") + .content.firstElementChild; + parentElement.addEventListener("dialogopen", this); + this._dialog = new SubDialog({ + template, + parentElement, + id: "window-modal-dialog-subdialog", + options: { + consumeOutsideClicks: false, + }, + }); + let closedPromise = new Promise(resolve => { + this._closedCallback = resolve; + }); + this._dialog.open( + uri, + { + features: "resizable=no", + modalType: Ci.nsIPrompt.MODAL_TYPE_INTERNAL_WINDOW, + closedCallback: () => { + this._closedCallback(); + }, + }, + args + ); + return closedPromise; + }, +}; + +// browser.js loads in the library window, too, but we can only show prompts +// in the main browser window: +if (window.location.href != AppConstants.BROWSER_CHROME_URL) { + gDialogBox = null; +} + var ConfirmationHint = { _timerID: null, diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml index fb69c72d02f5..d750b8104f26 100644 --- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -1618,6 +1618,17 @@ +