diff options
Diffstat (limited to 'toolkit/components/prompts/content/tabprompts.xml')
-rw-r--r-- | toolkit/components/prompts/content/tabprompts.xml | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/toolkit/components/prompts/content/tabprompts.xml b/toolkit/components/prompts/content/tabprompts.xml new file mode 100644 index 000000000..07c6c8efb --- /dev/null +++ b/toolkit/components/prompts/content/tabprompts.xml @@ -0,0 +1,352 @@ +<?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/. --> + +<!DOCTYPE bindings [ +<!ENTITY % commonDialogDTD SYSTEM "chrome://global/locale/commonDialog.dtd"> +<!ENTITY % dialogOverlayDTD SYSTEM "chrome://global/locale/dialogOverlay.dtd"> +%commonDialogDTD; +%dialogOverlayDTD; +]> + +<bindings id="tabPrompts" + xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:xbl="http://www.mozilla.org/xbl"> + + <binding id="tabmodalprompt"> + + <resources> + <stylesheet src="chrome://global/content/tabprompts.css"/> + <stylesheet src="chrome://global/skin/tabprompts.css"/> + </resources> + + <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + role="dialog" + aria-describedby="info.body"> + + <!-- This is based on the guts of commonDialog.xul --> + <spacer flex="1"/> + <hbox pack="center"> + <vbox anonid="mainContainer" class="mainContainer"> + <grid class="topContainer" flex="1"> + <columns> + <column/> + <column flex="1"/> + </columns> + + <rows> + <vbox anonid="infoContainer" align="center" pack="center" flex="1"> + <description anonid="info.title" class="info.title" hidden="true" /> + <description anonid="info.body" class="info.body"/> + </vbox> + + <row anonid="loginContainer" hidden="true" align="center"> + <label anonid="loginLabel" value="&editfield0.label;" control="loginTextbox"/> + <textbox anonid="loginTextbox"/> + </row> + + <row anonid="password1Container" hidden="true" align="center"> + <label anonid="password1Label" value="&editfield1.label;" control="password1Textbox"/> + <textbox anonid="password1Textbox" type="password"/> + </row> + + <row anonid="checkboxContainer" hidden="true"> + <spacer/> + <checkbox anonid="checkbox"/> + </row> + + <xbl:children includes="row"/> + </rows> + </grid> + <xbl:children/> + <hbox class="buttonContainer"> +#ifdef XP_UNIX + <button anonid="button3" hidden="true"/> + <button anonid="button2" hidden="true"/> + <spacer anonid="buttonSpacer" flex="1"/> + <button anonid="button1" label="&cancelButton.label;"/> + <button anonid="button0" label="&okButton.label;"/> +#else + <button anonid="button3" hidden="true"/> + <spacer anonid="buttonSpacer" flex="1"/> + <button anonid="button0" label="&okButton.label;"/> + <button anonid="button2" hidden="true"/> + <button anonid="button1" label="&cancelButton.label;"/> +#endif + </hbox> + </vbox> + </hbox> + <spacer flex="2"/> + </xbl:content> + + <implementation implements="nsIDOMEventListener"> + <constructor> + <![CDATA[ + let self = this; + function getElement(anonid) { + return document.getAnonymousElementByAttribute(self, "anonid", anonid); + } + + this.ui = { + prompt : this, + loginContainer : getElement("loginContainer"), + loginTextbox : getElement("loginTextbox"), + loginLabel : getElement("loginLabel"), + password1Container : getElement("password1Container"), + password1Textbox : getElement("password1Textbox"), + password1Label : getElement("password1Label"), + infoBody : getElement("info.body"), + infoTitle : getElement("info.title"), + infoIcon : null, + checkbox : getElement("checkbox"), + checkboxContainer : getElement("checkboxContainer"), + button3 : getElement("button3"), + button2 : getElement("button2"), + button1 : getElement("button1"), + button0 : getElement("button0"), + // focusTarget (for BUTTON_DELAY_ENABLE) not yet supported + }; + + this.ui.button0.addEventListener("command", this.onButtonClick.bind(this, 0), false); + this.ui.button1.addEventListener("command", this.onButtonClick.bind(this, 1), false); + this.ui.button2.addEventListener("command", this.onButtonClick.bind(this, 2), false); + this.ui.button3.addEventListener("command", this.onButtonClick.bind(this, 3), false); + // Anonymous wrapper used here because |Dialog| doesn't exist until init() is called! + this.ui.checkbox.addEventListener("command", function() { self.Dialog.onCheckbox(); }, false); + this.isLive = false; + ]]> + </constructor> + <destructor> + <![CDATA[ + if (this.isLive) { + this.abortPrompt(); + } + ]]> + </destructor> + + <field name="ui"/> + <field name="args"/> + <field name="linkedTab"/> + <field name="onCloseCallback"/> + <field name="Dialog"/> + <field name="isLive"/> + <field name="availWidth"/> + <field name="availHeight"/> + <field name="minWidth"/> + <field name="minHeight"/> + + <method name="init"> + <parameter name="args"/> + <parameter name="linkedTab"/> + <parameter name="onCloseCallback"/> + <body> + <![CDATA[ + this.args = args; + this.linkedTab = linkedTab; + this.onCloseCallback = onCloseCallback; + + if (args.enableDelay) + throw "BUTTON_DELAY_ENABLE not yet supported for tab-modal prompts"; + + // 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.isLive = true; + window.addEventListener("resize", this, false); + window.addEventListener("unload", this, false); + linkedTab.addEventListener("TabClose", this, false); + // 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 = {}; + Components.utils.import("resource://gre/modules/CommonDialog.jsm", tmp); + this.Dialog = new tmp.CommonDialog(args, this.ui); + this.Dialog.onLoad(null); + + // 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.showAlertOrigin) + 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) + + this.onResize(); + ]]> + </body> + </method> + + <method name="shutdownPrompt"> + <body> + <![CDATA[ + // remove our event listeners + try { + window.removeEventListener("resize", this, false); + window.removeEventListener("unload", this, false); + this.linkedTab.removeEventListener("TabClose", this, false); + } catch (e) { } + this.isLive = false; + // invoke callback + this.onCloseCallback(); + ]]> + </body> + </method> + + <method name="abortPrompt"> + <body> + <![CDATA[ + // Called from other code when the page changes. + this.Dialog.abortPrompt(); + this.shutdownPrompt(); + ]]> + </body> + </method> + + <method name="handleEvent"> + <parameter name="aEvent"/> + <body> + <![CDATA[ + switch (aEvent.type) { + case "resize": + this.onResize(); + break; + case "unload": + case "TabClose": + this.abortPrompt(); + break; + } + ]]> + </body> + </method> + + <method name="onResize"> + <body> + <![CDATA[ + let availWidth = this.clientWidth; + let availHeight = this.clientHeight; + if (availWidth == this.availWidth && availHeight == this.availHeight) + return; + this.availWidth = availWidth; + this.availHeight = availHeight; + + let self = this; + function getElement(anonid) { + return document.getAnonymousElementByAttribute(self, "anonid", anonid); + } + let main = getElement("mainContainer"); + let info = getElement("infoContainer"); + let body = this.ui.infoBody; + + // cap prompt dimensions at 60% width and 60% height of content area + if (!this.minWidth) + this.minWidth = parseInt(window.getComputedStyle(main).minWidth); + if (!this.minHeight) + this.minHeight = parseInt(window.getComputedStyle(main).minHeight); + let maxWidth = Math.max(Math.floor(availWidth * 0.6), this.minWidth) + + info.clientWidth - main.clientWidth; + let maxHeight = Math.max(Math.floor(availHeight * 0.6), this.minHeight) + + info.clientHeight - main.clientHeight; + body.style.maxWidth = maxWidth + "px"; + info.style.overflow = info.style.width = info.style.height = ""; + + // when prompt text is too long, use scrollbars + if (info.clientWidth > maxWidth) { + info.style.overflow = "auto"; + info.style.width = maxWidth + "px"; + } + if (info.clientHeight > maxHeight) { + info.style.overflow = "auto"; + info.style.height = maxHeight + "px"; + } + ]]> + </body> + </method> + + <method name="onButtonClick"> + <parameter name="buttonNum"/> + <body> + <![CDATA[ + // 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.mainThread.dispatch(() => { + this.Dialog["onButton" + buttonNum](); + this.shutdownPrompt(); + }, + Ci.nsIThread.DISPATCH_NORMAL); + ]]> + </body> + </method> + + <method name="onKeyAction"> + <parameter name="action"/> + <parameter name="event"/> + <body> + <![CDATA[ + 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 + } + ]]> + </body> + </method> + </implementation> + + <handlers> + <!-- Based on dialog.xml handlers --> + <handler event="keypress" keycode="VK_RETURN" + group="system" action="this.onKeyAction('default', event);"/> + <handler event="keypress" keycode="VK_ESCAPE" + group="system" action="this.onKeyAction('cancel', event);"/> +#ifdef XP_MACOSX + <handler event="keypress" key="." modifiers="meta" + group="system" action="this.onKeyAction('cancel', event);"/> +#endif + <handler event="focus" phase="capturing"> + let bnum = this.args.defaultButtonNum || 0; + let defaultButton = this.ui["button" + bnum]; + + let { AppConstants } = + Components.utils.import("resource://gre/modules/AppConstants.jsm", {}); + 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 instanceof Ci.nsIDOMXULButtonElement; + defaultButton.setAttribute("default", focusedDefault || !someButtonFocused); + } + </handler> + <handler event="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.setAttribute("default", false); + </handler> + </handlers> + + </binding> +</bindings> |