diff options
Diffstat (limited to 'toolkit/components/prompts/content')
-rw-r--r-- | toolkit/components/prompts/content/commonDialog.css | 42 | ||||
-rw-r--r-- | toolkit/components/prompts/content/commonDialog.js | 80 | ||||
-rw-r--r-- | toolkit/components/prompts/content/commonDialog.xhtml | 80 | ||||
-rw-r--r-- | toolkit/components/prompts/content/selectDialog.js | 75 | ||||
-rw-r--r-- | toolkit/components/prompts/content/selectDialog.xhtml | 23 | ||||
-rw-r--r-- | toolkit/components/prompts/content/tabprompts.css | 158 | ||||
-rw-r--r-- | toolkit/components/prompts/content/tabprompts.jsm | 310 |
7 files changed, 768 insertions, 0 deletions
diff --git a/toolkit/components/prompts/content/commonDialog.css b/toolkit/components/prompts/content/commonDialog.css new file mode 100644 index 0000000000..1f26257d69 --- /dev/null +++ b/toolkit/components/prompts/content/commonDialog.css @@ -0,0 +1,42 @@ +/* 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/. */ + +:root { + min-width: 29em; +} + +#dialogGrid { + display: grid; + grid-template-columns: auto 1fr; + align-items: center; +} + +.dialogRow:not([hidden]) { + display: contents; +} + +#iconContainer { + align-self: start; +} + +#infoContainer { + max-width: 45em; +} + +#infoBody { + -moz-user-focus: normal; + user-select: text; + cursor: text !important; + white-space: pre-wrap; + unicode-bidi: plaintext; +} + +#loginLabel, #password1Label { + text-align: end; +} + +#loginTextbox, +#password1Textbox { + text-align: match-parent; +} diff --git a/toolkit/components/prompts/content/commonDialog.js b/toolkit/components/prompts/content/commonDialog.js new file mode 100644 index 0000000000..f1b401f5f1 --- /dev/null +++ b/toolkit/components/prompts/content/commonDialog.js @@ -0,0 +1,80 @@ +/* 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/. */ + +const { CommonDialog } = ChromeUtils.import( + "resource://gre/modules/CommonDialog.jsm" +); + +var propBag, args, Dialog; + +function commonDialogOnLoad() { + propBag = window.arguments[0] + .QueryInterface(Ci.nsIWritablePropertyBag2) + .QueryInterface(Ci.nsIWritablePropertyBag); + // Convert to a JS object + args = {}; + for (let prop of propBag.enumerator) { + args[prop.name] = prop.value; + } + + let dialog = document.getElementById("commonDialog"); + + let ui = { + prompt: window, + loginContainer: document.getElementById("loginContainer"), + loginTextbox: document.getElementById("loginTextbox"), + loginLabel: document.getElementById("loginLabel"), + password1Container: document.getElementById("password1Container"), + password1Textbox: document.getElementById("password1Textbox"), + password1Label: document.getElementById("password1Label"), + infoBody: document.getElementById("infoBody"), + infoTitle: document.getElementById("infoTitle"), + infoIcon: document.getElementById("infoIcon"), + checkbox: document.getElementById("checkbox"), + checkboxContainer: document.getElementById("checkboxContainer"), + button3: dialog.getButton("extra2"), + button2: dialog.getButton("extra1"), + button1: dialog.getButton("cancel"), + button0: dialog.getButton("accept"), + focusTarget: window, + }; + + Dialog = new CommonDialog(args, ui); + window.addEventListener("dialogclosing", function(aEvent) { + if (aEvent.detail?.abort) { + Dialog.abortPrompt(); + } + }); + document.addEventListener("dialogaccept", function() { + Dialog.onButton0(); + }); + document.addEventListener("dialogcancel", function() { + Dialog.onButton1(); + }); + document.addEventListener("dialogextra1", function() { + Dialog.onButton2(); + window.close(); + }); + document.addEventListener("dialogextra2", function() { + Dialog.onButton3(); + window.close(); + }); + Dialog.onLoad(dialog); + + // resize the window to the content + window.sizeToContent(); + + // If the icon hasn't loaded yet, size the window to the content again when + // it does, as its layout can change. + ui.infoIcon.addEventListener("load", () => window.sizeToContent()); + + window.getAttention(); +} + +function commonDialogOnUnload() { + // Convert args back into property bag + for (let propName in args) { + propBag.setProperty(propName, args[propName]); + } +} diff --git a/toolkit/components/prompts/content/commonDialog.xhtml b/toolkit/components/prompts/content/commonDialog.xhtml new file mode 100644 index 0000000000..3840e0ec3c --- /dev/null +++ b/toolkit/components/prompts/content/commonDialog.xhtml @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<!-- 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/. --> + + +<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?> +<?xml-stylesheet href="chrome://global/content/commonDialog.css" type="text/css"?> +<?xml-stylesheet href="chrome://global/skin/commonDialog.css" type="text/css"?> + +<!DOCTYPE window SYSTEM "chrome://global/locale/commonDialog.dtd"> + +<window id="commonDialogWindow" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + aria-describedby="infoBody" + onunload="commonDialogOnUnload();"> +<dialog id="commonDialog" + buttonpack="center"> + + <script src="chrome://global/content/commonDialog.js"/> + <script src="chrome://global/content/globalOverlay.js"/> + <script src="chrome://global/content/editMenuOverlay.js"/> + <script src="chrome://global/content/customElements.js"/> + <script> + /* eslint-disable no-undef */ + document.addEventListener("DOMContentLoaded", function() { + commonDialogOnLoad(); + }); + </script> + + <commandset id="selectEditMenuItems"> + <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')" disabled="true"/> + <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/> + </commandset> + + <popupset id="contentAreaContextSet"> + <menupopup id="contentAreaContextMenu" + onpopupshowing="goUpdateCommand('cmd_copy')"> + <menuitem id="context-copy" + label="©Cmd.label;" + accesskey="©Cmd.accesskey;" + command="cmd_copy" + disabled="true"/> + <menuitem id="context-selectall" + label="&selectAllCmd.label;" + accesskey="&selectAllCmd.accesskey;" + command="cmd_selectAll"/> + </menupopup> + </popupset> + + <!-- The <div> was added in bug 1606617 to workaround bug 1614447 --> + <div xmlns="http://www.w3.org/1999/xhtml"> + <div id="dialogGrid"> + <div class="dialogRow"> + <div id="iconContainer"> + <xul:image id="infoIcon"/> + </div> + <div id="infoContainer"> + <xul:description id="infoTitle" style="margin-bottom: 1em;"/> + <xul:description id="infoBody" context="contentAreaContextMenu" noinitialfocus="true"/> + </div> + </div> + <div id="loginContainer" class="dialogRow" hidden="hidden"> + <xul:label id="loginLabel" value="&editfield0.label;" control="loginTextbox"/> + <input type="text" id="loginTextbox" dir="ltr"/> + </div> + <div id="password1Container" class="dialogRow" hidden="hidden"> + <xul:label id="password1Label" value="&editfield1.label;" control="password1Textbox"/> + <input type="password" id="password1Textbox" dir="ltr"/> + </div> + <div id="checkboxContainer" class="dialogRow" hidden="hidden"> + <div/> <!-- spacer --> + <xul:checkbox id="checkbox" oncommand="Dialog.onCheckbox()"/> + </div> + </div> + </div> +</dialog> +</window> diff --git a/toolkit/components/prompts/content/selectDialog.js b/toolkit/components/prompts/content/selectDialog.js new file mode 100644 index 0000000000..0065e21394 --- /dev/null +++ b/toolkit/components/prompts/content/selectDialog.js @@ -0,0 +1,75 @@ +/* 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/. */ + +// Defined in dialog.xml. +/* globals centerWindowOnScreen:false, moveToAlertPosition:false */ + +var propBag, listBox, args; + +function dialogOnLoad() { + propBag = window.arguments[0] + .QueryInterface(Ci.nsIWritablePropertyBag2) + .QueryInterface(Ci.nsIWritablePropertyBag); + + // Convert to a JS object + let args = {}; + for (let prop of propBag.enumerator) { + args[prop.name] = prop.value; + } + + let promptType = propBag.getProperty("promptType"); + if (promptType != "select") { + Cu.reportError("selectDialog opened for unknown type: " + promptType); + window.close(); + } + + // Default to canceled. + propBag.setProperty("ok", false); + + document.title = propBag.getProperty("title"); + + let text = propBag.getProperty("text"); + document.getElementById("info.txt").setAttribute("value", text); + + let items = propBag.getProperty("list"); + listBox = document.getElementById("list"); + + for (let i = 0; i < items.length; i++) { + let str = items[i]; + if (str == "") { + str = "<>"; + } + listBox.appendItem(str); + listBox.getItemAtIndex(i).addEventListener("dblclick", dialogDoubleClick); + } + listBox.selectedIndex = 0; + listBox.focus(); + + document.addEventListener("dialogaccept", dialogOK); + // resize the window to the content + window.sizeToContent(); + + // Move to the right location + moveToAlertPosition(); + centerWindowOnScreen(); + + // play sound + try { + if (!args.openedWithTabDialog) { + Cc["@mozilla.org/sound;1"] + .createInstance(Ci.nsISound) + .playEventSound(Ci.nsISound.EVENT_SELECT_DIALOG_OPEN); + } + } catch (e) {} +} + +function dialogOK() { + propBag.setProperty("selected", listBox.selectedIndex); + propBag.setProperty("ok", true); +} + +function dialogDoubleClick() { + dialogOK(); + window.close(); +} diff --git a/toolkit/components/prompts/content/selectDialog.xhtml b/toolkit/components/prompts/content/selectDialog.xhtml new file mode 100644 index 0000000000..966cea8fb2 --- /dev/null +++ b/toolkit/components/prompts/content/selectDialog.xhtml @@ -0,0 +1,23 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?> +<!DOCTYPE window SYSTEM "chrome://global/locale/commonDialog.dtd"> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="dialogOnLoad()"> +<dialog> + + <script src="chrome://global/content/selectDialog.js" /> + <keyset id="dialogKeys"/> + <vbox style="width: 24em;margin: 5px;"> + <label id="info.txt"/> + <vbox> + <richlistbox id="list" class="theme-listbox" style="height: 8em;"/> + </vbox> + </vbox> +</dialog> +</window> diff --git a/toolkit/components/prompts/content/tabprompts.css b/toolkit/components/prompts/content/tabprompts.css new file mode 100644 index 0000000000..bef56f7a87 --- /dev/null +++ b/toolkit/components/prompts/content/tabprompts.css @@ -0,0 +1,158 @@ +/* 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/. */ + +/* Tab Modal Prompt boxes */ + +.tabModalBackground { + -moz-box-pack: center; + -moz-box-orient: vertical; +} + +.tabModalBackground, +tabmodalprompt { + width: 100%; + height: 100%; +} + +tabmodalprompt { + --tabmodalprompt-padding: 20px; + overflow: hidden; + text-shadow: none; /* remove lightweight theme text-shadow */ +} + +tabmodalprompt:not([hidden]) { + display: grid; + grid-template-rows: 1fr [dialog-start] auto [dialog-end] 2fr; + justify-items: center; +} + +/* + Adjustments for chrome level tab-prompts to make them + overlap with the upper chrome UI and move them in + front of content prompts. +*/ +tabmodalprompt.tab-prompt { + overflow: visible; + z-index: 1; + grid-template-rows: [dialog-start] auto [dialog-end] 1fr; +} + +tabmodalprompt.tab-prompt .tabmodalprompt-mainContainer { + margin-top: -5px; +} + +.tabmodalprompt-mainContainer { + min-width: 20em; + min-height: 12em; + max-width: calc(60% + calc(var(--tabmodalprompt-padding) * 2)); + -moz-user-focus: normal; + grid-row: dialog; + + display: flex; + flex-direction: column; +} + +.tabmodalprompt-topContainer { + flex-grow: 1; + padding: var(--tabmodalprompt-padding); + display: grid; + grid-template-columns: auto 1fr; + align-items: baseline; + align-content: center; /* center content vertically */ + max-width: 100%; + min-height: 0; + max-height: 60vh; + box-sizing: border-box; +} + +.tabmodalprompt-topContainer > div:not(.tabmodalprompt-infoContainer, [hidden]) { + display: contents; +} + +.tabmodalprompt-infoContainer { + grid-column: span 2; + + display: block; + margin-block: auto; + max-width: 100%; + height: 100%; + min-height: 0; + justify-self: center; /* center text, but only when it fits in one line */ +} + +/* When all elements in the first column are hidden, prevent the second column + from becoming the first one because it won't have the right fraction */ +.tabmodalprompt-topContainer > div > *:nth-child(2) { + grid-column: 2; +} + +.infoTitle { + margin-bottom: 1em !important; + font-weight: bold; +} + +.infoBody { + margin: 0 !important; + -moz-user-focus: normal; + user-select: text; + cursor: text !important; + white-space: pre-wrap; + unicode-bidi: plaintext; + outline: none; /* remove focus outline */ + overflow: auto; + max-width: 100%; + max-height: 100%; +} + +tabmodalprompt label[value=""] { + display: none; +} + +.tabmodalprompt-buttonContainer { + display: flex; + padding: 12px var(--tabmodalprompt-padding) 15px; +} + +.tabmodalprompt-buttonSpacer { + flex-grow: 1; +} + +/* Tab-Modal Payment Request widget */ +.paymentDialogContainer:not([hidden]) { + /* Center the .paymentDialogContainerFrame horizontally with flexbox. */ + display: flex; + flex-direction: column; + position: absolute; + /* TODO: figure out which element is preventing this from showing */ + z-index: 1; + width: 100%; +} + +.paymentDialogContainer > div { + align-self: center; + box-sizing: border-box; + height: 600px; + /* By setting `inset-inline` to `auto` with `position: absolute`, the + * horizontal position from the `align-self: center` is used. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/right#Values. */ + inset-inline: auto; + /* Shrink the height for small browser window sizes so the dialog footer + remains visible. + Ideally this would be 100vh minus the #navigator-toolbox height. */ + max-height: 75vh; + /* Leave a 16px border on each side when the normal dialog width can't fit in + * the browser window. This ensure that the dialog still looks like a dialog + * (with content showing beside) instead of a full-width overlay. */ + max-width: calc(100vw - 16px - 16px); + position: absolute; + /* Vertically overlap the browser chrome. */ + top: -3px; + width: 600px; +} + +.paymentDialogContainerFrame { + box-sizing: border-box; + height: 100%; + width: 100%; +} diff --git a/toolkit/components/prompts/content/tabprompts.jsm b/toolkit/components/prompts/content/tabprompts.jsm new file mode 100644 index 0000000000..c495be2a31 --- /dev/null +++ b/toolkit/components/prompts/content/tabprompts.jsm @@ -0,0 +1,310 @@ +/* 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/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["TabModalPrompt"]; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { AppConstants } = ChromeUtils.import( + "resource://gre/modules/AppConstants.jsm" +); + +var TabModalPrompt = class { + constructor(win) { + this.win = win; + let newPrompt = (this.element = win.document.createElement( + "tabmodalprompt" + )); + newPrompt.setAttribute("role", "dialog"); + let randomIdSuffix = Math.random() + .toString(32) + .substr(2); + newPrompt.setAttribute("aria-describedby", `infoBody-${randomIdSuffix}`); + newPrompt.appendChild( + win.MozXULElement.parseXULToFragment( + ` + <div class="tabmodalprompt-mainContainer" xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <div class="tabmodalprompt-topContainer"> + <div class="tabmodalprompt-infoContainer"> + <div class="tabmodalprompt-infoTitle infoTitle" hidden="hidden"/> + <div class="tabmodalprompt-infoBody infoBody" id="infoBody-${randomIdSuffix}" tabindex="-1"/> + </div> + + <div class="tabmodalprompt-loginContainer" hidden="hidden"> + <xul:label class="tabmodalprompt-loginLabel" value="&editfield0.label;" control="loginTextbox-${randomIdSuffix}"/> + <input class="tabmodalprompt-loginTextbox" id="loginTextbox-${randomIdSuffix}"/> + </div> + + <div class="tabmodalprompt-password1Container" hidden="hidden"> + <xul:label class="tabmodalprompt-password1Label" value="&editfield1.label;" control="password1Textbox-${randomIdSuffix}"/> + <input class="tabmodalprompt-password1Textbox" type="password" id="password1Textbox-${randomIdSuffix}"/> + </div> + + <div class="tabmodalprompt-checkboxContainer" hidden="hidden"> + <div/> + <xul:checkbox class="tabmodalprompt-checkbox"/> + </div> + + <!-- content goes here --> + </div> + <div class="tabmodalprompt-buttonContainer"> + <xul:button class="tabmodalprompt-button3" hidden="true"/> + <div class="tabmodalprompt-buttonSpacer"/> + <xul:button class="tabmodalprompt-button0" label="&okButton.label;"/> + <xul:button class="tabmodalprompt-button2" hidden="true"/> + <xul:button class="tabmodalprompt-button1" label="&cancelButton.label;"/> + </div> + </div>`, + [ + "chrome://global/locale/commonDialog.dtd", + "chrome://global/locale/dialogOverlay.dtd", + ] + ) + ); + + this.ui = { + prompt: this, + promptContainer: this.element, + mainContainer: newPrompt.querySelector(".tabmodalprompt-mainContainer"), + loginContainer: newPrompt.querySelector(".tabmodalprompt-loginContainer"), + loginTextbox: newPrompt.querySelector(".tabmodalprompt-loginTextbox"), + loginLabel: newPrompt.querySelector(".tabmodalprompt-loginLabel"), + password1Container: newPrompt.querySelector( + ".tabmodalprompt-password1Container" + ), + password1Textbox: newPrompt.querySelector( + ".tabmodalprompt-password1Textbox" + ), + password1Label: newPrompt.querySelector(".tabmodalprompt-password1Label"), + infoContainer: newPrompt.querySelector(".tabmodalprompt-infoContainer"), + infoBody: newPrompt.querySelector(".tabmodalprompt-infoBody"), + infoTitle: newPrompt.querySelector(".tabmodalprompt-infoTitle"), + infoIcon: null, + rows: newPrompt.querySelector(".tabmodalprompt-topContainer"), + checkbox: newPrompt.querySelector(".tabmodalprompt-checkbox"), + checkboxContainer: newPrompt.querySelector( + ".tabmodalprompt-checkboxContainer" + ), + button3: newPrompt.querySelector(".tabmodalprompt-button3"), + button2: newPrompt.querySelector(".tabmodalprompt-button2"), + button1: newPrompt.querySelector(".tabmodalprompt-button1"), + button0: newPrompt.querySelector(".tabmodalprompt-button0"), + // focusTarget (for BUTTON_DELAY_ENABLE) not yet supported + }; + + if (AppConstants.XP_UNIX) { + // Reorder buttons on Linux + let buttonContainer = newPrompt.querySelector( + ".tabmodalprompt-buttonContainer" + ); + buttonContainer.appendChild(this.ui.button3); + buttonContainer.appendChild(this.ui.button2); + buttonContainer.appendChild( + newPrompt.querySelector(".tabmodalprompt-buttonSpacer") + ); + buttonContainer.appendChild(this.ui.button1); + buttonContainer.appendChild(this.ui.button0); + } + + this.ui.button0.addEventListener( + "command", + this.onButtonClick.bind(this, 0) + ); + this.ui.button1.addEventListener( + "command", + this.onButtonClick.bind(this, 1) + ); + this.ui.button2.addEventListener( + "command", + this.onButtonClick.bind(this, 2) + ); + this.ui.button3.addEventListener( + "command", + this.onButtonClick.bind(this, 3) + ); + // Anonymous wrapper used here because |Dialog| doesn't exist until init() is called! + this.ui.checkbox.addEventListener("command", () => { + this.Dialog.onCheckbox(); + }); + + /** + * Based on dialog.xml handlers + */ + this.element.addEventListener( + "keypress", + event => { + switch (event.keyCode) { + case KeyEvent.DOM_VK_RETURN: + this.onKeyAction("default", event); + break; + + case KeyEvent.DOM_VK_ESCAPE: + this.onKeyAction("cancel", event); + break; + + default: + if ( + AppConstants.platform == "macosx" && + event.key == "." && + event.metaKey + ) { + this.onKeyAction("cancel", event); + } + break; + } + }, + { mozSystemGroup: true } + ); + + this.element.addEventListener( + "focus", + event => { + let bnum = this.args.defaultButtonNum || 0; + let defaultButton = this.ui["button" + bnum]; + + if (AppConstants.platform == "macosx") { + // On OS X, the default button always stays marked as such (until + // the entire prompt blurs). + defaultButton.setAttribute("default", "true"); + } else { + // On other platforms, the default button is only marked as such + // when no other button has focus. XUL buttons on not-OSX will + // react to pressing enter as a command, so you can't trigger the + // default without tabbing to it or something that isn't a button. + let focusedDefault = event.originalTarget == defaultButton; + let someButtonFocused = + event.originalTarget.localName == "button" || + event.originalTarget.localName == "toolbarbutton"; + if (focusedDefault || !someButtonFocused) { + defaultButton.setAttribute("default", "true"); + } + } + }, + true + ); + + this.element.addEventListener("blur", () => { + // If focus shifted to somewhere else in the browser, don't make + // the default button look active. + let bnum = this.args.defaultButtonNum || 0; + let button = this.ui["button" + bnum]; + button.removeAttribute("default"); + }); + } + + init(args, linkedTab, onCloseCallback) { + this.args = args; + this.linkedTab = linkedTab; + this.onCloseCallback = onCloseCallback; + + if (args.enableDelay) { + throw new Error( + "BUTTON_DELAY_ENABLE not yet supported for tab-modal prompts" + ); + } + + // Apply styling depending on modalType (content or tab prompt) + if (args.modalType === Ci.nsIPrompt.MODAL_TYPE_TAB) { + this.element.classList.add("tab-prompt"); + } else { + this.element.classList.add("content-prompt"); + } + + // We need to remove the prompt when the tab or browser window is closed or + // the page navigates, else we never unwind the event loop and that's sad times. + // Remember to cleanup in shutdownPrompt()! + this.win.addEventListener("resize", this); + this.win.addEventListener("unload", this); + if (linkedTab) { + linkedTab.addEventListener("TabClose", this); + } + // Note: + // nsPrompter.js or in e10s mode browser-parent.js call abortPrompt, + // when the domWindow, for which the prompt was created, generates + // a "pagehide" event. + + let tmp = {}; + ChromeUtils.import("resource://gre/modules/CommonDialog.jsm", tmp); + this.Dialog = new tmp.CommonDialog(args, this.ui); + this.Dialog.onLoad(null); + + // For content prompts display the tabprompt title that shows the prompt origin when + // the prompt origin is not the same as that of the top window. + if ( + args.modalType == Ci.nsIPrompt.MODAL_TYPE_CONTENT && + args.showCallerOrigin + ) { + this.ui.infoTitle.removeAttribute("hidden"); + } + + // TODO: should unhide buttonSpacer on Windows when there are 4 buttons. + // Better yet, just drop support for 4-button dialogs. (bug 609510) + } + + shutdownPrompt() { + // remove our event listeners + try { + this.win.removeEventListener("resize", this); + this.win.removeEventListener("unload", this); + if (this.linkedTab) { + this.linkedTab.removeEventListener("TabClose", this); + } + } catch (e) {} + // invoke callback + this.onCloseCallback(); + this.win = null; + this.ui = null; + // Intentionally not cleaning up |this.element| here -- + // TabModalPromptBox.removePrompt() would need it and it might not + // be called yet -- see browser_double_close_tabs.js. + } + + abortPrompt() { + // Called from other code when the page changes. + this.Dialog.abortPrompt(); + this.shutdownPrompt(); + } + + handleEvent(aEvent) { + switch (aEvent.type) { + case "unload": + case "TabClose": + this.abortPrompt(); + break; + } + } + + onButtonClick(buttonNum) { + // We want to do all the work her asynchronously off a Gecko + // runnable, because of situations like the one described in + // https://bugzilla.mozilla.org/show_bug.cgi?id=1167575#c35 : we + // get here off processing of an OS event and will also process + // one more Gecko runnable before we break out of the event loop + // spin whoever posted the prompt is doing. If we do all our + // work sync, we will exit modal state _before_ processing that + // runnable, and if exiting moral state posts a runnable we will + // incorrectly process that runnable before leaving our event + // loop spin. + Services.tm.dispatchToMainThread(() => { + this.Dialog["onButton" + buttonNum](); + this.shutdownPrompt(); + }); + } + + onKeyAction(action, event) { + if (event.defaultPrevented) { + return; + } + + event.stopPropagation(); + if (action == "default") { + let bnum = this.args.defaultButtonNum || 0; + this.onButtonClick(bnum); + } else { + // action == "cancel" + this.onButtonClick(1); // Cancel button + } + } +}; |