From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- toolkit/content/widgets/menu.js | 493 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 toolkit/content/widgets/menu.js (limited to 'toolkit/content/widgets/menu.js') diff --git a/toolkit/content/widgets/menu.js b/toolkit/content/widgets/menu.js new file mode 100644 index 0000000000..f787747a01 --- /dev/null +++ b/toolkit/content/widgets/menu.js @@ -0,0 +1,493 @@ +/* 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. +{ + let imports = {}; + ChromeUtils.defineESModuleGetters(imports, { + ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs", + }); + + const MozMenuItemBaseMixin = Base => { + class MozMenuItemBase extends MozElements.BaseTextMixin(Base) { + // nsIDOMXULSelectControlItemElement + set value(val) { + this.setAttribute("value", val); + } + get value() { + return this.getAttribute("value"); + } + + // nsIDOMXULSelectControlItemElement + get selected() { + return this.getAttribute("selected") == "true"; + } + + // nsIDOMXULSelectControlItemElement + get control() { + var parent = this.parentNode; + // Return the parent if it is a menu or menulist. + if (parent && XULMenuElement.isInstance(parent.parentNode)) { + return parent.parentNode; + } + return null; + } + + // nsIDOMXULContainerItemElement + get parentContainer() { + for (var parent = this.parentNode; parent; parent = parent.parentNode) { + if (XULMenuElement.isInstance(parent)) { + return parent; + } + } + return null; + } + } + MozXULElement.implementCustomInterface(MozMenuItemBase, [ + Ci.nsIDOMXULSelectControlItemElement, + Ci.nsIDOMXULContainerItemElement, + ]); + return MozMenuItemBase; + }; + + const MozMenuBaseMixin = Base => { + class MozMenuBase extends MozMenuItemBaseMixin(Base) { + set open(val) { + this.openMenu(val); + } + + get open() { + return this.hasAttribute("open"); + } + + get itemCount() { + var menupopup = this.menupopup; + return menupopup ? menupopup.children.length : 0; + } + + get menupopup() { + const XUL_NS = + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + + for ( + var child = this.firstElementChild; + child; + child = child.nextElementSibling + ) { + if (child.namespaceURI == XUL_NS && child.localName == "menupopup") { + return child; + } + } + return null; + } + + appendItem(aLabel, aValue) { + var menupopup = this.menupopup; + if (!menupopup) { + menupopup = this.ownerDocument.createXULElement("menupopup"); + this.appendChild(menupopup); + } + + var menuitem = this.ownerDocument.createXULElement("menuitem"); + menuitem.setAttribute("label", aLabel); + menuitem.setAttribute("value", aValue); + + return menupopup.appendChild(menuitem); + } + + getIndexOfItem(aItem) { + var menupopup = this.menupopup; + if (menupopup) { + var items = menupopup.children; + var length = items.length; + for (var index = 0; index < length; ++index) { + if (items[index] == aItem) { + return index; + } + } + } + return -1; + } + + getItemAtIndex(aIndex) { + var menupopup = this.menupopup; + if (!menupopup || aIndex < 0 || aIndex >= menupopup.children.length) { + return null; + } + + return menupopup.children[aIndex]; + } + } + MozXULElement.implementCustomInterface(MozMenuBase, [ + Ci.nsIDOMXULContainerElement, + ]); + return MozMenuBase; + }; + + // The element is used for rendering inside of , + // See SelectParentHelper.jsm. + class MozMenuCaption extends MozMenuBaseMixin(MozXULElement) { + static get inheritedAttributes() { + return { + ".menu-iconic-left": "selected,disabled,checked", + ".menu-iconic-icon": "src=image,validate,src", + ".menu-iconic-text": "value=label,crop,highlightable", + ".menu-iconic-highlightable-text": "text=label,crop,highlightable", + }; + } + + connectedCallback() { + this.textContent = ""; + this.appendChild( + MozXULElement.parseXULToFragment(` + + + + `) + ); + this.initializeAttributeInheritance(); + } + } + + customElements.define("menucaption", MozMenuCaption); + + // 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 el of e.originalTarget.querySelectorAll("menuitem, menu")) { + el.render(); + } + }, + { capture: true } + ); + + class MozMenuItem extends MozMenuItemBaseMixin(MozXULElement) { + static get observedAttributes() { + return super.observedAttributes.concat("acceltext", "key"); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == "acceltext") { + if (this._ignoreAccelTextChange) { + this._ignoreAccelTextChange = false; + } else { + this._accelTextIsDerived = false; + this._computeAccelTextFromKeyIfNeeded(); + } + } + if (name == "key") { + this._computeAccelTextFromKeyIfNeeded(); + } + super.attributeChangedCallback(name, oldValue, newValue); + } + + 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(` + +