/* 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(`