|
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/. --> |
|
5 |
|
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 ]> |
|
12 |
|
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"> |
|
17 |
|
18 <binding id="tabmodalprompt"> |
|
19 |
|
20 <resources> |
|
21 <stylesheet src="chrome://global/content/tabprompts.css"/> |
|
22 <stylesheet src="chrome://global/skin/tabprompts.css"/> |
|
23 </resources> |
|
24 |
|
25 <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
|
26 role="dialog" |
|
27 aria-describedby="info.body"> |
|
28 |
|
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> |
|
38 |
|
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> |
|
44 |
|
45 <row anonid="loginContainer" hidden="true" align="center"> |
|
46 <label anonid="loginLabel" value="&editfield0.label;" control="loginTextbox"/> |
|
47 <textbox anonid="loginTextbox"/> |
|
48 </row> |
|
49 |
|
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> |
|
54 |
|
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> |
|
81 |
|
82 <implementation implements="nsIDOMEventListener"> |
|
83 <constructor> |
|
84 <![CDATA[ |
|
85 let self = this; |
|
86 function getElement(anonid) { |
|
87 return document.getAnonymousElementByAttribute(self, "anonid", anonid); |
|
88 } |
|
89 |
|
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 }; |
|
109 |
|
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> |
|
126 |
|
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"/> |
|
137 |
|
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; |
|
147 |
|
148 if (args.enableDelay) |
|
149 throw "BUTTON_DELAY_ENABLE not yet supported for tab-modal prompts"; |
|
150 |
|
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. |
|
162 |
|
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); |
|
167 |
|
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"); |
|
172 |
|
173 // TODO: should unhide buttonSpacer on Windows when there are 4 buttons. |
|
174 // Better yet, just drop support for 4-button dialogs. (bug 609510) |
|
175 |
|
176 this.onResize(); |
|
177 ]]> |
|
178 </body> |
|
179 </method> |
|
180 |
|
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> |
|
196 |
|
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> |
|
206 |
|
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> |
|
223 |
|
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; |
|
233 |
|
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; |
|
241 |
|
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 = ""; |
|
253 |
|
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> |
|
266 |
|
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> |
|
276 |
|
277 <method name="onKeyAction"> |
|
278 <parameter name="action"/> |
|
279 <parameter name="event"/> |
|
280 <body> |
|
281 <![CDATA[ |
|
282 if (event.defaultPrevented) |
|
283 return; |
|
284 |
|
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> |
|
297 |
|
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]; |
|
311 |
|
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> |
|
334 |
|
335 </binding> |
|
336 </bindings> |