summaryrefslogtreecommitdiffstats
path: root/toolkit/components/prompts/content/tabprompts.xml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/prompts/content/tabprompts.xml')
-rw-r--r--toolkit/components/prompts/content/tabprompts.xml352
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>