Bug 1519577 Convert toolbarbutton to a custom element r=surkov

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

--HG--
extra : rebase_source : 3709ef713f1a7dc9fbc2d0ac97f16bea757fcd2b
This commit is contained in:
Andrew Swan 2019-05-20 10:01:02 -07:00
parent b14c25d6ab
commit a745ec6099
29 changed files with 214 additions and 307 deletions

View File

@ -240,7 +240,7 @@ var BrowserPageActions = {
"pageAction-panel-button"
);
if (action.isBadged) {
buttonNode.classList.add("badged-button");
buttonNode.setAttribute("badged", "true");
}
buttonNode.setAttribute("actionid", action.id);
buttonNode.addEventListener("command", event => {

View File

@ -1136,8 +1136,7 @@ var LibraryUI = {
let animatableBox = document.getElementById("library-animatable-box");
let navBar = document.getElementById("nav-bar");
let libraryIcon = document.getAnonymousElementByAttribute(libraryButton, "class", "toolbarbutton-icon");
let iconBounds = window.windowUtils.getBoundsWithoutFlushing(libraryIcon);
let iconBounds = window.windowUtils.getBoundsWithoutFlushing(libraryButton.icon);
let libraryBounds = window.windowUtils.getBoundsWithoutFlushing(libraryButton);
animatableBox.style.setProperty("--library-button-height", libraryBounds.height + "px");
@ -1197,8 +1196,7 @@ var LibraryUI = {
}
let animatableBox = document.getElementById("library-animatable-box");
let libraryIcon = document.getAnonymousElementByAttribute(libraryButton, "class", "toolbarbutton-icon");
let iconBounds = window.windowUtils.getBoundsWithoutFlushing(libraryIcon);
let iconBounds = window.windowUtils.getBoundsWithoutFlushing(libraryButton.icon);
// Resizing the window will only have the ability to change the X offset of the
// library button.

View File

@ -568,7 +568,7 @@ function UpdateBackForwardCommands(aWebNavigation) {
/**
* Click-and-Hold implementation for the Back and Forward buttons
* XXXmano: should this live in toolbarbutton.xml?
* XXXmano: should this live in toolbarbutton.js?
*/
function SetClickAndHoldHandlers() {
// Bug 414797: Clone the back/forward buttons' context menu into both buttons.
@ -579,13 +579,13 @@ function SetClickAndHoldHandlers() {
let backButton = document.getElementById("back-button");
backButton.setAttribute("type", "menu");
backButton.appendChild(popup);
backButton.prepend(popup);
gClickAndHoldListenersOnElement.add(backButton);
let forwardButton = document.getElementById("forward-button");
popup = popup.cloneNode(true);
forwardButton.setAttribute("type", "menu");
forwardButton.appendChild(popup);
forwardButton.prepend(popup);
gClickAndHoldListenersOnElement.add(forwardButton);
}
@ -600,7 +600,7 @@ const gClickAndHoldListenersOnElement = {
return;
// Prevent the menupopup from opening immediately
aEvent.currentTarget.firstElementChild.hidden = true;
aEvent.currentTarget.menupopup.hidden = true;
aEvent.currentTarget.addEventListener("mouseout", this);
aEvent.currentTarget.addEventListener("mouseup", this);
@ -8391,8 +8391,7 @@ var PanicButtonNotifier = {
popup.addEventListener("popuphidden", removeListeners);
let widget = CustomizableUI.getWidget("panic-button").forWindow(window);
let anchor = widget.anchor;
anchor = document.getAnonymousElementByAttribute(anchor, "class", "toolbarbutton-icon");
let anchor = widget.anchor.icon;
popup.openPopup(anchor, popup.getAttribute("position"));
} catch (ex) {
Cu.reportError(ex);

View File

@ -745,7 +745,8 @@
removable="true"/>
<toolbarbutton id="alltabs-button"
class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button badged-button"
class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button"
badged="true"
oncommand="gTabsPanel.showAllTabsPanel();"
label="&listAllTabs.label;"
tooltiptext="&listAllTabs.label;"
@ -1021,7 +1022,8 @@
during the customization of the toolbar, in the palette, and before
the Downloads Indicator overlay is loaded. -->
<toolbarbutton id="downloads-button"
class="toolbarbutton-1 chromeclass-toolbar-additional badged-button"
class="toolbarbutton-1 chromeclass-toolbar-additional"
badged="true"
key="key_openDownloads"
onmousedown="DownloadsIndicatorView.onCommand(event);"
onkeypress="DownloadsIndicatorView.onCommand(event);"
@ -1057,7 +1059,8 @@
tooltiptext="&libraryButton.tooltip;"
label="&places.library.title;"/>
<toolbarbutton id="fxa-toolbar-menu-button" class="toolbarbutton-1 badged-button chromeclass-toolbar-additional subviewbutton-nav"
<toolbarbutton id="fxa-toolbar-menu-button" class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
badged="true"
onmousedown="gSync.toggleAccountPanel('PanelUI-fxa', event)"
onkeypress="gSync.toggleAccountPanel('PanelUI-fxa', event)"
consumeanchor="fxa-toolbar-menu-button"
@ -1084,7 +1087,8 @@
<toolbaritem id="PanelUI-button"
removable="false">
<toolbarbutton id="PanelUI-menu-button"
class="toolbarbutton-1 badged-button"
class="toolbarbutton-1"
badged="true"
consumeanchor="PanelUI-button"
label="&brandShortName;"
tooltiptext="&appmenu.tooltip;"/>

View File

@ -210,6 +210,9 @@
containersEnabled = false;
}
// There are separate "new tab" buttons for when the tab strip
// is overflowed and when it is not. Attach the long click
// popup to both of them.
const newTab = document.getElementById("new-tab-button");
const newTab2 = document.getAnonymousElementByAttribute(this, "anonid", "tabs-newtab-button");
@ -219,8 +222,8 @@
gClickAndHoldListenersOnElement.remove(parent);
parent.removeAttribute("type");
if (parent.firstElementChild) {
parent.firstElementChild.remove();
if (parent.menupopup) {
parent.menupopup.remove();
}
if (containersEnabled) {
@ -240,7 +243,7 @@
showDefaultTab: Services.prefs.getIntPref("privacy.userContext.longPressBehavior") == 1,
});
});
parent.appendChild(popup);
parent.prepend(popup);
// longPressBehavior == 2 means that the menu is shown after X
// millisecs. Otherwise, with 1, the menu is open immediatelly.

View File

@ -167,8 +167,7 @@ async function test_playing_icon_on_hidden_tab(tab) {
];
let tabContainer = tab.parentNode;
let alltabsButton = document.getElementById("alltabs-button");
let alltabsBadge = document.getAnonymousElementByAttribute(
alltabsButton, "class", "toolbarbutton-badge");
let alltabsBadge = alltabsButton.badgeLabel;
function assertIconShowing() {
is(getComputedStyle(alltabsBadge).backgroundImage,

View File

@ -4438,7 +4438,7 @@ OverflowableToolbar.prototype = {
let mainView = doc.getElementById(mainViewId);
let contextMenu = doc.getElementById(mainView.getAttribute("context"));
gELS.addSystemEventListener(contextMenu, "command", this, true);
let anchor = doc.getAnonymousElementByAttribute(this._chevron, "class", "toolbarbutton-icon");
let anchor = this._chevron.icon;
// Ensure we update the gEditUIVisible flag when opening the popup, in
// case the edit controls are in it.
this._panel.addEventListener("popupshowing", () => doc.defaultView.updateEditUIVisibility(), {once: true});

View File

@ -1348,6 +1348,11 @@ var PanelView = class extends AssociatedToNode {
continue;
}
// Ignore content inside a <toolbarbutton>
if (element.tagName != "toolbarbutton" && element.closest("toolbarbutton")) {
continue;
}
// Take the label for toolbarbuttons; it only exists on those elements.
element = element.multilineLabel || element;

View File

@ -866,11 +866,7 @@ const PanelUI = {
_getBadgeStatus(notification) { return notification.id; },
_getPanelAnchor(candidate) {
let iconAnchor =
document.getAnonymousElementByAttribute(candidate, "class",
"toolbarbutton-badge-stack") ||
document.getAnonymousElementByAttribute(candidate, "class",
"toolbarbutton-icon");
let iconAnchor = candidate.badgeStack || candidate.icon;
return iconAnchor || candidate;
},

View File

@ -30,7 +30,7 @@ const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kForceOverflowWidthPx = 300;
function createDummyXULButton(id, label, win = window) {
let btn = document.createElementNS(kNSXUL, "toolbarbutton");
let btn = win.document.createElementNS(kNSXUL, "toolbarbutton");
btn.id = id;
btn.setAttribute("label", label || id);
btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
@ -445,8 +445,7 @@ function checkContextMenu(aContextMenu, aExpectedEntries, aWindow = window) {
function waitForOverflowButtonShown(win = window) {
let ov = win.document.getElementById("nav-bar-overflow-button");
let icon = win.document.getAnonymousElementByAttribute(ov, "class", "toolbarbutton-icon");
return waitForElementShown(icon);
return waitForElementShown(ov.icon);
}
function waitForElementShown(element) {
let win = element.ownerGlobal;

View File

@ -613,11 +613,10 @@ const DownloadsIndicatorView = {
let widgetGroup = CustomizableUI.getWidget("downloads-button");
if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
let overflowIcon = widgetGroup.forWindow(window).anchor;
return document.getAnonymousElementByAttribute(overflowIcon, "class", "toolbarbutton-icon");
return overflowIcon.icon;
}
return document.getAnonymousElementByAttribute(this.indicator, "class",
"toolbarbutton-badge-stack");
return this.indicator.badgeStack;
},
get _progressIcon() {

View File

@ -34,9 +34,8 @@ add_task(async function test_overflow_anchor() {
let panel = DownloadsPanel.panel;
let chevron = document.getElementById("nav-bar-overflow-button");
let chevronIcon = document.getAnonymousElementByAttribute(chevron,
"class", "toolbarbutton-icon");
is(panel.anchorNode, chevronIcon, "Panel should be anchored to the chevron`s icon.");
is(panel.anchorNode, chevron.icon, "Panel should be anchored to the chevron`s icon.");
DownloadsPanel.hidePanel();
@ -47,8 +46,7 @@ add_task(async function test_overflow_anchor() {
EventUtils.sendMouseEvent({ type: "mousedown", button: 0 }, button.node);
await promise;
let downloadsAnchor = document.getAnonymousElementByAttribute(button.node, "class",
"toolbarbutton-badge-stack");
let downloadsAnchor = button.node.badgeStack;
is(panel.anchorNode, downloadsAnchor);
DownloadsPanel.hidePanel();

View File

@ -275,8 +275,7 @@ class ExtensionControlledPopup {
// Anchor to a toolbar browserAction if found, otherwise use the menu button.
anchorButton = action || doc.getElementById("PanelUI-menu-button");
}
let anchor = doc.getAnonymousElementByAttribute(
anchorButton, "class", "toolbarbutton-icon");
let anchor = anchorButton.icon;
panel.hidden = false;
popupnotification.show();
panel.openPopup(anchor);

View File

@ -165,8 +165,8 @@ this.browserAction = class extends ExtensionAPI {
},
onCreated: node => {
node.classList.add("badged-button");
node.classList.add("webextension-browser-action");
node.setAttribute("badged", "true");
node.setAttribute("constrain-size", "true");
node.setAttribute("data-extensionid", this.extension.id);

View File

@ -129,8 +129,7 @@ async function runTests(options) {
is(button.getAttribute("disabled") == "true", !details.enabled, "disabled state is correct");
if (details.badge) {
let badge = button.ownerDocument.getAnonymousElementByAttribute(
button, "class", "toolbarbutton-badge");
let badge = button.badgeLabel;
let style = window.getComputedStyle(badge);
let expected = {
backgroundColor: serializeColor(details.badgeBackgroundColor),
@ -426,7 +425,7 @@ add_task(async function testBadgeColorPersistence() {
function getBadgeForWindow(win) {
const widget = getBrowserActionWidget(extension).forWindow(win).node;
return document.getAnonymousElementByAttribute(widget, "class", "toolbarbutton-badge");
return widget.badgeLabel;
}
let badge = getBadgeForWindow(window);

View File

@ -247,7 +247,7 @@ add_task(async function browseraction_contextmenu_manage_extension() {
info("Wait until the overflow menu is ready");
let overflowButton = win.document.getElementById("nav-bar-overflow-button");
let icon = win.document.getAnonymousElementByAttribute(overflowButton, "class", "toolbarbutton-icon");
let icon = overflowButton.icon;
await waitForElementShown(icon);
if (!customizing) {
@ -326,7 +326,7 @@ async function runTestContextMenu({
info("Wait until the overflow menu is ready");
let overflowButton = win.document.getElementById("nav-bar-overflow-button");
let icon = win.document.getAnonymousElementByAttribute(overflowButton, "class", "toolbarbutton-icon");
let icon = overflowButton.icon;
await waitForElementShown(icon);
if (!customizing) {

View File

@ -1519,7 +1519,7 @@ PlacesToolbar.prototype = {
// If the menu is open, close it.
if (draggedElt.open) {
draggedElt.lastElementChild.hidePopup();
draggedElt.menupopup.hidePopup();
draggedElt.open = false;
}
}

View File

@ -289,7 +289,7 @@ function getNodeForToolbarItem(itemGuid, validator) {
// Don't search in queries, they could contain our item in a
// different position. Search only folders
if (PlacesUtils.nodeIsFolder(child._placesNode)) {
var popup = child.lastElementChild;
var popup = child.menupopup;
popup.openPopup();
var foundNode = findNode(popup);
popup.hidePopup();

View File

@ -37,7 +37,7 @@ add_task(async function test() {
Assert.equal(menuButton.type, "menu", "A menu button");
// Mouse over the menu button to open it.
let buttonPopup = menuButton.firstElementChild;
let buttonPopup = menuButton.menupopup;
promise = promiseEvent(buttonPopup, "popupshown");
EventUtils.synthesizeMouse(menuButton, 5, 5, { type: "mousemove" });
await promise;

View File

@ -87,9 +87,7 @@ var UITour = {
// Otherwise use the sync setup icon.
let statusButton = aDocument.getElementById("appMenu-fxa-label");
return aDocument.getAnonymousElementByAttribute(statusButton,
"class",
"toolbarbutton-icon");
return statusButton.icon;
},
// This is a fake widgetName starting with the "appMenu-" prefix so we know
// to automatically open the appMenu when annotating this target.

View File

@ -6,7 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=987230
-->
<window title="Mozilla Bug 987230"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="SimpleTest.waitForFocus(nextTest, window)">
onload="SimpleTest.waitForFocus(startTest, window)">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
@ -108,9 +108,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=987230
popup.openPopup(anchor, "bottomcenter topright");
}
let popup = document.getElementById("mypopup");
let outerAnchor = document.getElementById("toolbarbutton-anchor");
let anchor = document.getAnonymousElementByAttribute(outerAnchor, "class", "toolbarbutton-icon");
let popup, outerAnchor, anchor;
function startTest() {
popup = document.getElementById("mypopup");
outerAnchor = document.getElementById("toolbarbutton-anchor");
anchor = outerAnchor.icon;
nextTest();
}
function nextTest(e) {
synthesizeMouse(outerAnchor, 5, 5, {});

View File

@ -32,7 +32,7 @@ add_task(async function test_button_background_properties() {
await extension.startup();
let toolbarButton = document.querySelector("#home-button");
let toolbarButtonIcon = document.getAnonymousElementByAttribute(toolbarButton, "class", "toolbarbutton-icon");
let toolbarButtonIcon = toolbarButton.icon;
let toolbarButtonIconCS = window.getComputedStyle(toolbarButtonIcon);
InspectorUtils.addPseudoClassLock(toolbarButton, ":hover");

View File

@ -659,6 +659,7 @@ if (!isDummyDocument) {
"chrome://global/content/elements/textbox.js",
"chrome://global/content/elements/tabbox.js",
"chrome://global/content/elements/text.js",
"chrome://global/content/elements/toolbarbutton.js",
"chrome://global/content/elements/tree.js",
"chrome://global/content/elements/wizard.js",
]) {

View File

@ -62,7 +62,6 @@ toolkit.jar:
#endif
content/global/widgets.css
content/global/bindings/autocomplete.xml (widgets/autocomplete.xml)
content/global/bindings/button.xml (widgets/button.xml)
content/global/bindings/calendar.js (widgets/calendar.js)
content/global/bindings/datekeeper.js (widgets/datekeeper.js)
content/global/bindings/datepicker.js (widgets/datepicker.js)
@ -77,7 +76,6 @@ toolkit.jar:
* content/global/bindings/textbox.xml (widgets/textbox.xml)
content/global/bindings/timekeeper.js (widgets/timekeeper.js)
content/global/bindings/timepicker.js (widgets/timepicker.js)
content/global/bindings/toolbarbutton.xml (widgets/toolbarbutton.xml)
content/global/bindings/wizard.xml (widgets/wizard.xml)
content/global/elements/autocomplete-popup.js (widgets/autocomplete-popup.js)
content/global/elements/autocomplete-richlistitem.js (widgets/autocomplete-richlistitem.js)
@ -103,6 +101,7 @@ toolkit.jar:
content/global/elements/tabbox.js (widgets/tabbox.js)
content/global/elements/text.js (widgets/text.js)
content/global/elements/textbox.js (widgets/textbox.js)
content/global/elements/toolbarbutton.js (widgets/toolbarbutton.js)
content/global/elements/videocontrols.js (widgets/videocontrols.js)
content/global/elements/tree.js (widgets/tree.js)
content/global/elements/wizard.js (widgets/wizard.js)

View File

@ -214,6 +214,8 @@
MozXULElement.implementCustomInterface(MozButtonBase, [Ci.nsIDOMXULButtonElement]);
MozElements.ButtonBase = MozButtonBase;
class MozButton extends MozButtonBase {
static get inheritedAttributes() {
return {

View File

@ -1,192 +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/. -->
<bindings id="buttonBindings"
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="button-base" extends="chrome://global/content/bindings/general.xml#basetext">
<implementation implements="nsIDOMXULButtonElement">
<property name="type"
onget="return this.getAttribute('type');"
onset="this.setAttribute('type', val); return val;"/>
<property name="dlgType"
onget="return this.getAttribute('dlgtype');"
onset="this.setAttribute('dlgtype', val); return val;"/>
<property name="group"
onget="return this.getAttribute('group');"
onset="this.setAttribute('group', val); return val;"/>
<property name="open" onget="return this.hasAttribute('open');">
<setter><![CDATA[
if (this.hasMenu()) {
this.openMenu(val);
} else if (val) {
// Fall back to just setting the attribute
this.setAttribute("open", "true");
} else {
this.removeAttribute("open");
}
return val;
]]></setter>
</property>
<property name="checked" onget="return this.hasAttribute('checked');">
<setter><![CDATA[
if (this.type == "radio" && val) {
var sibs = this.parentNode.getElementsByAttribute("group", this.group);
for (var i = 0; i < sibs.length; ++i)
sibs[i].removeAttribute("checked");
}
if (val)
this.setAttribute("checked", "true");
else
this.removeAttribute("checked");
return val;
]]></setter>
</property>
<method name ="filterButtons">
<parameter name="node"/>
<body>
<![CDATA[
// if the node isn't visible, don't descend into it.
var cs = node.ownerGlobal.getComputedStyle(node);
if (cs.visibility != "visible" || cs.display == "none") {
return NodeFilter.FILTER_REJECT;
}
// but it may be a popup element, in which case we look at "state"...
if (cs.display == "-moz-popup" && node.state != "open") {
return NodeFilter.FILTER_REJECT;
}
// OK - the node seems visible, so it is a candidate.
if (node.localName == "button" && node.accessKey && !node.disabled)
return NodeFilter.FILTER_ACCEPT;
return NodeFilter.FILTER_SKIP;
]]>
</body>
</method>
<method name="fireAccessKeyButton">
<parameter name="aSubtree"/>
<parameter name="aAccessKeyLower"/>
<body>
<![CDATA[
var iterator = aSubtree.ownerDocument.createTreeWalker(aSubtree,
NodeFilter.SHOW_ELEMENT,
this.filterButtons);
while (iterator.nextNode()) {
var test = iterator.currentNode;
if (test.accessKey.toLowerCase() == aAccessKeyLower &&
!test.disabled && !test.collapsed && !test.hidden) {
test.focus();
test.click();
return true;
}
}
return false;
]]>
</body>
</method>
<method name="_handleClick">
<body>
<![CDATA[
if (!this.disabled) {
if (this.type == "checkbox") {
this.checked = !this.checked;
} else if (this.type == "radio") {
this.checked = true;
}
}
]]>
</body>
</method>
</implementation>
<handlers>
<!-- While it would seem we could do this by handling oncommand, we can't
because any external oncommand handlers might get called before ours,
and then they would see the incorrect value of checked. Additionally
a command attribute would redirect the command events anyway.-->
<handler event="click" button="0" action="this._handleClick();"/>
<handler event="keypress" key=" ">
<![CDATA[
this._handleClick();
// Prevent page from scrolling on the space key.
event.preventDefault();
]]>
</handler>
<handler event="keypress">
<![CDATA[
if (this.hasMenu()) {
if (this.open)
return;
} else {
if (event.keyCode == KeyEvent.DOM_VK_UP ||
(event.keyCode == KeyEvent.DOM_VK_LEFT &&
document.defaultView.getComputedStyle(this.parentNode)
.direction == "ltr") ||
(event.keyCode == KeyEvent.DOM_VK_RIGHT &&
document.defaultView.getComputedStyle(this.parentNode)
.direction == "rtl")) {
event.preventDefault();
window.document.commandDispatcher.rewindFocus();
return;
}
if (event.keyCode == KeyEvent.DOM_VK_DOWN ||
(event.keyCode == KeyEvent.DOM_VK_RIGHT &&
document.defaultView.getComputedStyle(this.parentNode)
.direction == "ltr") ||
(event.keyCode == KeyEvent.DOM_VK_LEFT &&
document.defaultView.getComputedStyle(this.parentNode)
.direction == "rtl")) {
event.preventDefault();
window.document.commandDispatcher.advanceFocus();
return;
}
}
if (event.keyCode || event.charCode <= 32 || event.altKey ||
event.ctrlKey || event.metaKey)
return; // No printable char pressed, not a potential accesskey
// Possible accesskey pressed
var charPressedLower = String.fromCharCode(event.charCode).toLowerCase();
// If the accesskey of the current button is pressed, just activate it
if (this.accessKey.toLowerCase() == charPressedLower) {
this.click();
return;
}
// Search for accesskey in the list of buttons for this doc and each subdoc
// Get the buttons for the main document and all sub-frames
for (var frameCount = -1; frameCount < window.top.frames.length; frameCount++) {
var doc = (frameCount == -1) ? window.top.document :
window.top.frames[frameCount].document;
if (this.fireAccessKeyButton(doc.documentElement, charPressedLower))
return;
}
// Test anonymous buttons
var dlg = window.top.document;
var buttonBox = dlg.getAnonymousElementByAttribute(dlg.documentElement,
"anonid", "buttons");
if (buttonBox)
this.fireAccessKeyButton(buttonBox, charPressedLower);
]]>
</handler>
</handlers>
</binding>
</bindings>

View File

@ -0,0 +1,153 @@
/* 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() {
return {
".toolbarbutton-icon": "validate,src=image,label,type,consumeanchor,triggeringprincipal=iconloadingprincipal",
".toolbarbutton-text": "value=label,accesskey,crop,dragover-top,wrap",
".toolbarbutton-multiline-text": "text=label,accesskey,wrap",
".toolbarbutton-menu-dropmarker": "disabled,label",
".toolbarbutton-badge": "value=badge,style=badgeStyle",
};
}
static get fragment() {
let frag = document.importNode(MozXULElement.parseXULToFragment(`
<image class="toolbarbutton-icon"></image>
<label class="toolbarbutton-text" crop="right" flex="1"></label>
<label class="toolbarbutton-multiline-text" flex="1"></label>
<dropmarker type="menu" class="toolbarbutton-menu-dropmarker"></dropmarker>`), 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"/>
<label class="toolbarbutton-badge" top="0" end="0" crop="none"/>
</stack>
<label class="toolbarbutton-text" crop="right" flex="1"/>
<label class="toolbarbutton-multiline-text" flex="1"/>
<dropmarker anonid="dropmarker" type="menu"
class="toolbarbutton-menu-dropmarker"/>`), true);
Object.defineProperty(this, "badgedFragment", {value: frag});
return frag;
}
get _hasRendered() {
return (this.querySelector(":scope > .toolbarbutton-text") != null);
}
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 (moveChildren.length > 0) {
let {badgeStack, icon} = this;
for (let child of moveChildren) {
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));
// XBL toolbarbutton explicitly places any <box> children
// right before the menu marker.
for (let child of moveChildren) {
this.insertBefore(child, this.lastChild);
}
}
this.initializeAttributeInheritance();
}
get icon() {
return this.querySelector(".toolbarbutton-icon");
}
get badgeLabel() {
return this.querySelector(".toolbarbutton-badge");
}
get badgeStack() {
return this.querySelector(".toolbarbutton-badge-stack");
}
get multilineLabel() {
return this.querySelector(".toolbarbutton-multiline-text");
}
get dropmarker() {
return this.querySelector(".toolbarbutton-menu-dropmarker");
}
get menupopup() {
return this.querySelector("menupopup");
}
}
customElements.define("toolbarbutton", MozToolbarbutton);
}

View File

@ -1,48 +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/. -->
<bindings id="toolbarbuttonBindings"
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="toolbarbutton"
extends="chrome://global/content/bindings/button.xml#button-base">
<implementation>
<property name="multilineLabel"
onget="return document.getAnonymousElementByAttribute(this, 'class', 'toolbarbutton-multiline-text');" />
</implementation>
<content>
<children includes="observes|template|menupopup|panel|tooltip"/>
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,type,consumeanchor,triggeringprincipal=iconloadingprincipal"/>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop,dragover-top,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey,wrap"/>
<children includes="box"/>
<xul:dropmarker anonid="dropmarker" type="menu"
class="toolbarbutton-menu-dropmarker" xbl:inherits="disabled,label"/>
</content>
</binding>
<binding id="toolbarbutton-badged"
extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
<content>
<children includes="observes|template|menupopup|panel|tooltip"/>
<xul:stack class="toolbarbutton-badge-stack">
<children/>
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,consumeanchor"/>
<xul:label class="toolbarbutton-badge" xbl:inherits="value=badge,style=badgeStyle" top="0" end="0" crop="none"/>
</xul:stack>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey,wrap"/>
<xul:dropmarker anonid="dropmarker" type="menu"
class="toolbarbutton-menu-dropmarker" xbl:inherits="disabled,label"/>
</content>
</binding>
</bindings>

View File

@ -107,15 +107,6 @@ label html|span.accesskey {
/********** toolbarbutton **********/
toolbarbutton {
-moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton");
}
toolbarbutton.badged-button > toolbarbutton,
toolbarbutton.badged-button {
-moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-badged");
}
.toolbarbutton-badge:not([value]),
.toolbarbutton-badge[value=""] {
display: none;