mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
581 lines
20 KiB
XML
581 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/. -->
|
|
|
|
|
|
<bindings id="popupBindings"
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
xmlns:xbl="http://www.mozilla.org/xbl">
|
|
|
|
<binding id="popup-base">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/popup.css"/>
|
|
</resources>
|
|
|
|
<implementation implements="nsIDOMXULPopupElement">
|
|
<property name="label" onget="return this.getAttribute('label');"
|
|
onset="this.setAttribute('label', val); return val;"/>
|
|
<property name="position" onget="return this.getAttribute('position');"
|
|
onset="this.setAttribute('position', val); return val;"/>
|
|
<property name="popupBoxObject">
|
|
<getter>
|
|
return this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject);
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="state" readonly="true"
|
|
onget="return this.popupBoxObject.popupState"/>
|
|
|
|
<property name="triggerNode" readonly="true"
|
|
onget="return this.popupBoxObject.triggerNode"/>
|
|
|
|
<property name="anchorNode" readonly="true"
|
|
onget="return this.popupBoxObject.anchorNode"/>
|
|
|
|
<method name="openPopup">
|
|
<parameter name="aAnchorElement"/>
|
|
<parameter name="aPosition"/>
|
|
<parameter name="aX"/>
|
|
<parameter name="aY"/>
|
|
<parameter name="aIsContextMenu"/>
|
|
<parameter name="aAttributesOverride"/>
|
|
<parameter name="aTriggerEvent"/>
|
|
<body>
|
|
<![CDATA[
|
|
try {
|
|
var popupBox = this.popupBoxObject;
|
|
if (popupBox)
|
|
popupBox.openPopup(aAnchorElement, aPosition, aX, aY,
|
|
aIsContextMenu, aAttributesOverride, aTriggerEvent);
|
|
} catch(e) {}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="openPopupAtScreen">
|
|
<parameter name="aX"/>
|
|
<parameter name="aY"/>
|
|
<parameter name="aIsContextMenu"/>
|
|
<parameter name="aTriggerEvent"/>
|
|
<body>
|
|
<![CDATA[
|
|
try {
|
|
var popupBox = this.popupBoxObject;
|
|
if (popupBox)
|
|
popupBox.openPopupAtScreen(aX, aY, aIsContextMenu, aTriggerEvent);
|
|
} catch(e) {}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="showPopup">
|
|
<parameter name="element"/>
|
|
<parameter name="xpos"/>
|
|
<parameter name="ypos"/>
|
|
<parameter name="popuptype"/>
|
|
<parameter name="anchoralignment"/>
|
|
<parameter name="popupalignment"/>
|
|
<body>
|
|
<![CDATA[
|
|
var popupBox = null;
|
|
var menuBox = null;
|
|
try {
|
|
popupBox = this.popupBoxObject;
|
|
} catch(e) {}
|
|
try {
|
|
menuBox = this.parentNode.boxObject;
|
|
} catch(e) {}
|
|
if (menuBox instanceof Components.interfaces.nsIMenuBoxObject)
|
|
menuBox.openMenu(true);
|
|
else if (popupBox)
|
|
popupBox.showPopup(element, this, xpos, ypos, popuptype, anchoralignment, popupalignment);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="hidePopup">
|
|
<body>
|
|
<![CDATA[
|
|
var popupBox = null;
|
|
var menuBox = null;
|
|
try {
|
|
popupBox = this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject);
|
|
} catch(e) {}
|
|
try {
|
|
menuBox = this.parentNode.boxObject;
|
|
} catch(e) {}
|
|
if (menuBox instanceof Components.interfaces.nsIMenuBoxObject)
|
|
menuBox.openMenu(false);
|
|
else if (popupBox)
|
|
popupBox.hidePopup();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<property name="autoPosition">
|
|
<getter>
|
|
<![CDATA[
|
|
return this.popupBoxObject.autoPosition;
|
|
]]>
|
|
</getter>
|
|
<setter>
|
|
<![CDATA[
|
|
return this.popupBoxObject.autoPosition = val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<method name="enableKeyboardNavigator">
|
|
<parameter name="aEnableKeyboardNavigator"/>
|
|
<body>
|
|
<![CDATA[
|
|
this.popupBoxObject.enableKeyboardNavigator(aEnableKeyboardNavigator);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="enableRollup">
|
|
<parameter name="aEnableRollup"/>
|
|
<body>
|
|
<![CDATA[
|
|
this.popupBoxObject.enableRollup(aEnableRollup);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="sizeTo">
|
|
<parameter name="aWidth"/>
|
|
<parameter name="aHeight"/>
|
|
<body>
|
|
<![CDATA[
|
|
this.popupBoxObject.sizeTo(aWidth, aHeight);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="moveTo">
|
|
<parameter name="aLeft"/>
|
|
<parameter name="aTop"/>
|
|
<body>
|
|
<![CDATA[
|
|
this.popupBoxObject.moveTo(aLeft, aTop);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="moveToAnchor">
|
|
<parameter name="aAnchorElement"/>
|
|
<parameter name="aPosition"/>
|
|
<parameter name="aX"/>
|
|
<parameter name="aY"/>
|
|
<parameter name="aAttributesOverride"/>
|
|
<body>
|
|
<![CDATA[
|
|
this.popupBoxObject.moveToAnchor(aAnchorElement, aPosition, aX, aY, aAttributesOverride);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="getOuterScreenRect">
|
|
<body>
|
|
<![CDATA[
|
|
return this.popupBoxObject.getOuterScreenRect();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
</implementation>
|
|
|
|
</binding>
|
|
|
|
<binding id="popup"
|
|
extends="chrome://global/content/bindings/popup.xml#popup-base">
|
|
|
|
<content>
|
|
<xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
|
|
smoothscroll="false">
|
|
<children/>
|
|
</xul:arrowscrollbox>
|
|
</content>
|
|
|
|
<implementation implements="nsIAccessibleProvider">
|
|
<property name="accessibleType" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
return Components.interfaces.nsIAccessibleProvider.XULMenupopup;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="popupshowing" phase="target">
|
|
<![CDATA[
|
|
var array = [];
|
|
var width = 0;
|
|
for (var menuitem = this.firstChild; menuitem; menuitem = menuitem.nextSibling) {
|
|
if (menuitem.localName == "menuitem" && menuitem.hasAttribute("acceltext")) {
|
|
var accel = document.getAnonymousElementByAttribute(menuitem, "anonid", "accel");
|
|
if (accel && accel.boxObject) {
|
|
array.push(accel);
|
|
if (accel.boxObject.width > width)
|
|
width = accel.boxObject.width;
|
|
}
|
|
}
|
|
}
|
|
for (var i = 0; i < array.length; i++)
|
|
array[i].width = width;
|
|
]]>
|
|
</handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="panel"
|
|
extends="chrome://global/content/bindings/popup.xml#popup-base">
|
|
<!-- This separate binding for dialog-like panels - not menu, list or autocomplete popups
|
|
exposes the popup as an alert or a pane, depending on whether it is always intended
|
|
to get keyboard navigation when it opens -->
|
|
<implementation implements="nsIDOMXULPopupElement, nsIAccessibleProvider">
|
|
<property name="accessibleType" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
return (this.getAttribute("noautofocus") == "true") ?
|
|
Components.interfaces.nsIAccessibleProvider.XULAlert :
|
|
Components.interfaces.nsIAccessibleProvider.XULPane;
|
|
]]></getter>
|
|
</property>
|
|
<field name="_prevFocus">0</field>
|
|
<field name="_dragBindingAlive">true</field>
|
|
<constructor>
|
|
<![CDATA[
|
|
if (this.getAttribute("backdrag") == "true" && !this._draggableStarted) {
|
|
this._draggableStarted = true;
|
|
try {
|
|
let tmp = {};
|
|
Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
|
|
let draghandle = new tmp.WindowDraggingElement(this);
|
|
draghandle.mouseDownCheck = function () this._dragBindingAlive;
|
|
} catch (e) {}
|
|
}
|
|
]]>
|
|
</constructor>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="popupshowing"><![CDATA[
|
|
// Capture the previous focus before has a chance to get set inside the panel
|
|
try {
|
|
this._prevFocus = document.commandDispatcher.focusedElement;
|
|
if (!this._prevFocus) // Content window has focus
|
|
this._prevFocus = document.commandDispatcher.focusedWindow;
|
|
} catch (ex) {
|
|
this._prevFocus = document.activeElement;
|
|
}
|
|
]]></handler>
|
|
<handler event="popupshown"><![CDATA[
|
|
// Fire event for accessibility APIs
|
|
var alertEvent = document.createEvent("Events");
|
|
alertEvent.initEvent("AlertActive", true, true);
|
|
this.dispatchEvent(alertEvent);
|
|
]]></handler>
|
|
<handler event="popuphiding"><![CDATA[
|
|
try {
|
|
this._currentFocus = document.commandDispatcher.focusedElement;
|
|
} catch (e) {
|
|
this._currentFocus = document.activeElement;
|
|
}
|
|
]]></handler>
|
|
<handler event="popuphidden"><![CDATA[
|
|
var currentFocus = this._currentFocus;
|
|
var prevFocus = this._prevFocus;
|
|
this._currentFocus = null;
|
|
this._prevFocus = null;
|
|
if (prevFocus && currentFocus && this.getAttribute("norestorefocus") != "true") {
|
|
// Try to restore focus
|
|
try {
|
|
if (document.commandDispatcher.focusedWindow != window)
|
|
return; // Focus has already been set to a window outside of this panel
|
|
} catch(ex) {}
|
|
while (currentFocus) {
|
|
if (currentFocus == this) {
|
|
// Focus was set on an element inside this panel,
|
|
// so we need to move it back to where it was previously
|
|
try {
|
|
let fm = Components.classes["@mozilla.org/focus-manager;1"]
|
|
.getService(Components.interfaces.nsIFocusManager);
|
|
fm.setFocus(prevFocus, fm.FLAG_NOSCROLL);
|
|
} catch(e) {
|
|
prevFocus.focus();
|
|
}
|
|
return;
|
|
}
|
|
currentFocus = currentFocus.parentNode;
|
|
}
|
|
}
|
|
]]></handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="arrowpanel" extends="chrome://global/content/bindings/popup.xml#panel">
|
|
<content flip="both" side="top" position="bottomcenter topleft">
|
|
<xul:box anonid="container" class="panel-arrowcontainer" flex="1"
|
|
xbl:inherits="side,panelopen">
|
|
<xul:box anonid="arrowbox" class="panel-arrowbox">
|
|
<xul:image anonid="arrow" class="panel-arrow"/>
|
|
</xul:box>
|
|
<xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
|
|
<children/>
|
|
<xul:box class="panel-inner-arrowcontentfooter" xbl:inherits="footertype" hidden="true"/>
|
|
</xul:box>
|
|
</xul:box>
|
|
</content>
|
|
<implementation>
|
|
<field name="_fadeTimer">null</field>
|
|
</implementation>
|
|
<handlers>
|
|
<handler event="popupshowing" phase="target">
|
|
<![CDATA[
|
|
var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
|
|
var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
|
|
var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
|
|
|
|
var anchor = this.anchorNode;
|
|
if (!anchor) {
|
|
arrow.hidden = true;
|
|
return;
|
|
}
|
|
|
|
// Returns whether the first float is smaller than the second float or
|
|
// equals to it in a range of epsilon.
|
|
function smallerTo(aFloat1, aFloat2, aEpsilon)
|
|
{
|
|
return aFloat1 <= (aFloat2 + aEpsilon);
|
|
}
|
|
|
|
let popupRect = this.getBoundingClientRect();
|
|
let popupLeft = window.mozInnerScreenX + popupRect.left;
|
|
let popupTop = window.mozInnerScreenY + popupRect.top;
|
|
let popupRight = popupLeft + popupRect.width;
|
|
let popupBottom = popupTop + popupRect.height;
|
|
|
|
let anchorRect = anchor.getBoundingClientRect();
|
|
let anchorLeft = anchor.ownerDocument.defaultView.mozInnerScreenX + anchorRect.left;
|
|
let anchorTop = anchor.ownerDocument.defaultView.mozInnerScreenY + anchorRect.top;
|
|
let anchorRight = anchorLeft + anchorRect.width;
|
|
let anchorBottom = anchorTop + anchorRect.height;
|
|
|
|
try {
|
|
let anchorWindow = anchor.ownerDocument.defaultView;
|
|
if (anchorWindow != window) {
|
|
let utils = anchorWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
|
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
|
let spp = utils.screenPixelsPerCSSPixel;
|
|
anchorLeft *= spp;
|
|
anchorRight *= spp;
|
|
anchorTop *= spp;
|
|
anchorBottom *= spp;
|
|
}
|
|
} catch(ex) { }
|
|
|
|
const epsilon = 0.2;
|
|
|
|
var horizPos = smallerTo(popupRight, anchorLeft, epsilon) ? -1 : smallerTo(anchorRight, popupLeft, epsilon) ? 1 : 0;
|
|
var vertPos = smallerTo(popupBottom, anchorTop, epsilon) ? -1 : smallerTo(anchorBottom, popupTop, epsilon) ? 1 : 0;
|
|
|
|
var anchorClass = "";
|
|
var hideAnchor = false;
|
|
if (horizPos == 0) {
|
|
container.orient = "vertical";
|
|
arrowbox.orient = "";
|
|
if (vertPos == 0) {
|
|
hideAnchor = true;
|
|
}
|
|
else {
|
|
let pack = "";
|
|
|
|
// We have to guess where to position the arrow given that we don't
|
|
// have access to the parameters passed to |openPopup|.
|
|
|
|
// If the popup is on the left of the anchor.
|
|
if (smallerTo(popupLeft, anchorLeft, epsilon) && smallerTo(popupRight, anchorRight, epsilon)) {
|
|
pack = "end";
|
|
// If the popup is on the right of the anchor.
|
|
} else if (smallerTo(anchorLeft, popupLeft, epsilon) && smallerTo(anchorRight, popupRight, epsilon)) {
|
|
pack = "start"; //(popupLeft + popupRect.width / 2 > anchorRight) ? "start" : "end";
|
|
// If the popup is not on the right nor on the left.
|
|
// Basically, that means one is above the other and one is bigger
|
|
// than the other.
|
|
// In that case, we can't easily choose a position for the arrow so
|
|
// we have to guess depending on which side the popup is more close to.
|
|
} else {
|
|
pack = (Math.abs(popupLeft - anchorLeft) < Math.abs(popupRight - anchorRight)) ? "start" : "end";
|
|
}
|
|
|
|
// In RTL, everything should be inverted.
|
|
if (window.getComputedStyle(this).direction == "rtl") {
|
|
pack = (pack == "start") ? "end" : "start";
|
|
}
|
|
|
|
arrowbox.pack = pack;
|
|
|
|
if (vertPos == 1) {
|
|
container.dir = "";
|
|
anchorClass = "top";
|
|
}
|
|
else if (vertPos == -1) {
|
|
container.dir = "reverse";
|
|
anchorClass = "bottom";
|
|
}
|
|
}
|
|
}
|
|
else if (vertPos == 0) {
|
|
container.orient = "";
|
|
arrowbox.orient = "vertical";
|
|
if (horizPos == 0) {
|
|
hideAnchor = true;
|
|
}
|
|
else {
|
|
let dir = "";
|
|
arrowbox.pack = popupTop + popupRect.height / 2 < anchorTop ? "end" : "start";
|
|
if (horizPos == 1) {
|
|
anchorClass = "left";
|
|
}
|
|
else if (horizPos == -1) {
|
|
dir = "reverse";
|
|
anchorClass = "right";
|
|
}
|
|
// In RTL, everything should be inverted.
|
|
if (window.getComputedStyle(this).direction == "rtl") {
|
|
dir = (dir == "") ? "reverse" : "";
|
|
}
|
|
container.dir = dir;
|
|
}
|
|
}
|
|
else {
|
|
hideAnchor = true;
|
|
}
|
|
arrow.hidden = hideAnchor;
|
|
arrow.setAttribute("side", anchorClass);
|
|
this.setAttribute("side", anchorClass);
|
|
|
|
// set fading
|
|
var fade = this.getAttribute("fade");
|
|
var fadeDelay = (fade == "fast") ? 1 : fade == "slow" ? 4000 : 0;
|
|
if (fadeDelay) {
|
|
this._fadeTimer = setTimeout(function (self) {
|
|
self.style.opacity = 0.2;
|
|
}, fadeDelay, this);
|
|
}
|
|
]]>
|
|
</handler>
|
|
<handler event="popuphiding" phase="target">
|
|
clearTimeout(this._fadeTimer);
|
|
this.style.removeProperty("opacity");
|
|
</handler>
|
|
<handler event="transitionend" phase="target">
|
|
<![CDATA[
|
|
if (event.propertyName == "opacity" &&
|
|
event.originalTarget == this) {
|
|
this.hidePopup();
|
|
this.style.removeProperty("opacity");
|
|
}
|
|
]]>
|
|
</handler>
|
|
<handler event="popupshown" phase="target">
|
|
this.setAttribute("panelopen", "true");
|
|
</handler>
|
|
<handler event="popuphidden" phase="target">
|
|
this.removeAttribute("panelopen");
|
|
</handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="tooltip" extends="chrome://global/content/bindings/popup.xml#popup-base">
|
|
<content>
|
|
<children>
|
|
<xul:label class="tooltip-label" xbl:inherits="xbl:text=label" flex="1"/>
|
|
</children>
|
|
</content>
|
|
|
|
<implementation implements="nsIAccessibleProvider">
|
|
<property name="accessibleType" readonly="true">
|
|
<getter>
|
|
return Components.interfaces.nsIAccessibleProvider.XULTooltip;
|
|
</getter>
|
|
</property>
|
|
|
|
<field name="_mouseOutCount">0</field>
|
|
<field name="_isMouseOver">false</field>
|
|
|
|
<property name="label"
|
|
onget="return this.getAttribute('label');"
|
|
onset="this.setAttribute('label', val); return val;"/>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="mouseover"><![CDATA[
|
|
var rel = event.relatedTarget;
|
|
//dump("ENTERING " + (rel ? rel.localName : "null") + "\n");
|
|
if (!rel)
|
|
return;
|
|
|
|
// find out if the node we entered from is one of our anonymous children
|
|
while (rel) {
|
|
if (rel == this)
|
|
break;
|
|
rel = rel.parentNode;
|
|
}
|
|
|
|
// if the exited node is not a descendant of ours, we are entering for the first time
|
|
if (rel != this)
|
|
this._isMouseOver = true;
|
|
]]></handler>
|
|
|
|
<handler event="mouseout"><![CDATA[
|
|
var rel = event.relatedTarget;
|
|
//dump("LEAVING " + (rel ? rel.localName : "null") + "\n");
|
|
|
|
// relatedTarget is null when the titletip is first shown: a mouseout event fires
|
|
// because the mouse is exiting the main window and entering the titletip "window".
|
|
// relatedTarget is also null when the mouse exits the main window completely,
|
|
// so count how many times relatedTarget was null after titletip is first shown
|
|
// and hide popup the 2nd time
|
|
if (!rel) {
|
|
++this._mouseOutCount;
|
|
if (this._mouseOutCount > 1)
|
|
this.hidePopup();
|
|
return;
|
|
}
|
|
|
|
// find out if the node we are entering is one of our anonymous children
|
|
while (rel) {
|
|
if (rel == this)
|
|
break;
|
|
rel = rel.parentNode;
|
|
}
|
|
|
|
// if the entered node is not a descendant of ours, hide the tooltip
|
|
if (rel != this && this._isMouseOver) {
|
|
this.hidePopup();
|
|
}
|
|
]]></handler>
|
|
|
|
<handler event="popuphiding"><![CDATA[
|
|
this._isMouseOver = false;
|
|
this._mouseOutCount = 0;
|
|
]]></handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="popup-scrollbars" extends="chrome://global/content/bindings/popup.xml#popup">
|
|
<content>
|
|
<xul:hbox class="popup-internal-box" flex="1" orient="vertical" style="overflow: auto;">
|
|
<children/>
|
|
</xul:hbox>
|
|
</content>
|
|
</binding>
|
|
|
|
</bindings>
|
|
|