Source code

Revision control

Copy as Markdown

Other Tools

/* 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.
{
const KEEP_CHILDREN = new Set([
"observes",
"template",
"menupopup",
"panel",
"tooltip",
]);
window.addEventListener(
"popupshowing",
e => {
if (e.originalTarget.ownerDocument != document) {
return;
}
e.originalTarget.setAttribute("hasbeenopened", "true");
for (let el of e.originalTarget.querySelectorAll("toolbarbutton")) {
el.render();
}
},
{ capture: true }
);
class MozToolbarbutton extends MozElements.ButtonBase {
static get inheritedAttributes() {
// Note: if you remove 'wrap' or 'label' from the inherited attributes,
// you'll need to add them to observedAttributes.
return {
".toolbarbutton-icon":
"validate,src=image,label,type,consumeanchor,triggeringprincipal=iconloadingprincipal",
".toolbarbutton-text": "accesskey,crop,dragover-top,wrap",
".toolbarbutton-menu-dropmarker": "disabled,label",
".toolbarbutton-badge": "text=badge,style=badgeStyle",
};
}
static get fragment() {
let frag = document.importNode(
MozXULElement.parseXULToFragment(`
<image class="toolbarbutton-icon"></image>
<label class="toolbarbutton-text" crop="end" flex="1"></label>
`),
true
);
Object.defineProperty(this, "fragment", { value: frag });
return frag;
}
static get badgedFragment() {
let frag = document.importNode(
MozXULElement.parseXULToFragment(`
<stack class="toolbarbutton-badge-stack">
<image class="toolbarbutton-icon"/>
<html:label class="toolbarbutton-badge"/>
</stack>
<label class="toolbarbutton-text" crop="end" flex="1"/>
`),
true
);
Object.defineProperty(this, "badgedFragment", { value: frag });
return frag;
}
static get dropmarkerFragment() {
let frag = document.importNode(
MozXULElement.parseXULToFragment(`
<dropmarker type="menu" class="toolbarbutton-menu-dropmarker"></dropmarker>
`),
true
);
Object.defineProperty(this, "dropmarkerFragment", { value: frag });
return frag;
}
get _hasRendered() {
return this.querySelector(":scope > .toolbarbutton-text") != null;
}
get _textNode() {
let node = this.getElementForAttrInheritance(".toolbarbutton-text");
if (node) {
Object.defineProperty(this, "_textNode", { value: node });
}
return node;
}
_setLabel() {
let label = this.getAttribute("label") || "";
let hasLabel = this.hasAttribute("label");
if (this.getAttribute("wrap") == "true") {
this._textNode.removeAttribute("value");
this._textNode.textContent = label;
} else {
this._textNode.textContent = "";
if (hasLabel) {
this._textNode.setAttribute("value", label);
} else {
this._textNode.removeAttribute("value");
}
}
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue || !this.initializedAttributeInheritance) {
return;
}
// Deal with single/multiline label inheritance:
if (name == "label" || name == "wrap") {
this._setLabel();
}
// The normal implementation will deal with everything else.
super.attributeChangedCallback(name, oldValue, newValue);
}
connectedCallback() {
if (this.delayConnectedCallback()) {
return;
}
// Defer creating DOM elements for content inside popups.
// These will be added in the popupshown handler above.
let panel = this.closest("panel");
if (panel && !panel.hasAttribute("hasbeenopened")) {
return;
}
this.render();
}
render() {
if (this._hasRendered) {
return;
}
let badged = this.getAttribute("badged") == "true";
if (badged) {
let moveChildren = [];
for (let child of this.children) {
if (!KEEP_CHILDREN.has(child.tagName)) {
moveChildren.push(child);
}
}
this.appendChild(this.constructor.badgedFragment.cloneNode(true));
if (this.hasAttribute("wantdropmarker")) {
this.appendChild(this.constructor.dropmarkerFragment.cloneNode(true));
}
if (moveChildren.length) {
let { badgeStack, icon } = this;
for (let child of moveChildren) {
if (child.getAttribute("move-after-stack") === "true") {
this.appendChild(child);
} else {
badgeStack.insertBefore(child, icon);
}
}
}
} else {
let moveChildren = [];
for (let child of this.children) {
if (!KEEP_CHILDREN.has(child.tagName) && child.tagName != "box") {
// XBL toolbarbutton doesn't insert any anonymous content
// if it has a child of any other type
return;
}
if (child.tagName == "box") {
moveChildren.push(child);
}
}
this.appendChild(this.constructor.fragment.cloneNode(true));
if (this.hasAttribute("wantdropmarker")) {
this.appendChild(this.constructor.dropmarkerFragment.cloneNode(true));
}
// XBL toolbarbutton explicitly places any <box> children
// right before the menu marker.
for (let child of moveChildren) {
this.insertBefore(child, this.lastChild);
}
}
this.initializeAttributeInheritance();
this._setLabel();
}
get icon() {
return this.querySelector(".toolbarbutton-icon");
}
get badgeLabel() {
return this.querySelector(".toolbarbutton-badge");
}
get badgeStack() {
return this.querySelector(".toolbarbutton-badge-stack");
}
get multilineLabel() {
if (this.getAttribute("wrap") == "true") {
return this._textNode;
}
return null;
}
get dropmarker() {
return this.querySelector(".toolbarbutton-menu-dropmarker");
}
get menupopup() {
return this.querySelector("menupopup");
}
}
customElements.define("toolbarbutton", MozToolbarbutton);
}