Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 <?xml version="1.0"?>
2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
3 - License, v. 2.0. If a copy of the MPL was not distributed with this
4 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
6 <!DOCTYPE bindings [
7 <!ENTITY % commonDialogDTD SYSTEM "chrome://global/locale/commonDialog.dtd">
8 <!ENTITY % dialogOverlayDTD SYSTEM "chrome://global/locale/dialogOverlay.dtd">
9 %commonDialogDTD;
10 %dialogOverlayDTD;
11 ]>
13 <bindings id="tabPrompts"
14 xmlns="http://www.mozilla.org/xbl"
15 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
16 xmlns:xbl="http://www.mozilla.org/xbl">
18 <binding id="tabmodalprompt">
20 <resources>
21 <stylesheet src="chrome://global/content/tabprompts.css"/>
22 <stylesheet src="chrome://global/skin/tabprompts.css"/>
23 </resources>
25 <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
26 role="dialog"
27 aria-describedby="info.body">
29 <!-- This is based on the guts of commonDialog.xul -->
30 <spacer flex="1"/>
31 <hbox pack="center">
32 <vbox anonid="mainContainer" class="mainContainer">
33 <grid class="topContainer" flex="1">
34 <columns>
35 <column/>
36 <column flex="1"/>
37 </columns>
39 <rows>
40 <vbox anonid="infoContainer" align="center" pack="center" flex="1">
41 <description anonid="info.title" class="info.title" hidden="true" />
42 <description anonid="info.body" class="info.body"/>
43 </vbox>
45 <row anonid="loginContainer" hidden="true" align="center">
46 <label anonid="loginLabel" value="&editfield0.label;" control="loginTextbox"/>
47 <textbox anonid="loginTextbox"/>
48 </row>
50 <row anonid="password1Container" hidden="true" align="center">
51 <label anonid="password1Label" value="&editfield1.label;" control="password1Textbox"/>
52 <textbox anonid="password1Textbox" type="password"/>
53 </row>
55 <row anonid="checkboxContainer" hidden="true">
56 <spacer/>
57 <checkbox anonid="checkbox"/>
58 </row>
59 </rows>
60 </grid>
61 <xbl:children/>
62 <hbox class="buttonContainer">
63 #ifdef XP_UNIX
64 <button anonid="button3" hidden="true"/>
65 <button anonid="button2" hidden="true"/>
66 <spacer anonid="buttonSpacer" flex="1"/>
67 <button anonid="button1" label="&cancelButton.label;"/>
68 <button anonid="button0" label="&okButton.label;"/>
69 #else
70 <button anonid="button3" hidden="true"/>
71 <spacer anonid="buttonSpacer" flex="1"/>
72 <button anonid="button0" label="&okButton.label;"/>
73 <button anonid="button2" hidden="true"/>
74 <button anonid="button1" label="&cancelButton.label;"/>
75 #endif
76 </hbox>
77 </vbox>
78 </hbox>
79 <spacer flex="2"/>
80 </xbl:content>
82 <implementation implements="nsIDOMEventListener">
83 <constructor>
84 <![CDATA[
85 let self = this;
86 function getElement(anonid) {
87 return document.getAnonymousElementByAttribute(self, "anonid", anonid);
88 }
90 this.ui = {
91 prompt : this,
92 loginContainer : getElement("loginContainer"),
93 loginTextbox : getElement("loginTextbox"),
94 loginLabel : getElement("loginLabel"),
95 password1Container : getElement("password1Container"),
96 password1Textbox : getElement("password1Textbox"),
97 password1Label : getElement("password1Label"),
98 infoBody : getElement("info.body"),
99 infoTitle : getElement("info.title"),
100 infoIcon : null,
101 checkbox : getElement("checkbox"),
102 checkboxContainer : getElement("checkboxContainer"),
103 button3 : getElement("button3"),
104 button2 : getElement("button2"),
105 button1 : getElement("button1"),
106 button0 : getElement("button0"),
107 // focusTarget (for BUTTON_DELAY_ENABLE) not yet supported
108 };
110 this.ui.button0.addEventListener("command", this.onButtonClick.bind(this, 0), false);
111 this.ui.button1.addEventListener("command", this.onButtonClick.bind(this, 1), false);
112 this.ui.button2.addEventListener("command", this.onButtonClick.bind(this, 2), false);
113 this.ui.button3.addEventListener("command", this.onButtonClick.bind(this, 3), false);
114 // Anonymous wrapper used here because |Dialog| doesn't exist until init() is called!
115 this.ui.checkbox.addEventListener("command", function() { self.Dialog.onCheckbox(); } , false);
116 this.isLive = false;
117 ]]>
118 </constructor>
119 <destructor>
120 <![CDATA[
121 if (this.isLive) {
122 this.abortPrompt();
123 }
124 ]]>
125 </destructor>
127 <field name="ui"/>
128 <field name="args"/>
129 <field name="linkedTab"/>
130 <field name="onCloseCallback"/>
131 <field name="Dialog"/>
132 <field name="isLive"/>
133 <field name="availWidth"/>
134 <field name="availHeight"/>
135 <field name="minWidth"/>
136 <field name="minHeight"/>
138 <method name="init">
139 <parameter name="args"/>
140 <parameter name="linkedTab"/>
141 <parameter name="onCloseCallback"/>
142 <body>
143 <![CDATA[
144 this.args = args;
145 this.linkedTab = linkedTab;
146 this.onCloseCallback = onCloseCallback;
148 if (args.enableDelay)
149 throw "BUTTON_DELAY_ENABLE not yet supported for tab-modal prompts";
151 // We need to remove the prompt when the tab or browser window is closed or
152 // the page navigates, else we never unwind the event loop and that's sad times.
153 // Remember to cleanup in shutdownPrompt()!
154 this.isLive = true;
155 window.addEventListener("resize", this, false);
156 window.addEventListener("unload", this, false);
157 linkedTab.addEventListener("TabClose", this, false);
158 // Note:
159 // nsPrompter.js or in e10s mode browser-parent.js call abortPrompt,
160 // when the domWindow, for which the prompt was created, generates
161 // a "pagehide" event.
163 let tmp = {};
164 Components.utils.import("resource://gre/modules/CommonDialog.jsm", tmp);
165 this.Dialog = new tmp.CommonDialog(args, this.ui);
166 this.Dialog.onLoad(null);
168 // Display the tabprompt title that shows the prompt origin when
169 // the prompt origin is not the same as that of the top window.
170 if (!args.showAlertOrigin)
171 this.ui.infoTitle.removeAttribute("hidden");
173 // TODO: should unhide buttonSpacer on Windows when there are 4 buttons.
174 // Better yet, just drop support for 4-button dialogs. (bug 609510)
176 this.onResize();
177 ]]>
178 </body>
179 </method>
181 <method name="shutdownPrompt">
182 <body>
183 <![CDATA[
184 // remove our event listeners
185 try {
186 window.removeEventListener("resize", this, false);
187 window.removeEventListener("unload", this, false);
188 this.linkedTab.removeEventListener("TabClose", this, false);
189 } catch(e) { }
190 this.isLive = false;
191 // invoke callback
192 this.onCloseCallback();
193 ]]>
194 </body>
195 </method>
197 <method name="abortPrompt">
198 <body>
199 <![CDATA[
200 // Called from other code when the page changes.
201 this.Dialog.abortPrompt();
202 this.shutdownPrompt();
203 ]]>
204 </body>
205 </method>
207 <method name="handleEvent">
208 <parameter name="aEvent"/>
209 <body>
210 <![CDATA[
211 switch (aEvent.type) {
212 case "resize":
213 this.onResize();
214 break;
215 case "unload":
216 case "TabClose":
217 this.abortPrompt();
218 break;
219 }
220 ]]>
221 </body>
222 </method>
224 <method name="onResize">
225 <body>
226 <![CDATA[
227 let availWidth = this.clientWidth;
228 let availHeight = this.clientHeight;
229 if (availWidth == this.availWidth && availHeight == this.availHeight)
230 return;
231 this.availWidth = availWidth;
232 this.availHeight = availHeight;
234 let self = this;
235 function getElement(anonid) {
236 return document.getAnonymousElementByAttribute(self, "anonid", anonid);
237 }
238 let main = getElement("mainContainer");
239 let info = getElement("infoContainer");
240 let body = this.ui.infoBody;
242 // cap prompt dimensions at 60% width and 60% height of content area
243 if (!this.minWidth)
244 this.minWidth = parseInt(window.getComputedStyle(main).minWidth);
245 if (!this.minHeight)
246 this.minHeight = parseInt(window.getComputedStyle(main).minHeight);
247 let maxWidth = Math.max(Math.floor(availWidth * 0.6), this.minWidth) +
248 info.clientWidth - main.clientWidth;
249 let maxHeight = Math.max(Math.floor(availHeight * 0.6), this.minHeight) +
250 info.clientHeight - main.clientHeight;
251 body.style.maxWidth = maxWidth + "px";
252 info.style.overflow = info.style.width = info.style.height = "";
254 // when prompt text is too long, use scrollbars
255 if (info.clientWidth > maxWidth) {
256 info.style.overflow = "auto";
257 info.style.width = maxWidth + "px";
258 }
259 if (info.clientHeight > maxHeight) {
260 info.style.overflow = "auto";
261 info.style.height = maxHeight + "px";
262 }
263 ]]>
264 </body>
265 </method>
267 <method name="onButtonClick">
268 <parameter name="buttonNum"/>
269 <body>
270 <![CDATA[
271 this.Dialog["onButton" + buttonNum]();
272 this.shutdownPrompt();
273 ]]>
274 </body>
275 </method>
277 <method name="onKeyAction">
278 <parameter name="action"/>
279 <parameter name="event"/>
280 <body>
281 <![CDATA[
282 if (event.defaultPrevented)
283 return;
285 event.stopPropagation();
286 if (action == "default") {
287 let bnum = this.args.defaultButtonNum || 0;
288 let button = this.ui["button" + bnum];
289 this.onButtonClick(bnum);
290 } else { // action == "cancel"
291 this.onButtonClick(1); // Cancel button
292 }
293 ]]>
294 </body>
295 </method>
296 </implementation>
298 <handlers>
299 <!-- Based on dialog.xml handlers -->
300 <handler event="keypress" keycode="VK_RETURN"
301 group="system" action="this.onKeyAction('default', event);"/>
302 <handler event="keypress" keycode="VK_ESCAPE"
303 group="system" action="this.onKeyAction('cancel', event);"/>
304 #ifdef XP_MACOSX
305 <handler event="keypress" key="." modifiers="meta"
306 group="system" action="this.onKeyAction('cancel', event);"/>
307 #endif
308 <handler event="focus" phase="capturing">
309 let bnum = this.args.defaultButtonNum || 0;
310 let defaultButton = this.ui["button" + bnum];
312 #ifdef XP_MACOSX
313 // On OS X, the default button always stays marked as such (until
314 // the entire prompt blurs).
315 defaultButton.setAttribute("default", true);
316 #else
317 // On other platforms, the default button is only marked as such
318 // when no other button has focus. XUL buttons on not-OSX will
319 // react to pressing enter as a command, so you can't trigger the
320 // default without tabbing to it or something that isn't a button.
321 let focusedDefault = (event.originalTarget == defaultButton);
322 let someButtonFocused = event.originalTarget instanceof Ci.nsIDOMXULButtonElement;
323 defaultButton.setAttribute("default", focusedDefault || !someButtonFocused);
324 #endif
325 </handler>
326 <handler event="blur">
327 // If focus shifted to somewhere else in the browser, don't make
328 // the default button look active.
329 let bnum = this.args.defaultButtonNum || 0;
330 let button = this.ui["button" + bnum];
331 button.setAttribute("default", false);
332 </handler>
333 </handlers>
335 </binding>
336 </bindings>