Bug 1500626 - Convert <menuitem> bindings to a Custom Element r=surkov

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Brian Grinstead 2019-05-02 19:28:18 +00:00
Родитель 8f13b0d82f
Коммит 787d9413a8
14 изменённых файлов: 136 добавлений и 129 удалений

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

@ -250,8 +250,9 @@ class SortedItemSelectList {
createItem({label, value, className, disabled}) {
let item = document.createElement("menuitem");
item.value = value;
item.setAttribute("label", label);
if (value)
item.value = value;
if (className)
item.classList.add(className);
if (disabled)

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

@ -198,11 +198,12 @@ var testMouseInteraction = async function() {
click(node, 2);
await onPopupShown;
is(table.menupopup.querySelectorAll("[disabled]").length, 1,
is(table.menupopup.querySelectorAll("menuitem[disabled]").length, 1,
"Only 1 menuitem is disabled");
is(table.menupopup.querySelector("[disabled]"),
is(table.menupopup.querySelector("menuitem[disabled]"),
table.menupopup.querySelector("[data-id='col1']"),
"Which is the unique column");
// popup should be open now
// clicking on second column label
let onPopupHidden = once(table.menupopup, "popuphidden");
@ -226,7 +227,7 @@ var testMouseInteraction = async function() {
click(node, 2);
await onPopupShown;
is(table.menupopup.querySelectorAll("[disabled]").length, 1,
is(table.menupopup.querySelectorAll("menuitem[disabled]").length, 1,
"Only 1 menuitem is disabled");
// popup should be open now
// clicking on second column label
@ -251,12 +252,12 @@ var testMouseInteraction = async function() {
click(node, 2);
await onPopupShown;
is(table.menupopup.querySelectorAll("[disabled]").length, 2,
is(table.menupopup.querySelectorAll("menuitem[disabled]").length, 2,
"2 menuitems are disabled now as only 2 columns remain visible");
is(table.menupopup.querySelectorAll("[disabled]")[0],
is(table.menupopup.querySelectorAll("menuitem[disabled]")[0],
table.menupopup.querySelector("[data-id='col1']"),
"First is the unique column");
is(table.menupopup.querySelectorAll("[disabled]")[1],
is(table.menupopup.querySelectorAll("menuitem[disabled]")[1],
table.menupopup.querySelector("[data-id='col4']"),
"Second is the last column");

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

@ -9,7 +9,7 @@ function testElements(baseid, callback)
var element = elements[t];
// Ignore presentational content inside menus
if (element.closest("menu") && element.closest("[aria-hidden=true]")) {
if (element.closest("menu, menuitem") && element.closest("[aria-hidden=true]")) {
continue;
}

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

@ -2,8 +2,8 @@
== menuitem-key.xul menuitem-key-ref.xul
# these random-if(Android) are due to differences between Android Native & Xul, see bug 732569
random-if(Android) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul
random-if(Android) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul
random-if(Android) == chrome://reftest/content/xul/menulist-shrinkwrap-1.xul chrome://reftest/content/xul/menulist-shrinkwrap-1-ref.xul
random-if(Android) == chrome://reftest/content/xul/menulist-shrinkwrap-2.xul chrome://reftest/content/xul/menulist-shrinkwrap-2-ref.xul
== textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658
# accesskeys are not normally displayed on Mac, so set a pref to enable them
pref(ui.key.menuAccessKey,18) == chrome://reftest/content/xul/accesskey.xul accesskey-ref.xul

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

@ -69,7 +69,6 @@ toolkit.jar:
content/global/bindings/datetimebox.css (widgets/datetimebox.css)
* content/global/bindings/dialog.xml (widgets/dialog.xml)
content/global/bindings/general.xml (widgets/general.xml)
content/global/bindings/menu.xml (widgets/menu.xml)
content/global/bindings/popup.xml (widgets/popup.xml)
content/global/bindings/richlistbox.xml (widgets/richlistbox.xml)
content/global/bindings/scrollbox.xml (widgets/scrollbox.xml)

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

@ -26,14 +26,23 @@ SimpleTest.waitForExplicitFinish();
function runTests()
{
var menu = $("menu");
let menu = $("menu");
let menuitemAddedWhileHidden = menu.appendItem("Added while hidden");
ok(!menuitemAddedWhileHidden.querySelector(".menu-text"), "hidden menuitem hasn't rendered yet.");
menu.menupopup.addEventListener("popupshown", () => {
var submenu = $("submenu");
is(menuitemAddedWhileHidden.querySelector(".menu-text").value, "Added while hidden",
"menuitemAddedWhileHidden item has rendered after shown.");
let menuitemAddedWhileShown = menu.appendItem("Added while shown");
is(menuitemAddedWhileShown.querySelector(".menu-text").value, "Added while shown",
"menuitemAddedWhileShown item has eagerly rendered.");
let submenu = $("submenu");
is(submenu.querySelector(".menu-text").value, "One", "submenu has rendered.");
var submenuDynamic = document.createXULElement("menu");
let submenuDynamic = document.createXULElement("menu");
submenuDynamic.setAttribute("label", "Dynamic");
ok(!submenuDynamic.querySelector(".menu-text"), "dynamic subment hasn't rendered yet.");
ok(!submenuDynamic.querySelector(".menu-text"), "dynamic submenu hasn't rendered yet.");
menu.menupopup.append(submenuDynamic);
is(submenuDynamic.querySelector(".menu-text").value, "Dynamic", "dynamic submenu has rendered.");
@ -44,7 +53,7 @@ function runTests()
function submenuOpened()
{
var submenu = $("submenu");
let submenu = $("submenu");
is(submenu.getAttribute('_moz-menuactive'), "true", "menu highlighted");
submenu.hidden = true;
$("menu").open = false;

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

@ -49,6 +49,9 @@ function test_crash(when, andThen) {
menuitem.addEventListener("mouseup", function (e) {
menuitem.removeEventListener("mouseup", arguments.callee, true);
menuitem.addEventListener("DOMAttrModified", function (e) {
if (e.target != e.currentTarget) {
return;
}
if (e.attrName == "_moz-menuactive") {
if (!attrChanges[e.attrChange])
attrChanges[e.attrChange] = 1;

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

@ -42,7 +42,7 @@ function popupShown()
let index = menulist.selectedIndex;
if (menulist.selectedItem && navigator.platform.includes("Mac")) {
let menulistlabel = menulist.querySelector(".menulist-label");
let mitemlabel = document.getAnonymousElementByAttribute(menulist.selectedItem, "class", "menu-iconic-text");
let mitemlabel = menulist.selectedItem.querySelector(".menu-iconic-text");
ok(isWithinHalfPixel(menulistlabel.getBoundingClientRect().left,
mitemlabel.getBoundingClientRect().left),

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

@ -139,17 +139,113 @@ class MozMenuCaption extends MozMenuBaseMixin(MozXULElement) {
customElements.define("menucaption", MozMenuCaption);
// In general, wait to render menus inside menupopups until they are going to be visible:
// In general, wait to render menus and menuitems inside menupopups
// until they are going to be visible:
window.addEventListener("popupshowing", (e) => {
if (e.originalTarget.ownerDocument != document) {
return;
}
e.originalTarget.setAttribute("hasbeenopened", "true");
for (let menu of e.originalTarget.querySelectorAll("menu")) {
menu.render();
for (let el of e.originalTarget.querySelectorAll("menuitem, menu")) {
el.render();
}
}, { capture: true });
class MozMenuItem extends MozMenuItemBaseMixin(MozXULElement) {
static get inheritedAttributes() {
return {
".menu-iconic-text": "value=label,crop,accesskey,highlightable",
".menu-text": "value=label,crop,accesskey,highlightable",
".menu-iconic-highlightable-text": "text=label,crop,accesskey,highlightable",
".menu-iconic-left": "selected,_moz-menuactive,disabled,checked",
".menu-iconic-icon": "src=image,validate,triggeringprincipal=iconloadingprincipal",
".menu-iconic-accel": "value=acceltext",
".menu-accel": "value=acceltext",
};
}
static get iconicNoAccelFragment() {
// Add aria-hidden="true" on all DOM, since XULMenuAccessible handles accessibility here.
let frag = document.importNode(MozXULElement.parseXULToFragment(`
<hbox class="menu-iconic-left" align="center" pack="center" aria-hidden="true">
<image class="menu-iconic-icon"/>
</hbox>
<label class="menu-iconic-text" flex="1" crop="right" aria-hidden="true"/>
<label class="menu-iconic-highlightable-text" crop="right" aria-hidden="true"/>
`), true);
Object.defineProperty(this, "iconicNoAccelFragment", {value: frag});
return frag;
}
static get iconicFragment() {
let frag = document.importNode(MozXULElement.parseXULToFragment(`
<hbox class="menu-iconic-left" align="center" pack="center" aria-hidden="true">
<image class="menu-iconic-icon"/>
</hbox>
<label class="menu-iconic-text" flex="1" crop="right" aria-hidden="true"/>
<label class="menu-iconic-highlightable-text" crop="right" aria-hidden="true"/>
<hbox class="menu-accel-container" aria-hidden="true">
<label class="menu-iconic-accel"/>
</hbox>
`), true);
Object.defineProperty(this, "iconicFragment", {value: frag});
return frag;
}
static get plainFragment() {
let frag = document.importNode(MozXULElement.parseXULToFragment(`
<label class="menu-text" crop="right" aria-hidden="true"/>
<hbox class="menu-accel-container" aria-hidden="true">
<label class="menu-accel"/>
</hbox>
`), true);
Object.defineProperty(this, "plainFragment", {value: frag});
return frag;
}
get isIconic() {
let type = this.getAttribute("type");
return type == "checkbox" || type == "radio" || this.classList.contains("menuitem-iconic");
}
get isMenulistChild() {
return this.matches("menulist > menupopup > menuitem");
}
get isInHiddenMenupopup() {
return this.matches("menupopup:not([hasbeenopened]) menuitem");
}
render() {
if (this.renderedOnce) {
return;
}
this.renderedOnce = true;
this.textContent = "";
if (this.isMenulistChild) {
this.append(this.constructor.iconicNoAccelFragment.cloneNode(true));
} else if (this.isIconic) {
this.append(this.constructor.iconicFragment.cloneNode(true));
} else {
this.append(this.constructor.plainFragment.cloneNode(true));
}
this.initializeAttributeInheritance();
}
connectedCallback() {
// Eagerly render if we are being inserted into a menulist (since we likely need to
// size it), or into an already-opened menupopup (since we are already visible).
// Checking isConnectedAndReady is an optimization that will let us quickly skip
// non-menulists that are being connected during parse.
if (this.isMenulistChild || (this.isConnectedAndReady && !this.isInHiddenMenupopup)) {
this.render();
}
}
}
customElements.define("menuitem", MozMenuItem);
const isHiddenWindow = document.documentURI == "chrome://browser/content/hiddenWindow.xul";
class MozMenu extends MozMenuBaseMixin(MozElements.MozElementMixin(XULMenuElement)) {
@ -168,15 +264,15 @@ class MozMenu extends MozMenuBaseMixin(MozElements.MozElementMixin(XULMenuElemen
}
get needsEagerRender() {
return this.isMenubarChild || this.isSizingPopup || !this.isInHiddenMenupopup;
return this.isMenubarChild || this.isMenulistChild || !this.isInHiddenMenupopup;
}
get isMenubarChild() {
return this.matches("menubar > menu");
}
get isSizingPopup() {
return this.matches("[sizetopopup] menu") || this.matches("menulist menu");
get isMenulistChild() {
return this.matches("menulist > menupopup > menu");
}
get isInHiddenMenupopup() {

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

@ -1,81 +0,0 @@
<?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/. -->
<!-- globals XULMenuElement -->
<bindings id="menuitemBindings"
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="menuitem-base"
extends="chrome://global/content/bindings/general.xml#basetext">
<implementation implements="nsIDOMXULSelectControlItemElement, nsIDOMXULContainerItemElement">
<property name="value" onset="this.setAttribute('value', val); return val;"
onget="return this.getAttribute('value');"/>
<!-- nsIDOMXULSelectControlItemElement -->
<property name="selected" readonly="true"
onget="return this.getAttribute('selected') == 'true';"/>
<property name="control" readonly="true">
<getter>
<![CDATA[
var parent = this.parentNode;
// Return the parent if it is a menu or menulist.
if (parent && parent.parentNode instanceof XULMenuElement) {
return parent.parentNode;
}
return null;
]]>
</getter>
</property>
<!-- nsIDOMXULContainerItemElement -->
<property name="parentContainer" readonly="true">
<getter>
for (var parent = this.parentNode; parent; parent = parent.parentNode) {
if (parent instanceof XULMenuElement) {
return parent;
}
}
return null;
</getter>
</property>
</implementation>
</binding>
<binding id="menuitem" extends="chrome://global/content/bindings/menu.xml#menuitem-base">
<content>
<xul:label class="menu-text" xbl:inherits="value=label,accesskey,crop,highlightable" crop="right"/>
<xul:hbox class="menu-accel-container" anonid="accel">
<xul:label class="menu-accel" xbl:inherits="value=acceltext"/>
</xul:hbox>
</content>
</binding>
<binding id="menuitem-iconic" extends="chrome://global/content/bindings/menu.xml#menuitem">
<content>
<xul:hbox class="menu-iconic-left" align="center" pack="center"
xbl:inherits="selected,_moz-menuactive,disabled,checked">
<xul:image class="menu-iconic-icon" xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,validate"/>
</xul:hbox>
<xul:label class="menu-iconic-text" flex="1" xbl:inherits="value=label,accesskey,crop,highlightable" crop="right"/>
<xul:label class="menu-iconic-highlightable-text" xbl:inherits="xbl:text=label,crop,accesskey,highlightable" crop="right"/>
<xul:hbox class="menu-accel-container" anonid="accel">
<xul:label class="menu-iconic-accel" xbl:inherits="value=acceltext"/>
</xul:hbox>
</content>
</binding>
<binding id="menuitem-iconic-noaccel" extends="chrome://global/content/bindings/menu.xml#menuitem">
<content>
<xul:hbox class="menu-iconic-left" align="center" pack="center"
xbl:inherits="selected,disabled,checked">
<xul:image class="menu-iconic-icon" xbl:inherits="src=image,validate"/>
</xul:hbox>
<xul:label class="menu-iconic-text" flex="1" xbl:inherits="value=label,accesskey,crop,highlightable" crop="right"/>
<xul:label class="menu-iconic-highlightable-text" xbl:inherits="xbl:text=label,crop,accesskey,highlightable" crop="right"/>
</content>
</binding>
</bindings>

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

@ -156,7 +156,7 @@
var width = 0;
for (var menuitem = this.firstElementChild; menuitem; menuitem = menuitem.nextElementSibling) {
if (menuitem.localName == "menuitem" && menuitem.hasAttribute("acceltext")) {
var accel = document.getAnonymousElementByAttribute(menuitem, "anonid", "accel");
var accel = menuitem.menuAccel;
if (accel) {
array.push(accel);
let accelWidth = accel.getBoundingClientRect().width;

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

@ -225,21 +225,6 @@ menubar > menu:empty {
visibility: collapse;
}
/********* menuitem ***********/
menuitem {
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem");
}
menuitem.menuitem-iconic {
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
}
menuitem[type="checkbox"],
menuitem[type="radio"] {
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
}
.menu-text {
-moz-box-flex: 1;
}
@ -543,10 +528,6 @@ menulist[popuponly="true"] {
border: 0 !important;
}
menulist > menupopup > menuitem {
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic-noaccel");
}
menulist > menupopup > .popup-internal-box > .scrollbutton-up,
menulist > menupopup > .popup-internal-box > .arrowscrollbox-overflow-start-indicator,
menulist > menupopup > .popup-internal-box > .arrowscrollbox-overflow-end-indicator,

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

@ -694,6 +694,9 @@
window.customElements.upgrade(this._stateMenulist);
window.customElements.upgrade(this._enableBtn);
window.customElements.upgrade(this._disableBtn);
window.customElements.upgrade(this._askToActivateMenuitem);
window.customElements.upgrade(this._alwaysActivateMenuitem);
window.customElements.upgrade(this._neverActivateMenuitem);
this._installStatus = document.getAnonymousElementByAttribute(this, "anonid", "install-status");
this._installStatus.mControl = this;

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

@ -2,11 +2,6 @@
* 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/. */
/* Stock icons for the menu bar items */
menuitem:not([type]) {
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
}
#menu_savePage {
list-style-image: url("moz-icon://stock/gtk-save-as?size=menu");
}