Bug 1531870 - [de-xbl] convert popup binding to custom element; r=bgrins

For now, only add the MozMenuPopup base class to MozElements,
and don't define a custom element for it with
`customElements.define`. This is to help avoid conflicts in
de-xbl work. (See the bug for details.)

Includes a function to do 'manual slotting', moving child
elements into place. Dynamically adding, modifying, or
removing child nodes after the element is connected needs
to be handled manually as well.

Differential Revision: https://phabricator.services.mozilla.com/D25467

--HG--
rename : toolkit/content/widgets/popup.xml => toolkit/content/widgets/menupopup.js
extra : moz-landing-system : lando
This commit is contained in:
Paul Morris 2019-03-30 00:05:08 +00:00
Родитель ff193fae3e
Коммит c3a3d09307
7 изменённых файлов: 189 добавлений и 9 удалений

Просмотреть файл

@ -28,11 +28,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=420499
</menu>
<popupset>
<popup id="contextmenu">
<menupopup id="contextmenu">
<menuitem label="Cut"/>
<menuitem label="Copy"/>
<menuitem label="Paste"/>
</popup>
</menupopup>
<tooltip id="tooltip" orient="vertical">
<description value="This is a tooltip"/>
</tooltip>

Просмотреть файл

@ -687,7 +687,7 @@ void nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent,
mAnchorType = aAnchorType;
// if aAttributesOverride is true, then the popupanchor, popupalign and
// position attributes on the <popup> override those values passed in.
// position attributes on the <menupopup> override those values passed in.
// If false, those attributes are only used if the values passed in are empty
if (aAnchorContent || aAnchorType == MenuPopupAnchorType_Rect) {
nsAutoString anchor, align, position, flip;

Просмотреть файл

@ -2,6 +2,6 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window align="start" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<label value="One"/>
<popup height="40"/>
<menupopup height="40"/>
<label value="Two"/>
</window>

Просмотреть файл

@ -505,6 +505,7 @@ if (!isDummyDocument) {
"chrome://global/content/elements/general.js",
"chrome://global/content/elements/checkbox.js",
"chrome://global/content/elements/menu.js",
"chrome://global/content/elements/menupopup.js",
"chrome://global/content/elements/notificationbox.js",
"chrome://global/content/elements/popupnotification.js",
"chrome://global/content/elements/radio.js",

Просмотреть файл

@ -92,6 +92,7 @@ toolkit.jar:
content/global/elements/editor.js (widgets/editor.js)
content/global/elements/general.js (widgets/general.js)
content/global/elements/menu.js (widgets/menu.js)
content/global/elements/menupopup.js (widgets/menupopup.js)
content/global/elements/notificationbox.js (widgets/notificationbox.js)
content/global/elements/pluginProblem.js (widgets/pluginProblem.js)
content/global/elements/radio.js (widgets/radio.js)

Просмотреть файл

@ -0,0 +1,182 @@
/* 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/. */
"use strict";
// This is loaded into all XUL windows. Wrap in a block to prevent
// leaking to window scope.
{
class MozMenuPopup extends MozElements.MozElementMixin(XULPopupElement) {
constructor() {
super();
this.AUTOSCROLL_INTERVAL = 25;
this.NOT_DRAGGING = 0;
this.DRAG_OVER_BUTTON = -1;
this.DRAG_OVER_POPUP = 1;
this._draggingState = this.NOT_DRAGGING;
this._scrollTimer = 0;
this.addEventListener("popupshowing", event => {
let array = [];
let width = 0;
for (let menuitem = this.firstElementChild; menuitem; menuitem = menuitem.nextElementSibling) {
if (menuitem.localName == "menuitem" && menuitem.hasAttribute("acceltext")) {
let accel = menuitem.querySelector(".menu-accel-container");
if (accel && accel.boxObject) {
array.push(accel);
if (accel.boxObject.width > width) {
width = accel.boxObject.width;
}
}
}
}
array.forEach(accel => accel.width = width);
});
}
connectedCallback() {
if (this.delayConnectedCallback() || this.hasConnected) {
return;
}
this.hasConnected = true;
this.appendChild(MozXULElement.parseXULToFragment(`
<arrowscrollbox class="popup-internal-box"
flex="1"
orient="vertical"
smoothscroll="false"/>
`));
this.scrollBox = this.querySelector(".popup-internal-box");
if (this.parentNode && this.parentNode.localName == "menulist") {
this._setUpMenulistPopup();
}
}
/**
* When a custom element (CE) class extends this MozMenuPopup class,
* and child nodes are present inside that CE in the XUL files
* where it is used, then this method should be called in that CE's
* connectedCallback. It will slot those child nodes into place
* inside the CE's internal scroll box element.
*
* This "manual slotting" is done with this method, and not by default
* in the connectedCallback of this base class, to support cases where
* children are dynamically added, etc. (Which also requires "manual
* slotting".) See bug 1531870.
*/
_setUpChildElements() {
while (this.childElementCount > 1) {
this.scrollBox.appendChild(this.firstElementChild);
}
}
/**
* Adds event listeners for a MozMenuPopup inside a menulist element.
*/
_setUpMenulistPopup() {
this.addEventListener("popupshown", () => {
// Enable drag scrolling even when the mouse wasn't used. The
// mousemove handler will remove it if the mouse isn't down.
this._enableDragScrolling(false);
});
this.addEventListener("popuphidden", () => {
this._draggingState = this.NOT_DRAGGING;
this._clearScrollTimer();
this.releaseCapture();
});
this.addEventListener("mousedown", event => {
if (event.button != 0) {
return;
}
if (this.state == "open" &&
(event.target.localName == "menuitem" ||
event.target.localName == "menu" ||
event.target.localName == "menucaption")) {
this._enableDragScrolling(true);
}
});
this.addEventListener("mouseup", event => {
if (event.button != 0) {
return;
}
this._draggingState = this.NOT_DRAGGING;
this._clearScrollTimer();
});
this.addEventListener("mousemove", event => {
if (!this._draggingState) {
return;
}
this._clearScrollTimer();
// If the user released the mouse before the menupopup opens, we will
// still be capturing, so check that the button is still pressed. If
// not, release the capture and do nothing else. This also handles if
// the dropdown was opened via the keyboard.
if (!(event.buttons & 1)) {
this._draggingState = this.NOT_DRAGGING;
this.releaseCapture();
return;
}
// If dragging outside the top or bottom edge of the menupopup, but
// within the menupopup area horizontally, scroll the list in that
// direction. The _draggingState flag is used to ensure that scrolling
// does not start until the mouse has moved over the menupopup first,
// preventing scrolling while over the dropdown button.
let popupRect = this.getOuterScreenRect();
if (event.screenX >= popupRect.left &&
event.screenX <= popupRect.right) {
if (this._draggingState == this.DRAG_OVER_BUTTON) {
if (event.screenY > popupRect.top &&
event.screenY < popupRect.bottom) {
this._draggingState = this.DRAG_OVER_POPUP;
}
}
if (this._draggingState == this.DRAG_OVER_POPUP &&
(event.screenY <= popupRect.top ||
event.screenY >= popupRect.bottom)) {
let scrollAmount = event.screenY <= popupRect.top ? -1 : 1;
this.scrollBox.scrollByIndex(scrollAmount, true);
let win = this.ownerGlobal;
this._scrollTimer = win.setInterval(() => {
this.scrollBox.scrollByIndex(scrollAmount, true);
}, this.AUTOSCROLL_INTERVAL);
}
}
});
this._menulistPopupIsSetUp = true;
}
_enableDragScrolling(overItem) {
if (!this._draggingState) {
this.setCaptureAlways();
this._draggingState = overItem
? this.DRAG_OVER_POPUP
: this.DRAG_OVER_BUTTON;
}
}
_clearScrollTimer() {
if (this._scrollTimer) {
this.ownerGlobal.clearInterval(this._scrollTimer);
this._scrollTimer = 0;
}
}
}
// Add this MozMenuPopup base class to MozElements, but don't define a custom
// element for it with `customElements.define` (for now, see bug 1531870).
MozElements.MozMenuPopup = MozMenuPopup;
}

Просмотреть файл

@ -288,11 +288,8 @@ menuitem[type="radio"] {
-moz-box-flex: 1;
}
/********* popup & menupopup ***********/
/********* menupopup, panel, & tooltip ***********/
/* <popup> is deprecated. Only <menupopup> and <tooltip> are still valid. */
popup,
menupopup {
-moz-binding: url("chrome://global/content/bindings/popup.xml#popup");
-moz-box-orient: vertical;
@ -303,7 +300,6 @@ panel {
-moz-box-orient: vertical;
}
popup,
menupopup,
panel,
tooltip {