mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
fd84c776c7
MozReview-Commit-ID: EMjJ3yWt9Wt --HG-- extra : rebase_source : 2b8327e5a7cf0bfd190d696ad5fe475f13faa3cc
531 lines
20 KiB
XML
531 lines
20 KiB
XML
<?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).close();"/>
|
|
</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>
|
|
|
|
<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"/>
|
|
</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: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">
|
|
<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="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>
|