|
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 |
|
7 <!DOCTYPE bindings [ |
|
8 <!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd"> |
|
9 %notificationDTD; |
|
10 ]> |
|
11 |
|
12 <bindings id="notificationBindings" |
|
13 xmlns="http://www.mozilla.org/xbl" |
|
14 xmlns:xbl="http://www.mozilla.org/xbl" |
|
15 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
16 |
|
17 <binding id="notificationbox"> |
|
18 <content> |
|
19 <xul:stack xbl:inherits="hidden=notificationshidden" |
|
20 class="notificationbox-stack"> |
|
21 <xul:spacer/> |
|
22 <children includes="notification"/> |
|
23 </xul:stack> |
|
24 <children/> |
|
25 </content> |
|
26 |
|
27 <implementation> |
|
28 <field name="PRIORITY_INFO_LOW" readonly="true">1</field> |
|
29 <field name="PRIORITY_INFO_MEDIUM" readonly="true">2</field> |
|
30 <field name="PRIORITY_INFO_HIGH" readonly="true">3</field> |
|
31 <field name="PRIORITY_WARNING_LOW" readonly="true">4</field> |
|
32 <field name="PRIORITY_WARNING_MEDIUM" readonly="true">5</field> |
|
33 <field name="PRIORITY_WARNING_HIGH" readonly="true">6</field> |
|
34 <field name="PRIORITY_CRITICAL_LOW" readonly="true">7</field> |
|
35 <field name="PRIORITY_CRITICAL_MEDIUM" readonly="true">8</field> |
|
36 <field name="PRIORITY_CRITICAL_HIGH" readonly="true">9</field> |
|
37 <field name="PRIORITY_CRITICAL_BLOCK" readonly="true">10</field> |
|
38 |
|
39 <field name="currentNotification">null</field> |
|
40 |
|
41 <field name="_closedNotification">null</field> |
|
42 <field name="_blockingCanvas">null</field> |
|
43 <field name="_animating">false</field> |
|
44 |
|
45 <property name="notificationsHidden" |
|
46 onget="return this.getAttribute('notificationshidden') == 'true';"> |
|
47 <setter> |
|
48 if (val) |
|
49 this.setAttribute('notificationshidden', true); |
|
50 else this.removeAttribute('notificationshidden'); |
|
51 return val; |
|
52 </setter> |
|
53 </property> |
|
54 |
|
55 <property name="allNotifications" readonly="true"> |
|
56 <getter> |
|
57 <![CDATA[ |
|
58 var closedNotification = this._closedNotification; |
|
59 var notifications = this.getElementsByTagName('notification'); |
|
60 return Array.filter(notifications, function(n) n != closedNotification); |
|
61 ]]> |
|
62 </getter> |
|
63 </property> |
|
64 |
|
65 <method name="getNotificationWithValue"> |
|
66 <parameter name="aValue"/> |
|
67 <body> |
|
68 <![CDATA[ |
|
69 var notifications = this.allNotifications; |
|
70 for (var n = notifications.length - 1; n >= 0; n--) { |
|
71 if (aValue == notifications[n].getAttribute("value")) |
|
72 return notifications[n]; |
|
73 } |
|
74 return null; |
|
75 ]]> |
|
76 </body> |
|
77 </method> |
|
78 |
|
79 <method name="appendNotification"> |
|
80 <parameter name="aLabel"/> |
|
81 <parameter name="aValue"/> |
|
82 <parameter name="aImage"/> |
|
83 <parameter name="aPriority"/> |
|
84 <parameter name="aButtons"/> |
|
85 <parameter name="aEventCallback"/> |
|
86 <body> |
|
87 <![CDATA[ |
|
88 if (aPriority < this.PRIORITY_INFO_LOW || |
|
89 aPriority > this.PRIORITY_CRITICAL_BLOCK) |
|
90 throw "Invalid notification priority " + aPriority; |
|
91 |
|
92 // check for where the notification should be inserted according to |
|
93 // priority. If two are equal, the existing one appears on top. |
|
94 var notifications = this.allNotifications; |
|
95 var insertPos = null; |
|
96 for (var n = notifications.length - 1; n >= 0; n--) { |
|
97 if (notifications[n].priority < aPriority) |
|
98 break; |
|
99 insertPos = notifications[n]; |
|
100 } |
|
101 |
|
102 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
103 var newitem = document.createElementNS(XULNS, "notification"); |
|
104 newitem.setAttribute("label", aLabel); |
|
105 newitem.setAttribute("value", aValue); |
|
106 if (aImage) |
|
107 newitem.setAttribute("image", aImage); |
|
108 newitem.eventCallback = aEventCallback; |
|
109 |
|
110 if (aButtons) { |
|
111 // The notification-button-default class is added to the button |
|
112 // with isDefault set to true. If there is no such button, it is |
|
113 // added to the first button (unless that button has isDefault |
|
114 // set to false). There cannot be multiple default buttons. |
|
115 var defaultElem; |
|
116 |
|
117 for (var b = 0; b < aButtons.length; b++) { |
|
118 var button = aButtons[b]; |
|
119 var buttonElem = document.createElementNS(XULNS, "button"); |
|
120 buttonElem.setAttribute("label", button.label); |
|
121 buttonElem.setAttribute("accesskey", button.accessKey); |
|
122 buttonElem.classList.add("notification-button"); |
|
123 |
|
124 if (button.isDefault || |
|
125 b == 0 && !("isDefault" in button)) |
|
126 defaultElem = buttonElem; |
|
127 |
|
128 newitem.appendChild(buttonElem); |
|
129 buttonElem.buttonInfo = button; |
|
130 } |
|
131 |
|
132 if (defaultElem) |
|
133 defaultElem.classList.add("notification-button-default"); |
|
134 } |
|
135 |
|
136 newitem.setAttribute("priority", aPriority); |
|
137 if (aPriority >= this.PRIORITY_CRITICAL_LOW) |
|
138 newitem.setAttribute("type", "critical"); |
|
139 else if (aPriority <= this.PRIORITY_INFO_HIGH) |
|
140 newitem.setAttribute("type", "info"); |
|
141 else |
|
142 newitem.setAttribute("type", "warning"); |
|
143 |
|
144 if (!insertPos) { |
|
145 newitem.style.position = "fixed"; |
|
146 newitem.style.top = "100%"; |
|
147 newitem.style.marginTop = "-15px"; |
|
148 newitem.style.opacity = "0"; |
|
149 } |
|
150 this.insertBefore(newitem, insertPos); |
|
151 if (!insertPos) |
|
152 this._showNotification(newitem, true); |
|
153 |
|
154 // Fire event for accessibility APIs |
|
155 var event = document.createEvent("Events"); |
|
156 event.initEvent("AlertActive", true, true); |
|
157 newitem.dispatchEvent(event); |
|
158 |
|
159 return newitem; |
|
160 ]]> |
|
161 </body> |
|
162 </method> |
|
163 |
|
164 <method name="removeNotification"> |
|
165 <parameter name="aItem"/> |
|
166 <parameter name="aSkipAnimation"/> |
|
167 <body> |
|
168 <![CDATA[ |
|
169 if (aItem == this.currentNotification) |
|
170 this.removeCurrentNotification(aSkipAnimation); |
|
171 else if (aItem != this._closedNotification) |
|
172 this._removeNotificationElement(aItem); |
|
173 return aItem; |
|
174 ]]> |
|
175 </body> |
|
176 </method> |
|
177 |
|
178 <method name="_removeNotificationElement"> |
|
179 <parameter name="aChild"/> |
|
180 <body> |
|
181 <![CDATA[ |
|
182 if (aChild.eventCallback) |
|
183 aChild.eventCallback("removed"); |
|
184 this.removeChild(aChild); |
|
185 |
|
186 // make sure focus doesn't get lost (workaround for bug 570835) |
|
187 let fm = Components.classes["@mozilla.org/focus-manager;1"] |
|
188 .getService(Components.interfaces.nsIFocusManager); |
|
189 if (!fm.getFocusedElementForWindow(window, false, {})) |
|
190 fm.moveFocus(window, this, fm.MOVEFOCUS_FORWARD, 0); |
|
191 ]]> |
|
192 </body> |
|
193 </method> |
|
194 |
|
195 <method name="removeCurrentNotification"> |
|
196 <parameter name="aSkipAnimation"/> |
|
197 <body> |
|
198 <![CDATA[ |
|
199 this._showNotification(this.currentNotification, false, aSkipAnimation); |
|
200 ]]> |
|
201 </body> |
|
202 </method> |
|
203 |
|
204 <method name="removeAllNotifications"> |
|
205 <parameter name="aImmediate"/> |
|
206 <body> |
|
207 <![CDATA[ |
|
208 var notifications = this.allNotifications; |
|
209 for (var n = notifications.length - 1; n >= 0; n--) { |
|
210 if (aImmediate) |
|
211 this._removeNotificationElement(notifications[n]); |
|
212 else |
|
213 this.removeNotification(notifications[n]); |
|
214 } |
|
215 this.currentNotification = null; |
|
216 |
|
217 // Must clear up any currently animating notification |
|
218 if (aImmediate) |
|
219 this._finishAnimation(); |
|
220 ]]> |
|
221 </body> |
|
222 </method> |
|
223 |
|
224 <method name="removeTransientNotifications"> |
|
225 <body> |
|
226 <![CDATA[ |
|
227 var notifications = this.allNotifications; |
|
228 for (var n = notifications.length - 1; n >= 0; n--) { |
|
229 var notification = notifications[n]; |
|
230 if (notification.persistence) |
|
231 notification.persistence--; |
|
232 else if (Date.now() > notification.timeout) |
|
233 this.removeNotification(notification); |
|
234 } |
|
235 ]]> |
|
236 </body> |
|
237 </method> |
|
238 |
|
239 <method name="_showNotification"> |
|
240 <parameter name="aNotification"/> |
|
241 <parameter name="aSlideIn"/> |
|
242 <parameter name="aSkipAnimation"/> |
|
243 <body> |
|
244 <![CDATA[ |
|
245 this._finishAnimation(); |
|
246 |
|
247 var height = aNotification.boxObject.height; |
|
248 var skipAnimation = aSkipAnimation || (height == 0); |
|
249 |
|
250 if (aSlideIn) { |
|
251 this.currentNotification = aNotification; |
|
252 aNotification.style.removeProperty("position"); |
|
253 aNotification.style.removeProperty("top"); |
|
254 aNotification.style.removeProperty("margin-top"); |
|
255 aNotification.style.removeProperty("opacity"); |
|
256 |
|
257 if (skipAnimation) { |
|
258 this._setBlockingState(this.currentNotification); |
|
259 return; |
|
260 } |
|
261 } |
|
262 else { |
|
263 this._closedNotification = aNotification; |
|
264 var notifications = this.allNotifications; |
|
265 var idx = notifications.length - 1; |
|
266 this.currentNotification = (idx >= 0) ? notifications[idx] : null; |
|
267 |
|
268 if (skipAnimation) { |
|
269 this._removeNotificationElement(this._closedNotification); |
|
270 this._closedNotification = null; |
|
271 this._setBlockingState(this.currentNotification); |
|
272 return; |
|
273 } |
|
274 |
|
275 aNotification.style.marginTop = -height + "px"; |
|
276 aNotification.style.opacity = 0; |
|
277 } |
|
278 |
|
279 this._animating = true; |
|
280 ]]> |
|
281 </body> |
|
282 </method> |
|
283 |
|
284 <method name="_finishAnimation"> |
|
285 <body><![CDATA[ |
|
286 if (this._animating) { |
|
287 this._animating = false; |
|
288 if (this._closedNotification) { |
|
289 this._removeNotificationElement(this._closedNotification); |
|
290 this._closedNotification = null; |
|
291 } |
|
292 this._setBlockingState(this.currentNotification); |
|
293 } |
|
294 ]]></body> |
|
295 </method> |
|
296 |
|
297 <method name="_setBlockingState"> |
|
298 <parameter name="aNotification"/> |
|
299 <body> |
|
300 <![CDATA[ |
|
301 var isblock = aNotification && |
|
302 aNotification.priority == this.PRIORITY_CRITICAL_BLOCK; |
|
303 var canvas = this._blockingCanvas; |
|
304 if (isblock) { |
|
305 if (!canvas) |
|
306 canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); |
|
307 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
308 var content = this.firstChild; |
|
309 if (!content || |
|
310 content.namespaceURI != XULNS || |
|
311 content.localName != "browser") |
|
312 return; |
|
313 |
|
314 var width = content.boxObject.width; |
|
315 var height = content.boxObject.height; |
|
316 content.collapsed = true; |
|
317 |
|
318 canvas.setAttribute("width", width); |
|
319 canvas.setAttribute("height", height); |
|
320 canvas.setAttribute("flex", "1"); |
|
321 |
|
322 this.appendChild(canvas); |
|
323 this._blockingCanvas = canvas; |
|
324 |
|
325 var bgcolor = "white"; |
|
326 try { |
|
327 var prefService = Components.classes["@mozilla.org/preferences-service;1"]. |
|
328 getService(Components.interfaces.nsIPrefBranch); |
|
329 bgcolor = prefService.getCharPref("browser.display.background_color"); |
|
330 |
|
331 var win = content.contentWindow; |
|
332 var context = canvas.getContext("2d"); |
|
333 context.globalAlpha = 0.5; |
|
334 context.drawWindow(win, win.scrollX, win.scrollY, |
|
335 width, height, bgcolor); |
|
336 } |
|
337 catch(ex) { }; |
|
338 } |
|
339 else if (canvas) { |
|
340 canvas.parentNode.removeChild(canvas); |
|
341 this._blockingCanvas = null; |
|
342 var content = this.firstChild; |
|
343 if (content) |
|
344 content.collapsed = false; |
|
345 } |
|
346 ]]> |
|
347 </body> |
|
348 </method> |
|
349 |
|
350 </implementation> |
|
351 |
|
352 <handlers> |
|
353 <handler event="transitionend"><![CDATA[ |
|
354 if (event.target.localName == "notification" && |
|
355 event.propertyName == "margin-top") |
|
356 this._finishAnimation(); |
|
357 ]]></handler> |
|
358 </handlers> |
|
359 |
|
360 </binding> |
|
361 |
|
362 <binding id="notification" role="xul:alert"> |
|
363 <content> |
|
364 <xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type"> |
|
365 <xul:hbox anonid="details" align="center" flex="1" |
|
366 oncommand="this.parentNode.parentNode._doButtonCommand(event);"> |
|
367 <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/> |
|
368 <xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/> |
|
369 <xul:spacer flex="1"/> |
|
370 <children/> |
|
371 </xul:hbox> |
|
372 <xul:toolbarbutton ondblclick="event.stopPropagation();" |
|
373 class="messageCloseButton close-icon tabbable" |
|
374 xbl:inherits="hidden=hideclose" |
|
375 tooltiptext="&closeNotification.tooltip;" |
|
376 oncommand="document.getBindingParent(this).close();"/> |
|
377 </xul:hbox> |
|
378 </content> |
|
379 <resources> |
|
380 <stylesheet src="chrome://global/skin/notification.css"/> |
|
381 </resources> |
|
382 <implementation> |
|
383 <property name="label" onset="this.setAttribute('label', val); return val;" |
|
384 onget="return this.getAttribute('label');"/> |
|
385 <property name="value" onset="this.setAttribute('value', val); return val;" |
|
386 onget="return this.getAttribute('value');"/> |
|
387 <property name="image" onset="this.setAttribute('image', val); return val;" |
|
388 onget="return this.getAttribute('image');"/> |
|
389 <property name="type" onset="this.setAttribute('type', val); return val;" |
|
390 onget="return this.getAttribute('type');"/> |
|
391 <property name="priority" onget="return parseInt(this.getAttribute('priority')) || 0;" |
|
392 onset="this.setAttribute('priority', val); return val;"/> |
|
393 <property name="persistence" onget="return parseInt(this.getAttribute('persistence')) || 0;" |
|
394 onset="this.setAttribute('persistence', val); return val;"/> |
|
395 <field name="timeout">0</field> |
|
396 |
|
397 <property name="control" readonly="true"> |
|
398 <getter> |
|
399 <![CDATA[ |
|
400 var parent = this.parentNode; |
|
401 while (parent) { |
|
402 if (parent.localName == "notificationbox") |
|
403 return parent; |
|
404 parent = parent.parentNode; |
|
405 } |
|
406 return null; |
|
407 ]]> |
|
408 </getter> |
|
409 </property> |
|
410 |
|
411 <method name="close"> |
|
412 <body> |
|
413 <![CDATA[ |
|
414 var control = this.control; |
|
415 if (control) |
|
416 control.removeNotification(this); |
|
417 else |
|
418 this.hidden = true; |
|
419 ]]> |
|
420 </body> |
|
421 </method> |
|
422 |
|
423 <method name="_doButtonCommand"> |
|
424 <parameter name="aEvent"/> |
|
425 <body> |
|
426 <![CDATA[ |
|
427 if (!("buttonInfo" in aEvent.target)) |
|
428 return; |
|
429 |
|
430 var button = aEvent.target.buttonInfo; |
|
431 if (button.popup) { |
|
432 document.getElementById(button.popup). |
|
433 openPopup(aEvent.originalTarget, "after_start", 0, 0, false, false, aEvent); |
|
434 aEvent.stopPropagation(); |
|
435 } |
|
436 else { |
|
437 var callback = button.callback; |
|
438 if (callback) { |
|
439 var result = callback(this, button); |
|
440 if (!result) |
|
441 this.close(); |
|
442 aEvent.stopPropagation(); |
|
443 } |
|
444 } |
|
445 ]]> |
|
446 </body> |
|
447 </method> |
|
448 </implementation> |
|
449 </binding> |
|
450 |
|
451 <binding id="popup-notification"> |
|
452 <content align="start"> |
|
453 <xul:image class="popup-notification-icon" |
|
454 xbl:inherits="popupid,src=icon"/> |
|
455 <xul:vbox flex="1"> |
|
456 <xul:description class="popup-notification-description" |
|
457 xbl:inherits="xbl:text=label"/> |
|
458 <children includes="popupnotificationcontent"/> |
|
459 <xul:label class="text-link popup-notification-learnmore-link" |
|
460 xbl:inherits="href=learnmoreurl">&learnMore;</xul:label> |
|
461 <xul:spacer flex="1"/> |
|
462 <xul:hbox class="popup-notification-button-container" |
|
463 pack="end" align="center"> |
|
464 <xul:button anonid="button" |
|
465 class="popup-notification-menubutton" |
|
466 type="menu-button" |
|
467 xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey"> |
|
468 <xul:menupopup anonid="menupopup" |
|
469 xbl:inherits="oncommand=menucommand"> |
|
470 <children/> |
|
471 <xul:menuitem class="menuitem-iconic popup-notification-closeitem" |
|
472 label="&closeNotificationItem.label;" |
|
473 xbl:inherits="oncommand=closeitemcommand,hidden=hidenotnow"/> |
|
474 </xul:menupopup> |
|
475 </xul:button> |
|
476 </xul:hbox> |
|
477 </xul:vbox> |
|
478 <xul:vbox pack="start"> |
|
479 <xul:toolbarbutton anonid="closebutton" |
|
480 class="messageCloseButton close-icon popup-notification-closebutton tabbable" |
|
481 xbl:inherits="oncommand=closebuttoncommand" |
|
482 tooltiptext="&closeNotification.tooltip;"/> |
|
483 </xul:vbox> |
|
484 </content> |
|
485 <resources> |
|
486 <stylesheet src="chrome://global/skin/notification.css"/> |
|
487 </resources> |
|
488 <implementation> |
|
489 <field name="closebutton" readonly="true"> |
|
490 document.getAnonymousElementByAttribute(this, "anonid", "closebutton"); |
|
491 </field> |
|
492 <field name="button" readonly="true"> |
|
493 document.getAnonymousElementByAttribute(this, "anonid", "button"); |
|
494 </field> |
|
495 <field name="menupopup" readonly="true"> |
|
496 document.getAnonymousElementByAttribute(this, "anonid", "menupopup"); |
|
497 </field> |
|
498 </implementation> |
|
499 </binding> |
|
500 </bindings> |