summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/toolbarbutton.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/toolbarbutton.js')
-rw-r--r--toolkit/content/widgets/toolbarbutton.js231
1 files changed, 231 insertions, 0 deletions
diff --git a/toolkit/content/widgets/toolbarbutton.js b/toolkit/content/widgets/toolbarbutton.js
new file mode 100644
index 0000000000..31dd0dc545
--- /dev/null
+++ b/toolkit/content/widgets/toolbarbutton.js
@@ -0,0 +1,231 @@
+/* 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);
+}