diff options
Diffstat (limited to 'toolkit/content/widgets/notification.xml')
-rw-r--r-- | toolkit/content/widgets/notification.xml | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/toolkit/content/widgets/notification.xml b/toolkit/content/widgets/notification.xml new file mode 100644 index 000000000..2cc2f4b2c --- /dev/null +++ b/toolkit/content/widgets/notification.xml @@ -0,0 +1,551 @@ +<?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 % notificationDTD SYSTEM "chrome://global/locale/notification.dtd"> +%notificationDTD; +]> + +<bindings id="notificationBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xbl="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <binding id="notificationbox"> + <content> + <xul:stack xbl:inherits="hidden=notificationshidden" + class="notificationbox-stack"> + <xul:spacer/> + <children includes="notification"/> + </xul:stack> + <children/> + </content> + + <implementation> + <field name="PRIORITY_INFO_LOW" readonly="true">1</field> + <field name="PRIORITY_INFO_MEDIUM" readonly="true">2</field> + <field name="PRIORITY_INFO_HIGH" readonly="true">3</field> + <field name="PRIORITY_WARNING_LOW" readonly="true">4</field> + <field name="PRIORITY_WARNING_MEDIUM" readonly="true">5</field> + <field name="PRIORITY_WARNING_HIGH" readonly="true">6</field> + <field name="PRIORITY_CRITICAL_LOW" readonly="true">7</field> + <field name="PRIORITY_CRITICAL_MEDIUM" readonly="true">8</field> + <field name="PRIORITY_CRITICAL_HIGH" readonly="true">9</field> + <field name="PRIORITY_CRITICAL_BLOCK" readonly="true">10</field> + + <field name="currentNotification">null</field> + + <field name="_closedNotification">null</field> + <field name="_blockingCanvas">null</field> + <field name="_animating">false</field> + + <property name="notificationsHidden" + onget="return this.getAttribute('notificationshidden') == 'true';"> + <setter> + if (val) + this.setAttribute('notificationshidden', true); + else this.removeAttribute('notificationshidden'); + return val; + </setter> + </property> + + <property name="allNotifications" readonly="true"> + <getter> + <![CDATA[ + var closedNotification = this._closedNotification; + var notifications = this.getElementsByTagName('notification'); + return Array.filter(notifications, n => n != closedNotification); + ]]> + </getter> + </property> + + <method name="getNotificationWithValue"> + <parameter name="aValue"/> + <body> + <![CDATA[ + var notifications = this.allNotifications; + for (var n = notifications.length - 1; n >= 0; n--) { + if (aValue == notifications[n].getAttribute("value")) + return notifications[n]; + } + return null; + ]]> + </body> + </method> + + <method name="appendNotification"> + <parameter name="aLabel"/> + <parameter name="aValue"/> + <parameter name="aImage"/> + <parameter name="aPriority"/> + <parameter name="aButtons"/> + <parameter name="aEventCallback"/> + <body> + <![CDATA[ + if (aPriority < this.PRIORITY_INFO_LOW || + aPriority > this.PRIORITY_CRITICAL_BLOCK) + throw "Invalid notification priority " + aPriority; + + // check for where the notification should be inserted according to + // priority. If two are equal, the existing one appears on top. + var notifications = this.allNotifications; + var insertPos = null; + for (var n = notifications.length - 1; n >= 0; n--) { + if (notifications[n].priority < aPriority) + break; + insertPos = notifications[n]; + } + + const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + var newitem = document.createElementNS(XULNS, "notification"); + // Can't use instanceof in case this was created from a different document: + let labelIsDocFragment = aLabel && typeof aLabel == "object" && aLabel.nodeType && + aLabel.nodeType == aLabel.DOCUMENT_FRAGMENT_NODE; + if (!labelIsDocFragment) + newitem.setAttribute("label", aLabel); + newitem.setAttribute("value", aValue); + if (aImage) + newitem.setAttribute("image", aImage); + newitem.eventCallback = aEventCallback; + + if (aButtons) { + // The notification-button-default class is added to the button + // with isDefault set to true. If there is no such button, it is + // added to the first button (unless that button has isDefault + // set to false). There cannot be multiple default buttons. + var defaultElem; + + for (var b = 0; b < aButtons.length; b++) { + var button = aButtons[b]; + var buttonElem = document.createElementNS(XULNS, "button"); + buttonElem.setAttribute("label", button.label); + if (typeof button.accessKey == "string") + buttonElem.setAttribute("accesskey", button.accessKey); + if (typeof button.type == "string") { + buttonElem.setAttribute("type", button.type); + if ((button.type == "menu-button" || button.type == "menu") && + "popup" in button) { + buttonElem.appendChild(button.popup); + delete button.popup; + } + if (typeof button.anchor == "string") + buttonElem.setAttribute("anchor", button.anchor); + } + buttonElem.classList.add("notification-button"); + + if (button.isDefault || + b == 0 && !("isDefault" in button)) + defaultElem = buttonElem; + + newitem.appendChild(buttonElem); + buttonElem.buttonInfo = button; + } + + if (defaultElem) + defaultElem.classList.add("notification-button-default"); + } + + newitem.setAttribute("priority", aPriority); + if (aPriority >= this.PRIORITY_CRITICAL_LOW) + newitem.setAttribute("type", "critical"); + else if (aPriority <= this.PRIORITY_INFO_HIGH) + newitem.setAttribute("type", "info"); + else + newitem.setAttribute("type", "warning"); + + if (!insertPos) { + newitem.style.position = "fixed"; + newitem.style.top = "100%"; + newitem.style.marginTop = "-15px"; + newitem.style.opacity = "0"; + } + this.insertBefore(newitem, insertPos); + // Can only insert the document fragment after the item has been created because + // otherwise the XBL structure isn't there yet: + if (labelIsDocFragment) { + document.getAnonymousElementByAttribute(newitem, "anonid", "messageText") + .appendChild(aLabel); + } + + if (!insertPos) + this._showNotification(newitem, true); + + // Fire event for accessibility APIs + var event = document.createEvent("Events"); + event.initEvent("AlertActive", true, true); + newitem.dispatchEvent(event); + + return newitem; + ]]> + </body> + </method> + + <method name="removeNotification"> + <parameter name="aItem"/> + <parameter name="aSkipAnimation"/> + <body> + <![CDATA[ + if (aItem == this.currentNotification) + this.removeCurrentNotification(aSkipAnimation); + else if (aItem != this._closedNotification) + this._removeNotificationElement(aItem); + return aItem; + ]]> + </body> + </method> + + <method name="_removeNotificationElement"> + <parameter name="aChild"/> + <body> + <![CDATA[ + if (aChild.eventCallback) + aChild.eventCallback("removed"); + this.removeChild(aChild); + + // make sure focus doesn't get lost (workaround for bug 570835) + let fm = Components.classes["@mozilla.org/focus-manager;1"] + .getService(Components.interfaces.nsIFocusManager); + if (!fm.getFocusedElementForWindow(window, false, {})) + fm.moveFocus(window, this, fm.MOVEFOCUS_FORWARD, 0); + ]]> + </body> + </method> + + <method name="removeCurrentNotification"> + <parameter name="aSkipAnimation"/> + <body> + <![CDATA[ + this._showNotification(this.currentNotification, false, aSkipAnimation); + ]]> + </body> + </method> + + <method name="removeAllNotifications"> + <parameter name="aImmediate"/> + <body> + <![CDATA[ + var notifications = this.allNotifications; + for (var n = notifications.length - 1; n >= 0; n--) { + if (aImmediate) + this._removeNotificationElement(notifications[n]); + else + this.removeNotification(notifications[n]); + } + this.currentNotification = null; + + // Must clear up any currently animating notification + if (aImmediate) + this._finishAnimation(); + ]]> + </body> + </method> + + <method name="removeTransientNotifications"> + <body> + <![CDATA[ + var notifications = this.allNotifications; + for (var n = notifications.length - 1; n >= 0; n--) { + var notification = notifications[n]; + if (notification.persistence) + notification.persistence--; + else if (Date.now() > notification.timeout) + this.removeNotification(notification); + } + ]]> + </body> + </method> + + <method name="_showNotification"> + <parameter name="aNotification"/> + <parameter name="aSlideIn"/> + <parameter name="aSkipAnimation"/> + <body> + <![CDATA[ + this._finishAnimation(); + + var height = aNotification.boxObject.height; + var skipAnimation = aSkipAnimation || (height == 0); + + if (aSlideIn) { + this.currentNotification = aNotification; + aNotification.style.removeProperty("position"); + aNotification.style.removeProperty("top"); + aNotification.style.removeProperty("margin-top"); + aNotification.style.removeProperty("opacity"); + + if (skipAnimation) { + this._setBlockingState(this.currentNotification); + return; + } + } + else { + this._closedNotification = aNotification; + var notifications = this.allNotifications; + var idx = notifications.length - 1; + this.currentNotification = (idx >= 0) ? notifications[idx] : null; + + if (skipAnimation) { + this._removeNotificationElement(this._closedNotification); + this._closedNotification = null; + this._setBlockingState(this.currentNotification); + return; + } + + aNotification.style.marginTop = -height + "px"; + aNotification.style.opacity = 0; + } + + this._animating = true; + ]]> + </body> + </method> + + <method name="_finishAnimation"> + <body><![CDATA[ + if (this._animating) { + this._animating = false; + if (this._closedNotification) { + this._removeNotificationElement(this._closedNotification); + this._closedNotification = null; + } + this._setBlockingState(this.currentNotification); + } + ]]></body> + </method> + + <method name="_setBlockingState"> + <parameter name="aNotification"/> + <body> + <![CDATA[ + var isblock = aNotification && + aNotification.priority == this.PRIORITY_CRITICAL_BLOCK; + var canvas = this._blockingCanvas; + if (isblock) { + if (!canvas) + canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); + const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + let content = this.firstChild; + if (!content || + content.namespaceURI != XULNS || + content.localName != "browser") + return; + + var width = content.boxObject.width; + var height = content.boxObject.height; + content.collapsed = true; + + canvas.setAttribute("width", width); + canvas.setAttribute("height", height); + canvas.setAttribute("flex", "1"); + + this.appendChild(canvas); + this._blockingCanvas = canvas; + + var bgcolor = "white"; + try { + var prefService = Components.classes["@mozilla.org/preferences-service;1"]. + getService(Components.interfaces.nsIPrefBranch); + bgcolor = prefService.getCharPref("browser.display.background_color"); + + var win = content.contentWindow; + var context = canvas.getContext("2d"); + context.globalAlpha = 0.5; + context.drawWindow(win, win.scrollX, win.scrollY, + width, height, bgcolor); + } + catch (ex) { } + } + else if (canvas) { + canvas.parentNode.removeChild(canvas); + this._blockingCanvas = null; + let content = this.firstChild; + if (content) + content.collapsed = false; + } + ]]> + </body> + </method> + + </implementation> + + <handlers> + <handler event="transitionend"><![CDATA[ + if (event.target.localName == "notification" && + event.propertyName == "margin-top") + this._finishAnimation(); + ]]></handler> + </handlers> + + </binding> + + <binding id="notification" role="xul:alert"> + <content> + <xul:hbox class="notification-inner" flex="1" xbl:inherits="type"> + <xul:hbox anonid="details" align="center" flex="1" + oncommand="this.parentNode.parentNode._doButtonCommand(event);"> + <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/> + <xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/> + <xul:spacer flex="1"/> + <children/> + </xul:hbox> + <xul:toolbarbutton ondblclick="event.stopPropagation();" + class="messageCloseButton close-icon tabbable" + xbl:inherits="hidden=hideclose" + tooltiptext="&closeNotification.tooltip;" + oncommand="document.getBindingParent(this).dismiss();"/> + </xul:hbox> + </content> + <resources> + <stylesheet src="chrome://global/skin/notification.css"/> + </resources> + <implementation> + <property name="label" onset="this.setAttribute('label', val); return val;" + onget="return this.getAttribute('label');"/> + <property name="value" onset="this.setAttribute('value', val); return val;" + onget="return this.getAttribute('value');"/> + <property name="image" onset="this.setAttribute('image', val); return val;" + onget="return this.getAttribute('image');"/> + <property name="type" onset="this.setAttribute('type', val); return val;" + onget="return this.getAttribute('type');"/> + <property name="priority" onget="return parseInt(this.getAttribute('priority')) || 0;" + onset="this.setAttribute('priority', val); return val;"/> + <property name="persistence" onget="return parseInt(this.getAttribute('persistence')) || 0;" + onset="this.setAttribute('persistence', val); return val;"/> + <field name="timeout">0</field> + + <property name="control" readonly="true"> + <getter> + <![CDATA[ + var parent = this.parentNode; + while (parent) { + if (parent.localName == "notificationbox") + return parent; + parent = parent.parentNode; + } + return null; + ]]> + </getter> + </property> + + <!-- This method should only be called when the user has + manually closed the notification. If you want to + programmatically close the notification, you should + call close() instead. --> + <method name="dismiss"> + <body> + <![CDATA[ + if (this.eventCallback) { + this.eventCallback("dismissed"); + } + this.close(); + ]]> + </body> + </method> + + <method name="close"> + <body> + <![CDATA[ + var control = this.control; + if (control) + control.removeNotification(this); + else + this.hidden = true; + ]]> + </body> + </method> + + <method name="_doButtonCommand"> + <parameter name="aEvent"/> + <body> + <![CDATA[ + if (!("buttonInfo" in aEvent.target)) + return; + + var button = aEvent.target.buttonInfo; + if (button.popup) { + document.getElementById(button.popup). + openPopup(aEvent.originalTarget, "after_start", 0, 0, false, false, aEvent); + aEvent.stopPropagation(); + } + else { + var callback = button.callback; + if (callback) { + var result = callback(this, button, aEvent.target); + if (!result) + this.close(); + aEvent.stopPropagation(); + } + } + ]]> + </body> + </method> + </implementation> + </binding> + + <binding id="popup-notification"> + <content> + <xul:vbox> + <xul:image class="popup-notification-icon" + xbl:inherits="popupid,src=icon,class=iconclass"/> + </xul:vbox> + <xul:vbox class="popup-notification-body" xbl:inherits="popupid"> + <xul:hbox align="start"> + <xul:vbox flex="1"> + <xul:label class="popup-notification-origin header" + xbl:inherits="value=origin,tooltiptext=origin" + crop="center"/> + <xul:description class="popup-notification-description" + xbl:inherits="xbl:text=label,popupid"/> + </xul:vbox> + <xul:toolbarbutton anonid="closebutton" + class="messageCloseButton close-icon popup-notification-closebutton tabbable" + xbl:inherits="oncommand=closebuttoncommand" + tooltiptext="&closeNotification.tooltip;"/> + </xul:hbox> + <children includes="popupnotificationcontent"/> + <xul:label class="text-link popup-notification-learnmore-link" + xbl:inherits="onclick=learnmoreclick,href=learnmoreurl">&learnMore;</xul:label> + <xul:checkbox anonid="checkbox" + xbl:inherits="hidden=checkboxhidden,checked=checkboxchecked,label=checkboxlabel,oncommand=checkboxcommand" /> + <xul:description class="popup-notification-warning" xbl:inherits="hidden=warninghidden,xbl:text=warninglabel"/> + <xul:spacer flex="1"/> + <xul:hbox class="popup-notification-button-container" + pack="end" align="center"> + <children includes="button"/> + <xul:button anonid="button" + class="popup-notification-menubutton" + type="menu-button" + xbl:inherits="oncommand=buttoncommand,onpopupshown=buttonpopupshown,label=buttonlabel,accesskey=buttonaccesskey,disabled=mainactiondisabled"> + <xul:menupopup anonid="menupopup" + xbl:inherits="oncommand=menucommand"> + <children/> + <xul:menuitem class="menuitem-iconic popup-notification-closeitem" + label="&closeNotificationItem.label;" + xbl:inherits="oncommand=closeitemcommand,hidden=hidenotnow"/> + </xul:menupopup> + </xul:button> + </xul:hbox> + </xul:vbox> + </content> + <resources> + <stylesheet src="chrome://global/skin/notification.css"/> + </resources> + <implementation> + <field name="checkbox" readonly="true"> + document.getAnonymousElementByAttribute(this, "anonid", "checkbox"); + </field> + <field name="closebutton" readonly="true"> + document.getAnonymousElementByAttribute(this, "anonid", "closebutton"); + </field> + <field name="button" readonly="true"> + document.getAnonymousElementByAttribute(this, "anonid", "button"); + </field> + <field name="menupopup" readonly="true"> + document.getAnonymousElementByAttribute(this, "anonid", "menupopup"); + </field> + </implementation> + </binding> +</bindings> |