commit a71fc67673cb6d488265253ed862d2591536c3ae parent 72d0c90bf2398b3a6e45acf1e945669176d7688a Author: Emilio Cobos Álvarez <emilio@crisal.io> Date: Tue, 6 Jan 2026 21:59:27 +0000 Bug 2008041 - Make XUL disabled / checked attributes html-style boolean attributes. r=desktop-theme-reviewers,tabbrowser-reviewers,extension-reviewers,credential-management-reviewers,devtools-reviewers,fxview-reviewers,translations-reviewers,layout-reviewers,sthompson,nsharpley,robwu,nchevobbe,jwatt,dimi,dao Much like bug 1979014 did for other attributes (these ones were a bit harder because there's also native code that looked at them). Make disabled imply :disabled, and checked imply :checked, which simplifies the theming code while at it. Differential Revision: https://phabricator.services.mozilla.com/D277665 Diffstat:
123 files changed, 427 insertions(+), 612 deletions(-)
diff --git a/accessible/generic/FormControlAccessible.cpp b/accessible/generic/FormControlAccessible.cpp @@ -47,9 +47,7 @@ uint64_t CheckboxAccessible::NativeState() const { return state | states::CHECKED; } - } else if (mContent->AsElement()->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::checked, nsGkAtoms::_true, - eCaseMatters)) { // XUL checkbox + } else if (mContent->AsElement()->GetBoolAttr(nsGkAtoms::checked)) { return state | states::CHECKED; } diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp @@ -426,9 +426,8 @@ uint64_t LocalAccessible::NativeLinkState() const { return 0; } bool LocalAccessible::NativelyUnavailable() const { if (mContent->IsHTMLElement()) return mContent->AsElement()->IsDisabled(); - return mContent->IsElement() && mContent->AsElement()->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::disabled, - nsGkAtoms::_true, eCaseMatters); + return mContent->IsElement() && + mContent->AsElement()->GetBoolAttr(nsGkAtoms::disabled); } Accessible* LocalAccessible::ChildAtPoint(int32_t aX, int32_t aY, diff --git a/accessible/xul/XULMenuAccessible.cpp b/accessible/xul/XULMenuAccessible.cpp @@ -63,9 +63,7 @@ uint64_t XULMenuitemAccessible::NativeState() const { state |= states::CHECKABLE; // Checked? - if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, - nsGkAtoms::checked, nsGkAtoms::_true, - eCaseMatters)) { + if (mContent->AsElement()->GetBoolAttr(nsGkAtoms::checked)) { state |= states::CHECKED; } } diff --git a/browser/actors/AboutReaderParent.sys.mjs b/browser/actors/AboutReaderParent.sys.mjs @@ -173,7 +173,7 @@ export class AboutReaderParent extends JSWindowActorParent { menuitem.hidden = false; doc.l10n.setAttributes(menuitem, "menu-view-close-readerview"); - key.setAttribute("disabled", false); + key.removeAttribute("disabled"); Services.obs.notifyObservers(null, "reader-mode-available"); } else { @@ -184,7 +184,7 @@ export class AboutReaderParent extends JSWindowActorParent { menuitem.hidden = !browser.isArticle; doc.l10n.setAttributes(menuitem, "menu-view-enter-readerview"); - key.setAttribute("disabled", !browser.isArticle); + key.toggleAttribute("disabled", !browser.isArticle); if (browser.isArticle) { Services.obs.notifyObservers(null, "reader-mode-available"); diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js @@ -441,13 +441,8 @@ customElements.define( #setAllowButtonEnabled(allowed) { let disabled = !allowed; // "mainactiondisabled" mirrors the "disabled" boolean attribute of the - // "Allow" button. toggleAttribute("mainactiondisabled", disabled) cannot - // be used due to bug 1938481. - if (disabled) { - this.setAttribute("mainactiondisabled", "true"); - } else { - this.removeAttribute("mainactiondisabled"); - } + // "Allow" button. + this.toggleAttribute("mainactiondisabled", disabled); // The "mainactiondisabled" attribute may also be toggled by the // PopupNotifications._setNotificationUIState() method, which can be @@ -2831,7 +2826,7 @@ var gUnifiedExtensions = { if (forBrowserAction) { let area = CustomizableUI.getPlacementOfWidget(widgetId).area; let inToolbar = area != CustomizableUI.AREA_ADDONS; - pinButton.setAttribute("checked", inToolbar); + pinButton.toggleAttribute("checked", inToolbar); const placement = CustomizableUI.getPlacementOfWidget(widgetId); const notInPanel = placement?.area !== CustomizableUI.AREA_ADDONS; @@ -2918,14 +2913,14 @@ var gUnifiedExtensions = { }, async onPinToToolbarChange(menu, event) { - let shouldPinToToolbar = event.target.getAttribute("checked") == "true"; + let shouldPinToToolbar = event.target.hasAttribute("checked"); // Revert the checkbox back to its original state. This is because the // addon context menu handlers are asynchronous, and there seems to be // a race where the checkbox state won't get set in time to show the // right state. So we err on the side of caution, and presume that future // attempts to open this context menu on an extension button will show // the same checked state that we started in. - event.target.setAttribute("checked", !shouldPinToToolbar); + event.target.toggleAttribute("checked", !shouldPinToToolbar); let widgetId = this._getWidgetId(menu); if (!widgetId) { diff --git a/browser/base/content/browser-customization.js b/browser/base/content/browser-customization.js @@ -48,7 +48,7 @@ var CustomizationHandler = { // Re-enable parts of the UI we disabled during the dialog let menubar = document.getElementById("main-menubar"); for (let childNode of menubar.children) { - childNode.setAttribute("disabled", false); + childNode.removeAttribute("disabled"); } gBrowser.selectedBrowser.focus(); diff --git a/browser/base/content/browser-fullScreenAndPointerLock.js b/browser/base/content/browser-fullScreenAndPointerLock.js @@ -343,11 +343,7 @@ var FullScreen = { // Toggle the View:FullScreen command, which controls elements like the // fullscreen menuitem, and menubars. let fullscreenCommand = document.getElementById("View:FullScreen"); - if (enterFS) { - fullscreenCommand.setAttribute("checked", enterFS); - } else { - fullscreenCommand.removeAttribute("checked"); - } + fullscreenCommand.toggleAttribute("checked", enterFS); if (AppConstants.platform == "macosx") { // Make sure the menu items are adjusted. @@ -835,7 +831,7 @@ var FullScreen = { // Autohide helpers for the context menu item updateAutohideMenuitem(aItem) { - aItem.setAttribute( + aItem.toggleAttribute( "checked", Services.prefs.getBoolPref("browser.fullscreen.autohide") ); diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc @@ -154,7 +154,7 @@ <menuitem id="toggle_zoom" type="checkbox" command="cmd_fullZoomToggle" - checked="false" data-l10n-id="menu-view-full-zoom-toggle"/> + data-l10n-id="menu-view-full-zoom-toggle"/> </menupopup> </menu> <menu id="pageStyleMenu" data-l10n-id="menu-view-page-style-menu"> diff --git a/browser/base/content/browser-pagestyle.js b/browser/base/content/browser-pagestyle.js @@ -55,7 +55,7 @@ var gPageStyleMenu = { menuItem.setAttribute("type", "radio"); menuItem.setAttribute("label", currentStyleSheet.title); menuItem.setAttribute("data", currentStyleSheet.title); - menuItem.setAttribute( + menuItem.toggleAttribute( "checked", !currentStyleSheet.disabled && !styleDisabled ); @@ -69,8 +69,11 @@ var gPageStyleMenu = { } } - noStyle.setAttribute("checked", styleDisabled); - persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled); + noStyle.toggleAttribute("checked", styleDisabled); + persistentOnly.toggleAttribute( + "checked", + !altStyleSelected && !styleDisabled + ); persistentOnly.hidden = styleSheetInfo.preferredStyleSheetSet ? haveAltSheets : false; diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js @@ -1509,7 +1509,7 @@ var BookmarkingUI = { menuItem.setAttribute("type", "radio"); // The persisted state of the PersonalToolbar is stored in // "browser.toolbars.bookmarks.visibility". - menuItem.setAttribute( + menuItem.toggleAttribute( "checked", gBookmarksToolbarVisibility == visibilityEnum ); @@ -2214,9 +2214,9 @@ var BookmarkingUI = { menuItem.setAttribute("id", "show-other-bookmarks_PersonalToolbar"); menuItem.setAttribute("toolbarId", "PersonalToolbar"); menuItem.setAttribute("type", "checkbox"); - menuItem.setAttribute("checked", SHOW_OTHER_BOOKMARKS); menuItem.setAttribute("selection-type", "none|single"); menuItem.setAttribute("start-disabled", "true"); + menuItem.toggleAttribute("checked", SHOW_OTHER_BOOKMARKS); MozXULElement.insertFTLIfNeeded("browser/toolbarContextMenu.ftl"); document.l10n.setAttributes( diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js @@ -1258,7 +1258,7 @@ function HandleAppCommandEvent(evt) { BrowserCommands.reloadSkipCache(); break; case "Stop": - if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true") { + if (XULBrowserWindow.stopCommand.hasAttribute("disabled")) { BrowserCommands.stop(); } break; @@ -2705,7 +2705,7 @@ var CombinedStopReload = { } this._initialized = true; - if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true") { + if (!XULBrowserWindow.stopCommand.hasAttribute("disabled")) { reload.setAttribute("displaystop", "true"); } stop.addEventListener("click", this); @@ -2833,7 +2833,7 @@ var CombinedStopReload = { this._stopClicked = false; this._cancelTransition(); this.reload.disabled = - XULBrowserWindow.reloadCommand.getAttribute("disabled") == "true"; + XULBrowserWindow.reloadCommand.hasAttribute("disabled"); return; } @@ -2848,7 +2848,7 @@ var CombinedStopReload = { function (self) { self._timer = 0; self.reload.disabled = - XULBrowserWindow.reloadCommand.getAttribute("disabled") == "true"; + XULBrowserWindow.reloadCommand.hasAttribute("disabled"); }, 650, this @@ -3029,7 +3029,7 @@ function onViewToolbarCommand(aEvent) { } else { menuId = node.parentNode.id; toolbarId = node.getAttribute("toolbarId"); - isVisible = node.getAttribute("checked") == "true"; + isVisible = node.hasAttribute("checked"); } CustomizableUI.setToolbarVisibility(toolbarId, isVisible); BrowserUsageTelemetry.recordToolbarVisibility(toolbarId, isVisible, menuId); @@ -3135,7 +3135,7 @@ function updateToggleControlLabel(control) { if (!control.hasAttribute("label-unchecked")) { control.setAttribute("label-unchecked", control.getAttribute("label")); } - let prefix = control.getAttribute("checked") == "true" ? "" : "un"; + let prefix = control.hasAttribute("checked") ? "" : "un"; control.setAttribute("label", control.getAttribute(`label-${prefix}checked`)); } @@ -3754,11 +3754,8 @@ var BrowserOffline = { _uiElement: null, _updateOfflineUI(aOffline) { var offlineLocked = Services.prefs.prefIsLocked("network.online"); - if (offlineLocked) { - this._uiElement.setAttribute("disabled", "true"); - } - - this._uiElement.setAttribute("checked", aOffline); + this._uiElement.toggleAttribute("disabled", !!offlineLocked); + this._uiElement.toggleAttribute("checked", aOffline); }, }; @@ -4768,7 +4765,7 @@ var gDialogBox = { continue; } if (!shouldBeEnabled) { - if (element.getAttribute("disabled") != "true") { + if (!element.hasAttribute("disabled")) { element.setAttribute("disabled", true); } else { element.setAttribute("wasdisabled", true); diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml @@ -810,26 +810,22 @@ <menuitem class="always-offer-translations-menuitem" data-l10n-id="translations-panel-settings-always-offer-translation" type="checkbox" - checked="false" autocheck="false" id="translations-panel-settings-always-offer-translation"/> <menuitem class="always-translate-language-menuitem" data-l10n-id="translations-panel-settings-always-translate-unknown-language" type="checkbox" - checked="false" autocheck="false" id="translations-panel-settings-always-translate"/> <menuitem class="never-translate-language-menuitem" data-l10n-id="translations-panel-settings-never-translate-unknown-language" type="checkbox" - checked="false" autocheck="false" id="translations-panel-settings-never-translate"/> <menuseparator/> <menuitem class="never-translate-site-menuitem" data-l10n-id="translations-panel-settings-never-translate-site" type="checkbox" - checked="false" autocheck="false" id="translations-panel-settings-never-translate-site"/> <menuseparator/> diff --git a/browser/base/content/main-popupset.js b/browser/base/content/main-popupset.js @@ -257,7 +257,7 @@ document.addEventListener( ToolbarContextMenu.onDownloadsAutoHideChange(event); break; case "toolbar-context-always-show-extensions-button": - if (event.target.getAttribute("checked") == "true") { + if (event.target.hasAttribute("checked")) { gUnifiedExtensions.showExtensionsButtonInToolbar(); } else { gUnifiedExtensions.hideExtensionsButtonFromToolbar(); diff --git a/browser/base/content/nsContextMenu.sys.mjs b/browser/base/content/nsContextMenu.sys.mjs @@ -981,7 +981,7 @@ export class nsContextMenu { this.showItem("spell-check-enabled", canSpell); document .getElementById("spell-check-enabled") - .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled); + .toggleAttribute("checked", canSpell && InlineSpellCheckerUI.enabled); this.showItem("spell-add-to-dictionary", onMisspelling); this.showItem("spell-undo-add-to-dictionary", showUndo); @@ -1404,11 +1404,7 @@ export class nsContextMenu { let revealPassword = this.document.getElementById( "context-reveal-password" ); - if (this.passwordRevealed) { - revealPassword.setAttribute("checked", "true"); - } else { - revealPassword.removeAttribute("checked"); - } + revealPassword.toggleAttribute("checked", this.passwordRevealed); } this.showItem("context-reveal-password", shouldShow); } @@ -2349,15 +2345,25 @@ export class nsContextMenu { // nicely for the disabled attribute). setItemAttr(aID, aAttr, aVal) { var elem = this.document.getElementById(aID); - if (elem) { - if (aVal == null) { - // null indicates attr should be removed. - elem.removeAttribute(aAttr); - } else { - // Set attr=val. + if (!elem) { + return; + } + if (aVal == null) { + // null indicates attr should be removed. + elem.removeAttribute(aAttr); + return; + } + if (typeof aVal == "boolean") { + // TODO(emilio): Replace this with toggleAttribute, but needs test fixes. + if (aVal) { elem.setAttribute(aAttr, aVal); + } else { + elem.removeAttribute(aAttr); } + return; } + // Set attr=val. + elem.setAttribute(aAttr, aVal); } // Temporary workaround for DOM api not yet implemented by XUL nodes. diff --git a/browser/base/content/test/about/browser_aboutNewTab_bookmarksToolbar.js b/browser/base/content/test/about/browser_aboutNewTab_bookmarksToolbar.js @@ -160,9 +160,9 @@ add_task(async function bookmarks_toolbar_open_persisted() { let newTabMenuItem = document.querySelector( 'menuitem[data-visibility-enum="newtab"]' ); - is(alwaysMenuItem.getAttribute("checked"), "false", "Menuitem isn't checked"); - is(neverMenuItem.getAttribute("checked"), "false", "Menuitem isn't checked"); - is(newTabMenuItem.getAttribute("checked"), "true", "Menuitem is checked"); + ok(!alwaysMenuItem.hasAttribute("checked"), "Menuitem isn't checked"); + ok(!neverMenuItem.hasAttribute("checked"), "Menuitem isn't checked"); + ok(newTabMenuItem.hasAttribute("checked"), "Menuitem is checked"); subMenu.activateItem(alwaysMenuItem); @@ -188,9 +188,9 @@ add_task(async function bookmarks_toolbar_open_persisted() { newTabMenuItem = document.querySelector( 'menuitem[data-visibility-enum="newtab"]' ); - is(alwaysMenuItem.getAttribute("checked"), "true", "Menuitem is checked"); - is(neverMenuItem.getAttribute("checked"), "false", "Menuitem isn't checked"); - is(newTabMenuItem.getAttribute("checked"), "false", "Menuitem isn't checked"); + ok(alwaysMenuItem.hasAttribute("checked"), "Menuitem is checked"); + ok(!neverMenuItem.hasAttribute("checked"), "Menuitem isn't checked"); + ok(!newTabMenuItem.hasAttribute("checked"), "Menuitem isn't checked"); contextMenu.hidePopup(); ok(isBookmarksToolbarVisible(), "Toolbar is visible"); ok(isToolbarPersistedOpen(), "Toolbar is persisted open"); @@ -220,9 +220,9 @@ add_task(async function bookmarks_toolbar_open_persisted() { newTabMenuItem = document.querySelector( 'menuitem[data-visibility-enum="newtab"]' ); - is(alwaysMenuItem.getAttribute("checked"), "true", "Menuitem is checked"); - is(neverMenuItem.getAttribute("checked"), "false", "Menuitem isn't checked"); - is(newTabMenuItem.getAttribute("checked"), "false", "Menuitem isn't checked"); + ok(alwaysMenuItem.hasAttribute("checked"), "Menuitem is checked"); + ok(!neverMenuItem.hasAttribute("checked"), "Menuitem isn't checked"); + ok(!newTabMenuItem.hasAttribute("checked"), "Menuitem isn't checked"); subMenu.activateItem(newTabMenuItem); await waitForBookmarksToolbarVisibility({ visible: false, diff --git a/browser/base/content/test/keyboard/browser_bookmarks_shortcut.js b/browser/base/content/test/keyboard/browser_bookmarks_shortcut.js @@ -82,7 +82,7 @@ async function testIsBookmarksMenuItemStateChecked(expected) { document.querySelector(`menuitem[data-visibility-enum="${e}"]`) ); - let checkedItem = menuitems.filter(m => m.getAttribute("checked") == "true"); + let checkedItem = menuitems.filter(m => m.hasAttribute("checked")); is(checkedItem.length, 1, "should have only one menuitem checked"); is( checkedItem[0].dataset.visibilityEnum, diff --git a/browser/base/content/test/pageStyle/browser_disable_author_style_oop.js b/browser/base/content/test/pageStyle/browser_disable_author_style_oop.js @@ -83,7 +83,7 @@ add_task(async function test_disable_style() { let { menupopup } = document.getElementById("pageStyleMenu"); gPageStyleMenu.fillPopup(menupopup); Assert.equal( - menupopup.querySelector("menuitem[checked='true']").dataset.l10nId, + menupopup.querySelector("menuitem[checked]").dataset.l10nId, "menu-view-page-style-no-style", "No style menu should be checked." ); diff --git a/browser/base/content/test/pageStyle/browser_page_style_menu.js b/browser/base/content/test/pageStyle/browser_page_style_menu.js @@ -43,7 +43,7 @@ add_task(async function test_menu() { let menuitems = fillPopupAndGetItems(); let items = menuitems.map(el => ({ label: el.getAttribute("label"), - checked: el.getAttribute("checked") == "true", + checked: el.hasAttribute("checked"), })); let validLinks = await SpecialPowers.spawn( diff --git a/browser/base/content/test/pageStyle/browser_page_style_menu_update.js b/browser/base/content/test/pageStyle/browser_page_style_menu_update.js @@ -24,7 +24,7 @@ add_task(async function () { // page_style_sample.html should default us to selecting the stylesheet // with the title "6" first. - let selected = menupopup.querySelector("menuitem[checked='true']"); + let selected = menupopup.querySelector("menuitem[checked]"); is( selected.getAttribute("label"), "6", @@ -38,7 +38,7 @@ add_task(async function () { gPageStyleMenu.fillPopup(menupopup); // gPageStyleMenu empties out the menu between opens, so we need // to get a new reference to the selected menuitem - selected = menupopup.querySelector("menuitem[checked='true']"); + selected = menupopup.querySelector("menuitem[checked]"); is( selected.getAttribute("label"), "1", diff --git a/browser/base/content/test/zoom/browser_zoom_commands.js b/browser/base/content/test/zoom/browser_zoom_commands.js @@ -53,8 +53,8 @@ async function waitForCommandEnabledState(expectedState) { function assertTextZoomCommandCheckedState(isChecked) { let command = document.getElementById("cmd_fullZoomToggle"); Assert.equal( - command.getAttribute("checked"), - "" + isChecked, + command.hasAttribute("checked"), + isChecked, "Text zoom command has expected checked attribute" ); } diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs @@ -2572,9 +2572,7 @@ var CustomizableUIInternal = { node.setAttribute("id", aWidget.id); node.setAttribute("widget-id", aWidget.id); node.setAttribute("widget-type", aWidget.type); - if (aWidget.disabled) { - node.setAttribute("disabled", true); - } + node.toggleAttribute("disabled", !!aWidget.disabled); node.setAttribute("removable", aWidget.removable); node.setAttribute("overflows", aWidget.overflows); if (aWidget.tabSpecific) { diff --git a/browser/components/customizableui/CustomizeMode.sys.mjs b/browser/components/customizableui/CustomizeMode.sys.mjs @@ -1148,7 +1148,7 @@ export class CustomizeMode { for (let command of this.#document.querySelectorAll("command")) { if (!command.id || !this.#enabledCommands.has(command.id)) { if (shouldBeDisabled) { - if (command.getAttribute("disabled") != "true") { + if (!command.hasAttribute("disabled")) { command.setAttribute("disabled", true); } else { command.setAttribute("wasdisabled", true); @@ -1352,7 +1352,7 @@ export class CustomizeMode { aNode.removeAttribute("observes"); } - if (aNode.getAttribute("checked") == "true") { + if (aNode.hasAttribute("checked")) { wrapper.setAttribute("itemchecked", "true"); aNode.removeAttribute("checked"); } @@ -1482,7 +1482,7 @@ export class CustomizeMode { // XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing let command = this.$(commandID); - if (command && command.hasAttribute("disabled")) { + if (command?.hasAttribute("disabled")) { toolbarItem.setAttribute("disabled", command.getAttribute("disabled")); } } diff --git a/browser/components/customizableui/ToolbarContextMenu.sys.mjs b/browser/components/customizableui/ToolbarContextMenu.sys.mjs @@ -54,11 +54,10 @@ export var ToolbarContextMenu = { popup.triggerNode.id ); checkbox.hidden = !isDownloads; - if (DownloadsButton.autoHideDownloadsButton) { - checkbox.setAttribute("checked", "true"); - } else { - checkbox.removeAttribute("checked"); - } + checkbox.toggleAttribute( + "checked", + DownloadsButton.autoHideDownloadsButton + ); }, /** @@ -70,7 +69,7 @@ export var ToolbarContextMenu = { * @param {CommandEvent} event */ onDownloadsAutoHideChange(event) { - let autoHide = event.target.getAttribute("checked") == "true"; + let autoHide = event.target.hasAttribute("checked"); Services.prefs.setBoolPref("browser.download.autohideButton", autoHide); }, @@ -99,9 +98,7 @@ export var ToolbarContextMenu = { popup.triggerNode.id ); separator.hidden = checkbox.hidden = !isDownloads; - lazy.gAlwaysOpenPanel - ? checkbox.setAttribute("checked", "true") - : checkbox.removeAttribute("checked"); + checkbox.toggleAttribute("checked", lazy.gAlwaysOpenPanel); }, /** @@ -113,7 +110,7 @@ export var ToolbarContextMenu = { * @param {CommandEvent} event */ onDownloadsAlwaysOpenPanelChange(event) { - let alwaysOpen = event.target.getAttribute("checked") == "true"; + let alwaysOpen = event.target.hasAttribute("checked"); Services.prefs.setBoolPref("browser.download.alwaysOpenPanel", alwaysOpen); }, @@ -216,7 +213,7 @@ export var ToolbarContextMenu = { toolbar.getAttribute("type") == "menubar" ? "autohide" : "collapsed"; - menuItem.setAttribute( + menuItem.toggleAttribute( "checked", !toolbar.hasAttribute(hidingAttribute) ); @@ -441,11 +438,10 @@ export var ToolbarContextMenu = { ); if (isCustomizingExtsButton) { checkbox.hidden = false; - if (gUnifiedExtensions.buttonAlwaysVisible) { - checkbox.setAttribute("checked", "true"); - } else { - checkbox.removeAttribute("checked"); - } + checkbox.toggleAttribute( + "checked", + gUnifiedExtensions.buttonAlwaysVisible + ); } else if (isExtsButton && !gUnifiedExtensions.buttonAlwaysVisible) { // The button may be visible despite the user's preference, which could // remind the user of the button's existence. Offer an option to unhide @@ -516,7 +512,7 @@ export var ToolbarContextMenu = { if (widgetId) { let area = lazy.CustomizableUI.getPlacementOfWidget(widgetId).area; let inToolbar = area != lazy.CustomizableUI.AREA_ADDONS; - pinToToolbar.setAttribute("checked", inToolbar); + pinToToolbar.toggleAttribute("checked", inToolbar); } } diff --git a/browser/components/customizableui/test/browser_customizemode_contextmenu_menubuttonstate.js b/browser/components/customizableui/test/browser_customizemode_contextmenu_menubuttonstate.js @@ -15,9 +15,8 @@ add_task(async function () { !PanelUI.menuButton.hasAttribute("open"), "Menu button should still not be 'pressed' when in customize mode" ); - is( - PanelUI.menuButton.getAttribute("disabled"), - "true", + ok( + PanelUI.menuButton.hasAttribute("disabled"), "Menu button should be disabled in customize mode" ); @@ -35,14 +34,12 @@ add_task(async function () { !PanelUI.menuButton.hasAttribute("open"), "Menu button should still not be 'pressed' when in customize mode after opening a context menu" ); - is( - PanelUI.menuButton.getAttribute("disabled"), - "true", + ok( + PanelUI.menuButton.hasAttribute("disabled"), "Menu button should still be disabled in customize mode" ); - is( - PanelUI.menuButton.getAttribute("disabled"), - "true", + ok( + PanelUI.menuButton.hasAttribute("disabled"), "Menu button should still be disabled in customize mode after opening context menu" ); @@ -53,9 +50,8 @@ add_task(async function () { !PanelUI.menuButton.hasAttribute("open"), "Menu button should still not be 'pressed' when in customize mode after hiding a context menu" ); - is( - PanelUI.menuButton.getAttribute("disabled"), - "true", + ok( + PanelUI.menuButton.hasAttribute("disabled"), "Menu button should still be disabled in customize mode after hiding context menu" ); await endCustomizing(); diff --git a/browser/components/customizableui/test/browser_history_recently_closed.js b/browser/components/customizableui/test/browser_history_recently_closed.js @@ -111,15 +111,15 @@ add_task(async function testRecentlyClosedDisabled() { // Wait for the disabled attribute to change, as we receive // the "viewshown" event before this changes await BrowserTestUtils.waitForCondition( - () => recentlyClosedTabs.getAttribute("disabled"), + () => recentlyClosedTabs.hasAttribute("disabled"), "Waiting for button to become disabled" ); Assert.ok( - recentlyClosedTabs.getAttribute("disabled"), + recentlyClosedTabs.hasAttribute("disabled"), "Recently closed tabs button disabled" ); Assert.ok( - recentlyClosedWindows.getAttribute("disabled"), + recentlyClosedWindows.hasAttribute("disabled"), "Recently closed windows button disabled" ); @@ -134,15 +134,15 @@ add_task(async function testRecentlyClosedDisabled() { await openHistoryPanel(); await BrowserTestUtils.waitForCondition( - () => !recentlyClosedTabs.getAttribute("disabled"), + () => !recentlyClosedTabs.hasAttribute("disabled"), "Waiting for button to be enabled" ); Assert.ok( - !recentlyClosedTabs.getAttribute("disabled"), + !recentlyClosedTabs.hasAttribute("disabled"), "Recently closed tabs is available" ); Assert.ok( - recentlyClosedWindows.getAttribute("disabled"), + recentlyClosedWindows.hasAttribute("disabled"), "Recently closed windows button disabled" ); @@ -162,15 +162,15 @@ add_task(async function testRecentlyClosedDisabled() { await openHistoryPanel(); await BrowserTestUtils.waitForCondition( - () => !recentlyClosedWindows.getAttribute("disabled"), + () => !recentlyClosedWindows.hasAttribute("disabled"), "Waiting for button to be enabled" ); Assert.ok( - !recentlyClosedTabs.getAttribute("disabled"), + !recentlyClosedTabs.hasAttribute("disabled"), "Recently closed tabs is available" ); Assert.ok( - !recentlyClosedWindows.getAttribute("disabled"), + !recentlyClosedWindows.hasAttribute("disabled"), "Recently closed windows is available" ); @@ -189,7 +189,7 @@ add_task(async function testRecentlyClosedTabsDisabledPersists() { let recentlyClosedTabs = document.getElementById("appMenuRecentlyClosedTabs"); Assert.ok( - recentlyClosedTabs.getAttribute("disabled"), + recentlyClosedTabs.hasAttribute("disabled"), "Recently closed tabs button disabled" ); @@ -202,7 +202,7 @@ add_task(async function testRecentlyClosedTabsDisabledPersists() { "appMenuRecentlyClosedTabs" ); Assert.ok( - recentlyClosedTabs.getAttribute("disabled"), + recentlyClosedTabs.hasAttribute("disabled"), "Recently closed tabs is disabled" ); @@ -216,7 +216,7 @@ add_task(async function testRecentlyClosedTabsDisabledPersists() { "appMenuRecentlyClosedTabs" ); Assert.ok( - recentlyClosedTabs.getAttribute("disabled"), + recentlyClosedTabs.hasAttribute("disabled"), "Recently closed tabs is disabled" ); await hideHistoryPanel(newWin.document); diff --git a/browser/components/downloads/test/browser/browser_downloads_autohide.js b/browser/components/downloads/test/browser/browser_downloads_autohide.js @@ -447,7 +447,7 @@ add_task(async function checkContextMenu() { info("Check context menu"); await openContextMenu(button); is(checkbox.hidden, false, "Auto-hide checkbox is visible"); - is(checkbox.getAttribute("checked"), "true", "Auto-hide is enabled"); + ok(checkbox.hasAttribute("checked"), "Auto-hide is enabled"); info("Disable auto-hide via context menu"); clickCheckbox(checkbox); @@ -506,7 +506,7 @@ function promiseCustomizeEnd(aWindow = window) { function clickCheckbox(checkbox) { // Clicking a checkbox toggles its checkedness first. - if (checkbox.getAttribute("checked") == "true") { + if (checkbox.hasAttribute("checked")) { checkbox.removeAttribute("checked"); } else { checkbox.setAttribute("checked", "true"); diff --git a/browser/components/downloads/test/browser/browser_downloads_context_menu_always_open_similar_files.js b/browser/components/downloads/test/browser/browser_downloads_context_menu_always_open_similar_files.js @@ -103,8 +103,9 @@ add_task(async function test_checkbox_useSystemDefault() { !BrowserTestUtils.isHidden(alwaysOpenSimilarFilesItem), "alwaysOpenSimilarFiles should be visible" ); - ok( - alwaysOpenSimilarFilesItem.hasAttribute("checked"), + is( + alwaysOpenSimilarFilesItem.getAttribute("type"), + "checkbox", "alwaysOpenSimilarFiles should have checkbox attribute" ); diff --git a/browser/components/downloads/test/browser/browser_downloads_panel_opens.js b/browser/components/downloads/test/browser/browser_downloads_panel_opens.js @@ -80,11 +80,7 @@ async function downloadAndCheckPanel({ openDownloadsListOnStart = true } = {}) { function clickCheckbox(checkbox) { // Clicking a checkbox toggles its checkedness first. - if (checkbox.getAttribute("checked") == "true") { - checkbox.removeAttribute("checked"); - } else { - checkbox.setAttribute("checked", "true"); - } + checkbox.toggleAttribute("checked"); // Then it runs the command and closes the popup. checkbox.doCommand(); checkbox.parentElement.hidePopup(); @@ -599,7 +595,7 @@ add_task(async function test_alwaysOpenPanel_menuitem() { info("Check context menu for downloads button."); await openContextMenu(button); is(checkbox.hidden, false, "Always Open checkbox is visible."); - is(checkbox.getAttribute("checked"), "true", "Always Open is enabled."); + ok(checkbox.hasAttribute("checked"), "Always Open is enabled."); info("Disable Always Open via context menu."); clickCheckbox(checkbox); @@ -613,7 +609,7 @@ add_task(async function test_alwaysOpenPanel_menuitem() { await openContextMenu(button); is(checkbox.hidden, false, "Always Open checkbox is visible."); - isnot(checkbox.getAttribute("checked"), "true", "Always Open is disabled."); + ok(!checkbox.hasAttribute("checked"), "Always Open is disabled."); info("Enable Always Open via context menu"); clickCheckbox(checkbox); diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_support.js b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_support.js @@ -13,9 +13,8 @@ add_setup(async function () { add_task(async function test_help_menu() { buildHelpMenu(); let troubleshootingInfoMenu = document.getElementById("troubleShooting"); - is( - troubleshootingInfoMenu.getAttribute("disabled"), - "true", + ok( + troubleshootingInfoMenu.hasAttribute("disabled"), "The `More Troubleshooting Information` item should be disabled" ); }); diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_feedback_commands.js b/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_feedback_commands.js @@ -23,18 +23,16 @@ async function checkItemsAreDisabled(url) { let reportMenu = document.getElementById( "menu_HelpPopup_reportPhishingtoolmenu" ); - is( - reportMenu.getAttribute("disabled"), - "true", + ok( + reportMenu.disabled, "The `Report Deceptive Site` item should be disabled" ); let errorMenu = document.getElementById( "menu_HelpPopup_reportPhishingErrortoolmenu" ); - is( - errorMenu.getAttribute("disabled"), - "true", + ok( + errorMenu.disabled, "The `This isn’t a deceptive site` item should be disabled" ); } @@ -52,9 +50,8 @@ add_task(async function test_policy_feedback_commands() { buildHelpMenu(); let feedbackPageMenu = document.getElementById("feedbackPage"); - is( - feedbackPageMenu.getAttribute("disabled"), - "true", + ok( + feedbackPageMenu.disabled, "The `Submit Feedback...` item should be disabled" ); diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_masterpassword.js b/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_masterpassword.js @@ -35,7 +35,7 @@ async function checkDeviceManager({ buttonIsDisabled }) { let changePwButton = deviceManagerWindow.document.getElementById("change_pw_button"); is( - changePwButton.getAttribute("disabled") == "true", + changePwButton.hasAttribute("disabled"), buttonIsDisabled, "Change Password button is in the correct state: " + buttonIsDisabled ); diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js b/browser/components/enterprisepolicies/tests/browser/browser_policy_disable_safemode.js @@ -13,9 +13,8 @@ add_setup(async function () { add_task(async function test_help_menu() { buildHelpMenu(); let safeModeMenu = document.getElementById("helpSafeMode"); - is( - safeModeMenu.getAttribute("disabled"), - "true", + ok( + safeModeMenu.hasAttribute("disabled"), "The `Restart with Add-ons Disabled...` item should be disabled" ); }); @@ -28,9 +27,8 @@ add_task(async function test_safemode_from_about_support() { await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { let button = content.document.getElementById("restart-in-safe-mode-button"); - is( - button.getAttribute("disabled"), - "true", + ok( + button.hasAttribute("disabled"), "The `Restart with Add-ons Disabled...` button should be disabled" ); }); @@ -46,9 +44,8 @@ add_task(async function test_safemode_from_about_profiles() { await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { let button = content.document.getElementById("restart-in-safe-mode-button"); - is( - button.getAttribute("disabled"), - "true", + ok( + button.hasAttribute("disabled"), "The `Restart with Add-ons Disabled...` button should be disabled" ); }); diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js b/browser/components/extensions/test/browser/browser_ext_browserAction_contextMenu.js @@ -714,11 +714,7 @@ add_task(async function test_unified_extensions_toolbar_pinning() { ); let pinToToolbar = menu.querySelector(".customize-context-pinToToolbar"); Assert.ok(!pinToToolbar.hidden, "Pin to Toolbar is visible."); - Assert.equal( - pinToToolbar.getAttribute("checked"), - "true", - "Pin to Toolbar is checked." - ); + Assert.ok(pinToToolbar.hasAttribute("checked"), "Pin to Toolbar is checked."); info("Pinning addon to the addons panel."); await closeChromeContextMenu(TOOLBAR_CONTEXT_MENU, pinToToolbar); @@ -743,9 +739,8 @@ add_task(async function test_unified_extensions_toolbar_pinning() { ); Assert.ok(!pinToToolbar.hidden, "Pin to Toolbar is visible."); - Assert.equal( - pinToToolbar.getAttribute("checked"), - "false", + Assert.ok( + !pinToToolbar.hasAttribute("checked"), "Pin to Toolbar is not checked." ); await closeChromeContextMenu(UNIFIED_CONTEXT_MENU, pinToToolbar); diff --git a/browser/components/extensions/test/browser/browser_ext_originControls.js b/browser/components/extensions/test/browser/browser_ext_originControls.js @@ -208,7 +208,7 @@ async function testOriginControls( `Visible menu item ${i} has correct l10n attrs.` ); - let checked = visibleOriginItems[i].getAttribute("checked") === "true"; + let checked = visibleOriginItems[i].hasAttribute("checked"); is(i === selected, checked, `Expected checked value for item ${i}.`); } diff --git a/browser/components/extensions/test/browser/browser_ext_windows_create_url.js b/browser/components/extensions/test/browser/browser_ext_windows_create_url.js @@ -206,7 +206,7 @@ add_task(async function testWindowCreate() { ); let dialogEl = dialog._frame.contentDocument.querySelector("dialog"); Assert.ok(dialogEl, "Dialog element should exist"); - dialogEl.setAttribute("buttondisabledaccept", false); + dialogEl.removeAttribute("buttondisabledaccept"); dialogEl.acceptDialog(); }); }; diff --git a/browser/components/extensions/test/browser/browser_unified_extensions_button_visibility.js b/browser/components/extensions/test/browser/browser_unified_extensions_button_visibility.js @@ -300,7 +300,7 @@ add_task(async function test_customization_button_and_menu_item_visibility() { "toolbar-context-always-show-extensions-button" ); is(item.hidden, false, "Menu item should be visible"); - is(item.getAttribute("checked"), "true", "Should be checked by default"); + ok(item.hasAttribute("checked"), "Should be checked by default"); await closeChromeContextMenu(contextMenu.id, item, win); info("The button should still be visible while customizing"); @@ -341,7 +341,7 @@ add_task(async function test_customization_button_and_menu_item_visibility() { "toolbar-context-always-show-extensions-button" ); is(item.hidden, false, "Menu item should be visible"); - ok(!item.getAttribute("checked"), "Should be unchecked by earlier action"); + ok(!item.hasAttribute("checked"), "Should be unchecked by earlier action"); await closeChromeContextMenu(contextMenu.id, null, win); } diff --git a/browser/components/extensions/test/browser/browser_unified_extensions_overflowable_toolbar.js b/browser/components/extensions/test/browser/browser_unified_extensions_overflowable_toolbar.js @@ -994,9 +994,8 @@ add_task(async function test_unpin_overflowed_widget() { !pinToToolbar.hidden, "expected 'Pin to Toolbar' to be visible" ); - Assert.equal( - pinToToolbar.getAttribute("checked"), - "true", + Assert.ok( + pinToToolbar.hasAttribute("checked"), "expected 'Pin to Toolbar' to be checked" ); diff --git a/browser/components/extensions/test/browser/browser_unified_extensions_vertical_tabs.js b/browser/components/extensions/test/browser/browser_unified_extensions_vertical_tabs.js @@ -149,9 +149,8 @@ async function unpinFromToolbar(extension, win = window) { ".unified-extensions-context-menu-pin-to-toolbar" ); ok(pinToToolbarItem, "expected 'pin to toolbar' menu item"); - is( - pinToToolbarItem.getAttribute("checked"), - "true", + ok( + pinToToolbarItem.hasAttribute("checked"), "pin menu item is currently checked" ); const hidden = BrowserTestUtils.waitForEvent( diff --git a/browser/components/firefoxview/fxview-tab-row.css b/browser/components/firefoxview/fxview-tab-row.css @@ -38,7 +38,7 @@ grid-template-columns: min-content auto; } - &[disabled="true"] { + &[disabled] { pointer-events: none; color: var(--text-color-disabled); } diff --git a/browser/components/firefoxview/syncedtabs-tab-list.mjs b/browser/components/firefoxview/syncedtabs-tab-list.mjs @@ -167,7 +167,7 @@ export class SyncedTabsTabRow extends FxviewTabRowBase { href=${ifDefined(this.url)} class="fxview-tab-row-main" id="fxview-tab-row-main" - disabled=${this.closeRequested} + ?disabled=${this.closeRequested} tabindex=${this.active && this.currentActiveElementId === "fxview-tab-row-main" ? "0" diff --git a/browser/components/firefoxview/tests/browser/browser_syncedtabs_firefoxview.js b/browser/components/firefoxview/tests/browser/browser_syncedtabs_firefoxview.js @@ -878,7 +878,7 @@ add_task(async function view_all_synced_tabs_recent_browsing() { is(gBrowser.tabs.length, openTabsCount, "No new tabs were opened"); Assert.ok( FirefoxViewHandler.tab.selected, - "Firefox View tab is still selected selected" + "Firefox View tab is still selected" ); Assert.equal( pagesDeck.selectedViewName, diff --git a/browser/components/places/tests/browser/browser_bookmarks_toolbar_context_menu_view_options.js b/browser/components/places/tests/browser/browser_bookmarks_toolbar_context_menu_view_options.js @@ -69,8 +69,8 @@ add_task(async function testPopup() { for (let menuitem of menuitems) { let expected = menuitem.dataset.visibilityEnum == state; is( - menuitem.getAttribute("checked"), - expected.toString(), + menuitem.hasAttribute("checked"), + expected, `The corresponding menuitem, ${menuitem.dataset.visibilityEnum}, ${ expected ? "should" : "shouldn't" } be checked if state=${state}` diff --git a/browser/components/places/tests/browser/browser_toolbar_other_bookmarks.js b/browser/components/places/tests/browser/browser_toolbar_other_bookmarks.js @@ -418,8 +418,8 @@ async function testOtherBookmarksCheckedState(expectedCheckedState) { ); is( - otherBookmarksMenuItem.getAttribute("checked"), - `${expectedCheckedState}`, + otherBookmarksMenuItem.hasAttribute("checked"), + expectedCheckedState, `Other Bookmarks item's checked state should be ${expectedCheckedState}` ); diff --git a/browser/components/preferences/dialogs/clearSiteData.js b/browser/components/preferences/dialogs/clearSiteData.js @@ -63,7 +63,7 @@ var gClearSiteDataDialog = { }, onCheckboxCommand() { - this._dialog.setAttribute( + this._dialog.toggleAttribute( "buttondisabledaccept", !(this._clearSiteDataCheckbox.checked || this._clearCacheCheckbox.checked) ); diff --git a/browser/components/preferences/dialogs/containers.js b/browser/components/preferences/dialogs/containers.js @@ -89,7 +89,7 @@ let gContainersManager = { // Check if name is provided to determine if the form can be submitted checkForm() { const name = document.getElementById("name"); - this._dialog.setAttribute("buttondisabledaccept", !name.value.trim()); + this._dialog.toggleAttribute("buttondisabledaccept", !name.value.trim()); }, createIconButtons() { diff --git a/browser/components/preferences/dialogs/permissions.js b/browser/components/preferences/dialogs/permissions.js @@ -504,7 +504,7 @@ var gPermissionManager = { let hbox = document.createXULElement("hbox"); let website = document.createXULElement("label"); - website.setAttribute("disabled", disabledByPolicy); + website.toggleAttribute("disabled", disabledByPolicy); website.setAttribute("class", "website-name-value"); website.setAttribute("value", permission.origin); hbox.setAttribute("class", "website-name"); @@ -515,7 +515,7 @@ var gPermissionManager = { if (!this._hideStatusColumn) { hbox = document.createXULElement("hbox"); let capability = document.createXULElement("label"); - capability.setAttribute("disabled", disabledByPolicy); + capability.toggleAttribute("disabled", disabledByPolicy); capability.setAttribute("class", "website-capability-value"); document.l10n.setAttributes( capability, diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js @@ -3281,11 +3281,10 @@ function dataCollectionCheckboxHandler({ ); if (collectionEnabled && matchPref()) { - if (Services.prefs.getBoolPref(pref, false)) { - checkbox.setAttribute("checked", "true"); - } else { - checkbox.removeAttribute("checked"); - } + checkbox.toggleAttribute( + "checked", + Services.prefs.getBoolPref(pref, false) + ); checkbox.setAttribute("preference", pref); } else { checkbox.removeAttribute("preference"); @@ -3925,7 +3924,7 @@ var gPrivacyPane = { let notificationsDoNotDisturb = document.getElementById( "notificationsDoNotDisturb" ); - notificationsDoNotDisturb.setAttribute("checked", true); + notificationsDoNotDisturb.toggleAttribute("checked", true); } } @@ -5364,7 +5363,7 @@ var gPrivacyPane = { return; } - osReauthCheckbox.setAttribute("checked", LoginHelper.getOSAuthEnabled()); + osReauthCheckbox.toggleAttribute("checked", LoginHelper.getOSAuthEnabled()); setEventListener( "osReauthCheckbox", @@ -5586,11 +5585,10 @@ var gPrivacyPane = { Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED, false) && Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false) ) { - if (Services.prefs.getBoolPref(PREF_OPT_OUT_STUDIES_ENABLED, false)) { - checkbox.setAttribute("checked", "true"); - } else { - checkbox.removeAttribute("checked"); - } + checkbox.toggleAttribute( + "checked", + Services.prefs.getBoolPref(PREF_OPT_OUT_STUDIES_ENABLED, false) + ); checkbox.setAttribute("preference", PREF_OPT_OUT_STUDIES_ENABLED); checkbox.removeAttribute("disabled"); } else { diff --git a/browser/components/preferences/tests/browser_contentblocking.js b/browser/components/preferences/tests/browser_contentblocking.js @@ -134,11 +134,7 @@ add_task(async function testContentBlockingMainCategory() { for (let selector of checkboxes) { let element = doc.querySelector(selector); ok(element, "checkbox " + selector + " exists"); - is( - element.getAttribute("checked"), - "true", - "checkbox " + selector + " is checked" - ); + ok(element.hasAttribute("checked"), "checkbox " + selector + " is checked"); } // Ensure the dependent controls of the tracking protection subsection behave properly. @@ -1074,15 +1070,11 @@ add_task(async function testContentBlockingCustomCategory() { function checkControlState(doc, controls, enabled) { for (let selector of controls) { for (let control of doc.querySelectorAll(selector)) { - if (enabled) { - ok(!control.hasAttribute("disabled"), `${selector} is enabled.`); - } else { - is( - control.getAttribute("disabled"), - "true", - `${selector} is disabled.` - ); - } + is( + !control.hasAttribute("disabled"), + enabled, + `${selector} is ${enabled ? "enabled" : "disabled"}.` + ); } } } @@ -1128,9 +1120,8 @@ add_task(async function testDisableTPCheckBoxDisablesEmailTP() { ); // Verify the initial check state of the tracking protection checkbox. - is( - tpCheckbox.getAttribute("checked"), - "true", + ok( + tpCheckbox.hasAttribute("checked"), "Tracking protection checkbox is checked initially" ); @@ -1243,7 +1234,7 @@ add_task(async function testFPPCustomCheckBox() { // Verify the default state of the FPP checkbox. ok(fppCheckbox, "FPP checkbox exists"); - is(fppCheckbox.getAttribute("checked"), "true", "FPP checkbox is checked"); + ok(fppCheckbox.hasAttribute("checked"), "FPP checkbox is checked"); let menu = doc.querySelector("#fingerprintingProtectionMenu"); let alwaysMenuItem = doc.querySelector( diff --git a/browser/components/sidebar/sidebar-tab-list.mjs b/browser/components/sidebar/sidebar-tab-list.mjs @@ -271,7 +271,7 @@ export class SidebarTabRow extends FxviewTabRowBase { "activemedia-blocked" ), })} - disabled=${this.closeRequested} + ?disabled=${this.closeRequested} data-l10n-args=${ifDefined(this.primaryL10nArgs)} data-l10n-id=${ifDefined(this.primaryL10nId)} href=${ifDefined(this.url)} diff --git a/browser/components/tabbrowser/content/browser-fullZoom.js b/browser/components/tabbrowser/content/browser-fullZoom.js @@ -358,11 +358,7 @@ var FullZoom = { } let fullZoomCmd = document.getElementById("cmd_fullZoomToggle"); - if (!ZoomManager.useFullZoom) { - fullZoomCmd.setAttribute("checked", "true"); - } else { - fullZoomCmd.setAttribute("checked", "false"); - } + fullZoomCmd.toggleAttribute("checked", !ZoomManager.useFullZoom); }, // Setting & Pref Manipulation diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js @@ -5819,7 +5819,7 @@ // We should be using the disabled property here instead of the attribute, // but some elements that this function is used with don't support it (e.g. // menuitem). - if (node.getAttribute("disabled") == "true") { + if (node.hasAttribute("disabled")) { return; } // Do nothing @@ -9689,7 +9689,7 @@ var TabContextMenu = { let closedCount = SessionStore.getLastClosedTabCount(window); document .getElementById("History:UndoCloseTab") - .setAttribute("disabled", closedCount == 0); + .toggleAttribute("disabled", closedCount == 0); document.l10n.setArgs(document.getElementById("context_undoCloseTab"), { tabCount: closedCount, }); diff --git a/browser/components/translations/content/fullPageTranslationsPanel.js b/browser/components/translations/content/fullPageTranslationsPanel.js @@ -737,23 +737,14 @@ var FullPageTranslationsPanel = new (class { (await TranslationsParent.getTopPreferredSupportedToLang()); for (const menuitem of alwaysOfferTranslationsMenuItems) { - menuitem.setAttribute( - "checked", - alwaysOfferTranslations ? "true" : "false" - ); + menuitem.toggleAttribute("checked", alwaysOfferTranslations); } for (const menuitem of alwaysTranslateMenuItems) { - menuitem.setAttribute( - "checked", - alwaysTranslateLanguage ? "true" : "false" - ); + menuitem.toggleAttribute("checked", alwaysTranslateLanguage); menuitem.disabled = shouldDisable; } for (const menuitem of neverTranslateMenuItems) { - menuitem.setAttribute( - "checked", - neverTranslateLanguage ? "true" : "false" - ); + menuitem.toggleAttribute("checked", neverTranslateLanguage); menuitem.disabled = shouldDisable; } } @@ -772,7 +763,7 @@ var FullPageTranslationsPanel = new (class { ).shouldNeverTranslateSite(); for (const menuitem of neverTranslateSiteMenuItems) { - menuitem.setAttribute("checked", neverTranslateSite ? "true" : "false"); + menuitem.toggleAttribute("checked", neverTranslateSite); } } @@ -1284,11 +1275,11 @@ var FullPageTranslationsPanel = new (class { } = this.elements; const alwaysTranslateLanguage = - alwaysTranslateLanguageMenuItem.getAttribute("checked") === "true"; + alwaysTranslateLanguageMenuItem.hasAttribute("checked"); const neverTranslateLanguage = - neverTranslateLanguageMenuItem.getAttribute("checked") === "true"; + neverTranslateLanguageMenuItem.hasAttribute("checked"); const neverTranslateSite = - neverTranslateSiteMenuItem.getAttribute("checked") === "true"; + neverTranslateSiteMenuItem.hasAttribute("checked"); return new CheckboxPageAction( this.#isTranslationsActive(), diff --git a/browser/components/translations/tests/browser/head.js b/browser/components/translations/tests/browser/head.js @@ -1360,12 +1360,12 @@ class FullPageTranslationsTestUtils { `Should match expected disabled state for ${dataL10nId}` ); await waitForCondition( - () => menuItem.getAttribute("checked") === (checked ? "true" : "false"), + () => menuItem.hasAttribute("checked") === checked, "Waiting for checkbox state" ); is( - menuItem.getAttribute("checked"), - checked ? "true" : "false", + menuItem.hasAttribute("checked"), + checked, `Should match expected checkbox state for ${dataL10nId}` ); } diff --git a/browser/extensions/formautofill/content/formautofill.css b/browser/extensions/formautofill/content/formautofill.css @@ -22,7 +22,7 @@ } } - &[disabled="true"] { + &[disabled] { opacity: 0.5; } } diff --git a/browser/modules/ExtensionsUI.sys.mjs b/browser/modules/ExtensionsUI.sys.mjs @@ -806,7 +806,7 @@ export var ExtensionsUI = { if (state.allDomains) { let allDomains = doc.createXULElement("menuitem"); allDomains.setAttribute("type", "radio"); - allDomains.setAttribute("checked", state.hasAccess); + allDomains.toggleAttribute("checked", state.hasAccess); doc.l10n.setAttributes(allDomains, "origin-controls-option-all-domains"); items.push(allDomains); } @@ -814,7 +814,7 @@ export var ExtensionsUI = { if (state.whenClicked) { let whenClicked = doc.createXULElement("menuitem"); whenClicked.setAttribute("type", "radio"); - whenClicked.setAttribute("checked", !state.hasAccess); + whenClicked.toggleAttribute("checked", !state.hasAccess); doc.l10n.setAttributes( whenClicked, "origin-controls-option-when-clicked" @@ -829,7 +829,7 @@ export var ExtensionsUI = { if (state.alwaysOn) { let alwaysOn = doc.createXULElement("menuitem"); alwaysOn.setAttribute("type", "radio"); - alwaysOn.setAttribute("checked", state.hasAccess); + alwaysOn.toggleAttribute("checked", state.hasAccess); doc.l10n.setAttributes(alwaysOn, "origin-controls-option-always-on", { domain: uri.host, }); diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css @@ -74,7 +74,7 @@ /* Inactive elements are faded out on OSX */ .toolbarbutton-1:not(:hover):-moz-window-inactive, .bookmark-item:not(:hover):-moz-window-inactive, -:root:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled="true"] { +:root:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled] { opacity: 0.5; } diff --git a/browser/themes/shared/autocomplete.css b/browser/themes/shared/autocomplete.css @@ -281,7 +281,7 @@ /* Popup states */ .autocomplete-richlistitem { - &:not([disabled="true"]):not([selected]):hover { + &:not([disabled]):not([selected]):hover { background-color: var(--arrowpanel-dimmed); @media (forced-colors) { background-color: ButtonText; diff --git a/browser/themes/shared/customizableui/customizeMode.css b/browser/themes/shared/customizableui/customizeMode.css @@ -262,8 +262,8 @@ toolbarpaletteitem { justify-content: inherit; } - #PersonalToolbar & toolbarbutton[checked="true"], - toolbar & toolbarbutton[checked="true"] > :where(.toolbarbutton-icon, .toolbarbutton-text, .toolbarbutton-badge-stack) { + #PersonalToolbar & toolbarbutton[checked], + toolbar & toolbarbutton[checked] > :where(.toolbarbutton-icon, .toolbarbutton-text, .toolbarbutton-badge-stack) { background-color: revert !important; } diff --git a/browser/themes/shared/customizableui/panelUI-shared.css b/browser/themes/shared/customizableui/panelUI-shared.css @@ -258,7 +258,7 @@ panelview { border-radius: var(--button-border-radius); flex-shrink: 0; - &[disabled="true"] { + &[disabled] { visibility: hidden; } @@ -1231,7 +1231,7 @@ panelview .toolbarbutton-1, margin-inline-start: 10px; } - &[checked="true"] { + &[checked] { list-style-image: url(chrome://global/skin/icons/check.svg); -moz-context-properties: fill; fill: currentColor; @@ -1257,7 +1257,7 @@ panelview .toolbarbutton-1, .subviewbutton[image] > .toolbarbutton-text, .subviewbutton[targetURI] > .toolbarbutton-text, .subviewbutton.bookmark-item > .toolbarbutton-text, -.subviewbutton[checked="true"] > .toolbarbutton-text { +.subviewbutton[checked] > .toolbarbutton-text { padding-inline-start: 8px; } @@ -1540,7 +1540,7 @@ panelview .toolbarbutton-1 { min-width: 0; display: flex; - &[disabled="true"] { + &[disabled] { pointer-events: none; } } @@ -2013,7 +2013,7 @@ radiogroup:focus-visible > .subviewradio[focused="true"] { #PanelUI-profiler-presets { margin: 8px 0; - &[disabled="true"]::part(label-box) { + &[disabled]::part(label-box) { opacity: 0.5; } } diff --git a/browser/themes/shared/downloads/contentAreaDownloadsView.css b/browser/themes/shared/downloads/contentAreaDownloadsView.css @@ -13,8 +13,7 @@ background-color: transparent; } -.downloadButton:not([disabled="true"]):hover, -.downloadButton:not([disabled="true"]):hover:active, +.downloadButton:not([disabled]):hover, .downloadButton:not([disabled]):hover:active { background: transparent; border: none; diff --git a/browser/themes/shared/preferences/preferences.css b/browser/themes/shared/preferences/preferences.css @@ -555,7 +555,7 @@ a[is="moz-support-link"]:not(.sidebar-footer-link, [hidden]) { margin-bottom: 0; } -.text-link[disabled="true"] { +.text-link[disabled] { pointer-events: none; } diff --git a/browser/themes/shared/translations/panel.css b/browser/themes/shared/translations/panel.css @@ -38,7 +38,7 @@ /* The default styling is to dim the default, but here override it so that it still uses the primary color. */ -.translations-panel-button-group > button[default][disabled="true"] { +.translations-panel-button-group > button[default][disabled] { color: var(--button-text-color-primary); background-color: var(--color-accent-primary); } diff --git a/devtools/client/debugger/src/components/shared/menu.css b/devtools/client/debugger/src/components/shared/menu.css @@ -28,11 +28,11 @@ menuitem:hover { color: white; } -menuitem[disabled="true"] { +menuitem[disabled] { color: #cccccc; } -menuitem[disabled="true"]:hover { +menuitem[disabled]:hover { background-color: transparent; cursor: default; } diff --git a/devtools/client/framework/test/browser_menu_api.js b/devtools/client/framework/test/browser_menu_api.js @@ -106,14 +106,14 @@ async function testMenuPopup(toolbox) { is(menuItems[1].getAttribute("label"), MENU_ITEMS[1].label, "Correct label"); is(menuItems[1].getAttribute("type"), "checkbox", "Correct type attr"); - is(menuItems[1].getAttribute("checked"), "true", "Has checked attr"); + ok(menuItems[1].hasAttribute("checked"), "Has checked attr"); is(menuItems[2].getAttribute("label"), MENU_ITEMS[2].label, "Correct label"); is(menuItems[2].getAttribute("type"), "radio", "Correct type attr"); ok(!menuItems[2].hasAttribute("checked"), "Doesn't have checked attr"); is(menuItems[3].getAttribute("label"), MENU_ITEMS[3].label, "Correct label"); - is(menuItems[3].getAttribute("disabled"), "true", "disabled attr menuitem"); + ok(menuItems[3].hasAttribute("disabled"), "disabled attr menuitem"); is( menuItems[4].getAttribute("data-l10n-id"), diff --git a/devtools/client/storage/ui.js b/devtools/client/storage/ui.js @@ -1604,7 +1604,7 @@ class StorageUI { onVariableViewPopupShowing() { const item = this.view.getFocusedItem(); - this._variableViewPopupCopy.setAttribute("disabled", !item); + this._variableViewPopupCopy.toggleAttribute("disabled", !item); } /** diff --git a/dom/xul/XULButtonElement.cpp b/dom/xul/XULButtonElement.cpp @@ -20,6 +20,7 @@ #include "mozilla/dom/MouseEventBinding.h" #include "mozilla/dom/NameSpaceConstants.h" #include "mozilla/dom/XULMenuBarElement.h" +#include "mozilla/glue/Debug.h" #include "nsCaseTreatment.h" #include "nsChangeHint.h" #include "nsGkAtoms.h" @@ -38,7 +39,9 @@ XULButtonElement::XULButtonElement( already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) : nsXULElement(std::move(aNodeInfo)), mIsAlwaysMenu(IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menulist, - nsGkAtoms::menuitem)) {} + nsGkAtoms::menuitem)), + mCheckable(IsAnyOfXULElements(nsGkAtoms::menuitem, nsGkAtoms::radio, + nsGkAtoms::checkbox)) {} XULButtonElement::~XULButtonElement() { StopBlinking(); @@ -52,11 +55,6 @@ nsChangeHint XULButtonElement::GetAttributeChangeHint( // type=menu switches to a menu frame. return nsChangeHint_ReconstructFrame; } - if (aAttribute == nsGkAtoms::checked && - IsAnyOfXULElements(nsGkAtoms::menuitem, nsGkAtoms::radio, - nsGkAtoms::checkbox)) { - return nsChangeHint_RepaintFrame; - } return nsXULElement::GetAttributeChangeHint(aAttribute, aModType); } @@ -247,7 +245,8 @@ void XULButtonElement::ExecuteMenu(Modifiers aModifiers, int16_t aButton, // Flip "checked" state if we're a checkbox menu, or an un-checked radio menu. bool needToFlipChecked = false; if (*menuType == MenuType::Checkbox || - (*menuType == MenuType::Radio && !GetXULBoolAttr(nsGkAtoms::checked))) { + (*menuType == MenuType::Radio && + !State().HasState(ElementState::CHECKED))) { needToFlipChecked = !AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocheck, nsGkAtoms::_false, eCaseMatters); } @@ -701,7 +700,7 @@ void XULButtonElement::UncheckRadioSiblings() { } // we're in the same group, only uncheck if we're checked (for some reason, // some tests rely on that specifically). - return button->GetXULBoolAttr(nsGkAtoms::checked); + return button->GetBoolAttr(nsGkAtoms::checked); }; for (nsIContent* child = parent->GetFirstChild(); child; @@ -720,16 +719,24 @@ void XULButtonElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, bool aNotify) { nsXULElement::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); - if (IsAlwaysMenu() && aNamespaceID == kNameSpaceID_None) { + if (aNamespaceID != kNameSpaceID_None) { + return; + } + if (aName == nsGkAtoms::checked && mCheckable) { + SetStates(ElementState::CHECKED, !!aValue, aNotify); + } + if (aName == nsGkAtoms::disabled) { + SetStates(ElementState::DISABLED, !!aValue, aNotify); + } + if (IsAlwaysMenu()) { // We need to uncheck radio siblings when we're a checked radio and switch // groups, or become checked. const bool shouldUncheckSiblings = [&] { if (aName == nsGkAtoms::type || aName == nsGkAtoms::name) { return *GetMenuType() == MenuType::Radio && - GetXULBoolAttr(nsGkAtoms::checked); + State().HasState(ElementState::CHECKED); } - if (aName == nsGkAtoms::checked && aValue && - aValue->Equals(nsGkAtoms::_true, eCaseMatters)) { + if (aName == nsGkAtoms::checked && aValue) { return *GetMenuType() == MenuType::Radio; } return false; diff --git a/dom/xul/XULButtonElement.h b/dom/xul/XULButtonElement.h @@ -91,8 +91,6 @@ class XULButtonElement : public nsXULElement { XULPopupElement* GetMenuPopupContent() const; int32_t MenuOpenCloseDelay() const; - bool IsDisabled() const { return GetXULBoolAttr(nsGkAtoms::disabled); } - private: XULMenuBarElement* GetMenuBar() const; void Blurred(); @@ -112,7 +110,9 @@ class XULButtonElement : public nsXULElement { bool mIsHandlingKeyEvent = false; // Whether this is a XULMenuElement. - const bool mIsAlwaysMenu; + const bool mIsAlwaysMenu : 1; + // Whether this supports the `checked` attribute. + const bool mCheckable : 1; RefPtr<nsXULMenuCommandEvent> mDelayedMenuCommandEvent; nsCOMPtr<nsITimer> mMenuOpenTimer; nsCOMPtr<nsITimer> mMenuBlinkTimer; diff --git a/dom/xul/XULTextElement.cpp b/dom/xul/XULTextElement.cpp @@ -39,6 +39,18 @@ nsChangeHint XULTextElement::GetAttributeChangeHint( return nsXULElement::GetAttributeChangeHint(aAttribute, aModType); } +void XULTextElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + nsIPrincipal* aSubjectPrincipal, + bool aNotify) { + nsXULElement::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue, + aSubjectPrincipal, aNotify); + if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) { + SetStates(ElementState::DISABLED, !!aValue, aNotify); + } +} + JSObject* XULTextElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return XULTextElement_Binding::Wrap(aCx, this, aGivenProto); diff --git a/dom/xul/XULTextElement.h b/dom/xul/XULTextElement.h @@ -16,9 +16,9 @@ class XULTextElement final : public nsXULElement { explicit XULTextElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) : nsXULElement(std::move(aNodeInfo)) {} - bool Disabled() { return GetXULBoolAttr(nsGkAtoms::disabled); } + bool Disabled() { return IsDisabled(); } MOZ_CAN_RUN_SCRIPT void SetDisabled(bool aValue) { - SetXULBoolAttr(nsGkAtoms::disabled, aValue, mozilla::IgnoreErrors()); + SetBoolAttr(nsGkAtoms::disabled, aValue); } void GetValue(DOMString& aValue) const { GetXULAttr(nsGkAtoms::value, aValue); @@ -36,6 +36,10 @@ class XULTextElement final : public nsXULElement { nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute, AttrModType aModType) const override; + void AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, + const nsAttrValue* aValue, const nsAttrValue* aOldValue, + nsIPrincipal* aSubjectPrincipal, bool aNotify) override; + NS_IMPL_FROMNODE_HELPER(XULTextElement, IsAnyOfXULElements(nsGkAtoms::label, nsGkAtoms::description)); diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp @@ -899,7 +899,9 @@ void nsXULElement::Click(CallerType aCallerType) { void nsXULElement::ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent) { - if (BoolAttrIsTrue(nsGkAtoms::disabled)) return; + if (State().HasState(ElementState::DISABLED)) { + return; + } nsCOMPtr<Document> doc = GetComposedDoc(); // Strong just in case if (doc) { @@ -1005,13 +1007,6 @@ nsresult nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) { return NS_OK; } -bool nsXULElement::BoolAttrIsTrue(nsAtom* aName) const { - const nsAttrValue* attr = GetAttrInfo(kNameSpaceID_None, aName).mValue; - - return attr && attr->Type() == nsAttrValue::eAtom && - attr->GetAtomValue() == nsGkAtoms::_true; -} - bool nsXULElement::IsEventAttributeNameInternal(nsAtom* aName) { return nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL); } diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h @@ -357,11 +357,6 @@ class nsXULElement : public nsStyledElement { NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULElement, nsStyledElement) - // This doesn't work on XUL elements! You probably want - // GetXULBoolAttr(nsGkAtoms::disabled) or so. - // TODO(emilio): Maybe we should unify HTML and XUL here. - bool IsDisabled() const = delete; - // nsINode void GetEventTargetParent(mozilla::EventChainPreVisitor& aVisitor) override; MOZ_CAN_RUN_SCRIPT_BOUNDARY @@ -526,7 +521,6 @@ class nsXULElement : public nsStyledElement { bool SupportsAccessKey() const; void RegUnRegAccessKey(bool aDoReg) override; - bool BoolAttrIsTrue(nsAtom* aName) const; friend nsXULElement* NS_NewBasicXULElement( already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); diff --git a/layout/tools/layout-debug/ui/content/layoutdebug.js b/layout/tools/layout-debug/ui/content/layoutdebug.js @@ -603,7 +603,7 @@ function OnLDBUnload() { function toggle(menuitem) { // trim the initial "menu_" var feature = menuitem.id.substring(5); - gDebugger[feature] = menuitem.getAttribute("checked") == "true"; + gDebugger[feature] = menuitem.hasAttribute("checked"); } function openFile() { diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp @@ -2308,6 +2308,9 @@ void nsXULPopupManager::UpdateMenuItems(Element* aPopup) { if (commandElement->GetAttr(nsGkAtoms::checked, commandValue)) { grandChildElement->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandValue, true); + } else { + grandChildElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, + true); } if (commandElement->GetAttr(nsGkAtoms::hidden, commandValue)) { @@ -3059,11 +3062,8 @@ nsXULMenuCommandEvent::Run() { RefPtr menu = XULButtonElement::FromNode(mMenu); MOZ_ASSERT(menu); if (mFlipChecked) { - if (menu->GetXULBoolAttr(nsGkAtoms::checked)) { - menu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, true); - } else { - menu->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, u"true"_ns, true); - } + menu->SetBoolAttr(nsGkAtoms::checked, + !menu->GetBoolAttr(nsGkAtoms::checked)); } RefPtr<nsPresContext> presContext = menu->OwnerDoc()->GetPresContext(); diff --git a/security/manager/pki/resources/content/certManager.js b/security/manager/pki/resources/content/certManager.js @@ -616,7 +616,7 @@ function enableButtonsForCertTree(certTree, idList) { let disableButtons = nothingOrContainerSelected(certTree); for (let id of idList) { - document.getElementById(id).setAttribute("disabled", disableButtons); + document.getElementById(id).toggleAttribute("disabled", disableButtons); } } diff --git a/security/manager/pki/resources/content/device_manager.js b/security/manager/pki/resources/content/device_manager.js @@ -146,13 +146,13 @@ function enableButtons() { return; } - var login_toggle = "true"; - var logout_toggle = "true"; - var pw_toggle = "true"; - var unload_toggle = "true"; + var login_toggle = true; + var logout_toggle = true; + var pw_toggle = true; + var unload_toggle = true; getSelectedItem(); if (selected_module) { - unload_toggle = "false"; + unload_toggle = false; showModuleInfo(); } else if (selected_slot) { // here's the workaround - login functions are all with token, @@ -160,12 +160,12 @@ function enableButtons() { var selected_token = selected_slot.getToken(); if (selected_token != null) { if (selected_token.needsLogin() || !selected_token.needsUserInit) { - pw_toggle = "false"; + pw_toggle = false; if (selected_token.needsLogin()) { if (selected_token.isLoggedIn()) { - logout_toggle = "false"; + logout_toggle = false; } else { - login_toggle = "false"; + login_toggle = false; } } } @@ -182,16 +182,16 @@ function enableButtons() { } document .getElementById("login_button") - .setAttribute("disabled", login_toggle); + .toggleAttribute("disabled", login_toggle); document .getElementById("logout_button") - .setAttribute("disabled", logout_toggle); + .toggleAttribute("disabled", logout_toggle); document .getElementById("change_pw_button") - .setAttribute("disabled", pw_toggle); + .toggleAttribute("disabled", pw_toggle); document .getElementById("unload_button") - .setAttribute("disabled", unload_toggle); + .toggleAttribute("disabled", unload_toggle); } // clear the display of information for the slot diff --git a/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js b/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js @@ -289,8 +289,8 @@ async function testModuleNameHelper(moduleName, acceptButtonShouldBeDisabled) { let dialogNode = win.document.querySelector("dialog"); Assert.equal( - dialogNode.getAttribute("buttondisabledaccept"), - acceptButtonShouldBeDisabled ? "true" : null, + dialogNode.hasAttribute("buttondisabledaccept"), + acceptButtonShouldBeDisabled, `dialog accept button should ${ acceptButtonShouldBeDisabled ? "" : "not " }be disabled` diff --git a/toolkit/components/pictureinpicture/tests/browser_contextMenu.js b/toolkit/components/pictureinpicture/tests/browser_contextMenu.js @@ -83,9 +83,8 @@ async function runTaskOpenClosePiPWithContextMenu(isCtrlClick = false) { !menuItem.hidden, "Should show Picture-in-Picture menu item." ); - Assert.equal( - menuItem.getAttribute("checked"), - "false", + Assert.ok( + !menuItem.hasAttribute("checked"), "Picture-in-Picture should be unchecked." ); @@ -155,9 +154,8 @@ add_task(async () => { !menuItem.hidden, "Should show Picture-in-Picture menu item." ); - Assert.equal( - menuItem.getAttribute("checked"), - "false", + Assert.ok( + !menuItem.hasAttribute("checked"), "Picture-in-Picture should be unchecked." ); await closeContextMenu(menu); @@ -177,9 +175,8 @@ add_task(async () => { !menuItem.hidden, "Should show Picture-in-Picture menu item." ); - Assert.equal( - menuItem.getAttribute("checked"), - "true", + Assert.ok( + menuItem.hasAttribute("checked"), "Picture-in-Picture should be checked." ); await closeContextMenu(menu); @@ -202,9 +199,8 @@ add_task(async () => { !menuItem.hidden, "Should show Picture-in-Picture menu item." ); - Assert.equal( - menuItem.getAttribute("checked"), - "false", + Assert.ok( + !menuItem.hasAttribute("checked"), "Picture-in-Picture should be unchecked." ); await closeContextMenu(menu); @@ -230,9 +226,8 @@ add_task(async () => { !menuItem.hidden, "Should be showing Picture-in-Picture menu item." ); - Assert.equal( - menuItem.getAttribute("checked"), - "false", + Assert.ok( + !menuItem.hasAttribute("checked"), "Picture-in-Picture should be unchecked." ); await closeContextMenu(menu); @@ -252,9 +247,8 @@ add_task(async () => { !menuItem.hidden, "Should show Picture-in-Picture menu item." ); - Assert.equal( - menuItem.getAttribute("checked"), - "true", + Assert.ok( + menuItem.hasAttribute("checked"), "Picture-in-Picture should be checked." ); await closeContextMenu(menu); diff --git a/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js b/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js @@ -75,7 +75,7 @@ var exercisePrefs = async function (source, highlightable) { await checkHighlight(browser, highlightable); is( getAttribute(wrapMenuItem, "checked"), - "false", + null, "Wrap menu item not checked by default" ); is( @@ -105,11 +105,7 @@ var exercisePrefs = async function (source, highlightable) { await simulateClick(wrapMenuItem); await openContextMenu(browser); await checkStyle(browser, "white-space", "pre"); - is( - getAttribute(wrapMenuItem, "checked"), - "false", - "Wrap menu item unchecked" - ); + is(getAttribute(wrapMenuItem, "checked"), null, "Wrap menu item unchecked"); await prefReady; is( SpecialPowers.getBoolPref("view_source.wrap_long_lines"), @@ -126,7 +122,7 @@ var exercisePrefs = async function (source, highlightable) { await checkHighlight(browser, false); is( getAttribute(syntaxMenuItem, "checked"), - "false", + null, "Syntax menu item unchecked" ); await prefReady; @@ -171,7 +167,7 @@ var exercisePrefs = async function (source, highlightable) { is(getAttribute(wrapMenuItem, "checked"), "true", "Wrap menu item checked"); is( getAttribute(syntaxMenuItem, "checked"), - "false", + null, "Syntax menu item unchecked" ); diff --git a/toolkit/content/customElements.js b/toolkit/content/customElements.js @@ -694,15 +694,11 @@ MozElements.BaseControlMixin = Base => { class BaseControl extends Base { get disabled() { - return this.getAttribute("disabled") == "true"; + return this.hasAttribute("disabled"); } set disabled(val) { - if (val) { - this.setAttribute("disabled", "true"); - } else { - this.removeAttribute("disabled"); - } + this.toggleAttribute("disabled", !!val); } get tabIndex() { diff --git a/toolkit/content/tests/browser/browser_bug1170531.js b/toolkit/content/tests/browser/browser_bug1170531.js @@ -65,12 +65,13 @@ add_task(async function () { window.requestAnimationFrame(() => executeSoon(resolve)) ); await new Promise(openMenu); - menu_cut_disabled = - menuPopup.querySelector("#menu_cut").getAttribute("disabled") == "true"; + menu_cut_disabled = menuPopup + .querySelector("#menu_cut") + .hasAttribute("disabled"); is(menu_cut_disabled, false, "menu_cut should be enabled"); - menu_copy_disabled = - menuPopup.querySelector("#menu_copy").getAttribute("disabled") == - "true"; + menu_copy_disabled = menuPopup + .querySelector("#menu_copy") + .hasAttribute("disabled"); is(menu_copy_disabled, false, "menu_copy should be enabled"); await new Promise(closeMenu); @@ -87,12 +88,13 @@ add_task(async function () { window.requestAnimationFrame(() => executeSoon(resolve)) ); await new Promise(openMenu); - menu_cut_disabled = - menuPopup.querySelector("#menu_cut").getAttribute("disabled") == "true"; + menu_cut_disabled = menuPopup + .querySelector("#menu_cut") + .hasAttribute("disabled"); is(menu_cut_disabled, true, "menu_cut should be disabled"); - menu_copy_disabled = - menuPopup.querySelector("#menu_copy").getAttribute("disabled") == - "true"; + menu_copy_disabled = menuPopup + .querySelector("#menu_copy") + .hasAttribute("disabled"); is(menu_copy_disabled, true, "menu_copy should be disabled"); await new Promise(closeMenu); @@ -109,12 +111,13 @@ add_task(async function () { window.requestAnimationFrame(() => executeSoon(resolve)) ); await new Promise(openMenu); - menu_cut_disabled = - menuPopup.querySelector("#menu_cut").getAttribute("disabled") == "true"; + menu_cut_disabled = menuPopup + .querySelector("#menu_cut") + .hasAttribute("disabled"); is(menu_cut_disabled, false, "menu_cut should be enabled"); - menu_copy_disabled = - menuPopup.querySelector("#menu_copy").getAttribute("disabled") == - "true"; + menu_copy_disabled = menuPopup + .querySelector("#menu_copy") + .hasAttribute("disabled"); is(menu_copy_disabled, false, "menu_copy should be enabled"); await new Promise(closeMenu); @@ -126,12 +129,13 @@ add_task(async function () { window.requestAnimationFrame(() => executeSoon(resolve)) ); await new Promise(openMenu); - menu_cut_disabled = - menuPopup.querySelector("#menu_cut").getAttribute("disabled") == "true"; + menu_cut_disabled = menuPopup + .querySelector("#menu_cut") + .hasAttribute("disabled"); is(menu_cut_disabled, true, "menu_cut should be disabled"); - menu_copy_disabled = - menuPopup.querySelector("#menu_copy").getAttribute("disabled") == - "true"; + menu_copy_disabled = menuPopup + .querySelector("#menu_copy") + .hasAttribute("disabled"); is(menu_copy_disabled, true, "menu_copy should be disabled"); await new Promise(closeMenu); } diff --git a/toolkit/content/tests/chrome/bug366992_window.xhtml b/toolkit/content/tests/chrome/bug366992_window.xhtml @@ -61,7 +61,7 @@ function onLoad() { document.getElementById("input").focus(); var deleteDisabled = document.getElementById("cmd_delete") - .getAttribute("disabled") == "true"; + .hasAttribute("disabled"); ok(deleteDisabled, "cmd_delete should be disabled when the empty input is focused"); finish(); diff --git a/toolkit/content/tests/chrome/test_custom_element_base.xhtml b/toolkit/content/tests/chrome/test_custom_element_base.xhtml @@ -282,7 +282,7 @@ async function testCustomInterface() { class SimpleElement extends MozXULElement { get disabled() { - return this.getAttribute("disabled") == "true"; + return this.hasAttribute("disabled"); } set disabled(val) { @@ -345,7 +345,7 @@ is(document.activeElement.id, "four", "Tab 3"); twoElement.disabled = true; - is(twoElement.getAttribute("disabled"), "true", "two disabled after change"); + ok(twoElement.hasAttribute("disabled"), "two disabled after change"); synthesizeKey("VK_TAB", { shiftKey: true }); is(document.activeElement.id, "one", "Tab 1"); diff --git a/toolkit/content/tests/chrome/test_edit_contextmenu.html b/toolkit/content/tests/chrome/test_edit_contextmenu.html @@ -76,7 +76,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1513343 let popuphidden = new Promise(r => win.addEventListener("popuphidden", r, { once: true })); if (isPassword) { let isRevealed = element.revealPassword; - is(revealPassword.getAttribute("checked"), isRevealed ? "true" : null, "reveal password checked"); + is(revealPassword.hasAttribute("checked"), isRevealed, "reveal password checked"); contextmenu.activateItem(revealPassword); await popuphidden; is(element.revealPassword, !isRevealed, "Password was revealed / unrevealed"); diff --git a/toolkit/content/tests/chrome/test_menuchecks.xhtml b/toolkit/content/tests/chrome/test_menuchecks.xhtml @@ -99,7 +99,7 @@ var children = document.getElementById("popup").childNodes; for (var c = 0; c < children.length; c++) { var child = children[c]; - if ((checkedItems.includes(child.id) && child.getAttribute("checked") != "true") || + if ((checkedItems.includes(child.id) && !child.hasAttribute("checked")) || (!checkedItems.includes(child.id) && child.hasAttribute("checked"))) { isok = false; break; diff --git a/toolkit/content/widgets/button.js b/toolkit/content/widgets/button.js @@ -122,15 +122,11 @@ } set disabled(val) { - if (val) { - this.setAttribute("disabled", "true"); - } else { - this.removeAttribute("disabled"); - } + this.toggleAttribute("disabled", !!val); } get disabled() { - return this.getAttribute("disabled") == "true"; + return this.hasAttribute("disabled"); } set group(val) { @@ -163,12 +159,7 @@ sibs[i].removeAttribute("checked"); } } - - if (val) { - this.setAttribute("checked", "true"); - } else { - this.removeAttribute("checked"); - } + this.toggleAttribute("checked", !!val); } get checked() { diff --git a/toolkit/content/widgets/checkbox.js b/toolkit/content/widgets/checkbox.js @@ -58,13 +58,9 @@ } set checked(val) { - let change = val != (this.getAttribute("checked") == "true"); - if (val) { - this.setAttribute("checked", "true"); - } else { - this.removeAttribute("checked"); - } - + val = !!val; + let change = val != this.hasAttribute("checked"); + this.toggleAttribute("checked", val); if (change) { let event = document.createEvent("Events"); event.initEvent("CheckboxStateChange", true, true); @@ -73,7 +69,7 @@ } get checked() { - return this.getAttribute("checked") == "true"; + return this.hasAttribute("checked"); } } diff --git a/toolkit/content/widgets/menu.js b/toolkit/content/widgets/menu.js @@ -24,7 +24,7 @@ // nsIDOMXULSelectControlItemElement get selected() { - return this.getAttribute("selected") == "true"; + return this.hasAttribute("selected"); } // nsIDOMXULSelectControlItemElement diff --git a/toolkit/content/widgets/menupopup.js b/toolkit/content/widgets/menupopup.js @@ -17,10 +17,10 @@ // need selected to deal with the menulists (like `<select>`). const ITEM_NEEDS_GUTTER_SELECTOR = (() => { if (AppConstants.platform == "macosx") { - return "[checked=true], [selected=true]"; + return "[checked], [selected]"; } if (AppConstants.platform == "win") { - return "[checked=true]"; + return "[checked]"; } return "[type=checkbox], [type=radio]"; })(); diff --git a/toolkit/content/widgets/moz-input-box.js b/toolkit/content/widgets/moz-input-box.js @@ -135,7 +135,7 @@ var showUndo = spellui.canSpellCheck && spellui.canUndo(); var enabledCheckbox = this.getMenuItem("spell-check-enabled"); - enabledCheckbox.setAttribute("checked", enabled); + enabledCheckbox.toggleAttribute("checked", enabled); var overMisspelling = spellui.overMisspelling; this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling); diff --git a/toolkit/content/widgets/moz-label/moz-label.mjs b/toolkit/content/widgets/moz-label/moz-label.mjs @@ -141,7 +141,7 @@ class MozTextLabel extends HTMLLabelElement { if ( (controlElement.localName == "checkbox" || controlElement.localName == "radio") && - controlElement.getAttribute("disabled") == "true" + controlElement.hasAttribute("disabled") ) { return; } diff --git a/toolkit/content/widgets/radio.js b/toolkit/content/widgets/radio.js @@ -154,14 +154,14 @@ init() { this._radioChildren = null; - if (this.getAttribute("disabled") == "true") { + if (this.hasAttribute("disabled")) { this.disabled = true; } var children = this._getRadioChildren(); var length = children.length; for (var i = 0; i < length; i++) { - if (children[i].getAttribute("selected") == "true") { + if (children[i].hasAttribute("selected")) { this.selectedIndex = i; return; } @@ -230,7 +230,7 @@ } get disabled() { - if (this.getAttribute("disabled") == "true") { + if (this.hasAttribute("disabled")) { return true; } var children = this._getRadioChildren(); @@ -265,12 +265,12 @@ } set selectedItem(val) { - var focused = this.getAttribute("focused") == "true"; + var focused = this.hasAttribute("focused"); var alreadySelected = false; if (val) { - alreadySelected = val.getAttribute("selected") == "true"; - val.setAttribute("focused", focused); + alreadySelected = val.hasAttribute("selected"); + val.toggleAttribute("focused", focused); val.setAttribute("selected", "true"); this.setAttribute("value", val.value); } else { @@ -282,7 +282,7 @@ var previousItem = null; for (var i = 0; i < children.length; ++i) { if (children[i] != val) { - if (children[i].getAttribute("selected") == "true") { + if (children[i].hasAttribute("selected")) { previousItem = children[i]; } @@ -350,7 +350,7 @@ get focusedItem() { var children = this._getRadioChildren(); for (var i = 0; i < children.length; ++i) { - if (children[i].getAttribute("focused") == "true") { + if (children[i].hasAttribute("focused")) { return children[i]; } } diff --git a/toolkit/content/widgets/richlistbox.js b/toolkit/content/widgets/richlistbox.js @@ -993,7 +993,7 @@ } get selected() { - return this.getAttribute("selected") == "true"; + return this.hasAttribute("selected"); } /** * nsIDOMXULSelectControlItemElement diff --git a/toolkit/content/widgets/text.js b/toolkit/content/widgets/text.js @@ -68,7 +68,7 @@ if ( (controlElement.localName == "checkbox" || controlElement.localName == "radio") && - controlElement.getAttribute("disabled") == "true" + controlElement.hasAttribute("disabled") ) { return; } diff --git a/toolkit/modules/PopupNotifications.sys.mjs b/toolkit/modules/PopupNotifications.sys.mjs @@ -1211,15 +1211,12 @@ PopupNotifications.prototype = { _setNotificationUIState(notification, state = {}) { let mainAction = notification.notification.mainAction; - if ( + notification.toggleAttribute( + "mainactiondisabled", (mainAction && mainAction.disabled) || - state.disableMainAction || - notification.hasAttribute("invalidselection") - ) { - notification.setAttribute("mainactiondisabled", "true"); - } else { - notification.removeAttribute("mainactiondisabled"); - } + state.disableMainAction || + notification.hasAttribute("invalidselection") + ); if (state.warningLabel) { notification.setAttribute("warninglabel", state.warningLabel); notification.removeAttribute("warninghidden"); diff --git a/toolkit/mozapps/handling/content/permissionDialog.js b/toolkit/mozapps/handling/content/permissionDialog.js @@ -69,7 +69,7 @@ let dialog = { this._dialog.setAttribute("buttondisabledaccept", true); }, enableDialog: () => { - this._dialog.setAttribute("buttondisabledaccept", false); + this._dialog.removeAttribute("buttondisabledaccept"); }, focusTarget: window, }); diff --git a/toolkit/mozapps/preferences/changemp.js b/toolkit/mozapps/preferences/changemp.js @@ -213,15 +213,10 @@ function checkPasswords() { return; } } - - if ( + let enabled = pw1 == pw2 && - (pw1 != "" || Services.policies.isAllowed("removeMasterPassword")) - ) { - ok.setAttribute("disabled", "false"); - } else { - ok.setAttribute("disabled", "true"); - } + (pw1 != "" || Services.policies.isAllowed("removeMasterPassword")); + ok.toggleAttribute("disabled", !enabled); } window.addEventListener("load", init); diff --git a/toolkit/themes/linux/global/richlistbox.css b/toolkit/themes/linux/global/richlistbox.css @@ -13,7 +13,7 @@ richlistbox { border: 1px solid ThreeDShadow; } -richlistbox[disabled="true"] { +richlistbox[disabled] { color: GrayText; } diff --git a/toolkit/themes/osx/global/button.css b/toolkit/themes/osx/global/button.css @@ -19,7 +19,7 @@ button:where(:hover:active) { /* When the window isn't focused, the default button background isn't drawn, * so don't change the text color then: */ -button:where([default="true"]:not(:-moz-window-inactive)) { +button:where([default]:not(:-moz-window-inactive)) { color: -moz-mac-defaultbuttontext; } @@ -35,7 +35,7 @@ button[type="default"] { /* .......... disabled state .......... */ -button:where([disabled="true"]) { +button:where([disabled]) { color: GrayText; } diff --git a/toolkit/themes/osx/global/richlistbox.css b/toolkit/themes/osx/global/richlistbox.css @@ -13,7 +13,7 @@ richlistbox { border: 1px solid ThreeDShadow; } -richlistbox[disabled="true"] { +richlistbox[disabled] { color: GrayText; } diff --git a/toolkit/themes/shared/checkbox.css b/toolkit/themes/shared/checkbox.css @@ -9,11 +9,11 @@ checkbox { align-items: center; margin: 4px 2px; - &[disabled="true"][native] { + &[disabled][native] { color: GrayText; } - &[disabled="true"]:not([native]) > .checkbox-label-box { + &[disabled]:not([native]) > .checkbox-label-box { opacity: 0.4; } } diff --git a/toolkit/themes/shared/findbar.css b/toolkit/themes/shared/findbar.css @@ -130,7 +130,7 @@ xul|findbar { background-color: var(--toolbarbutton-active-background, rgba(190, 190, 190, 0.4)); } - &[disabled="true"] > .toolbarbutton-icon { + &[disabled] > .toolbarbutton-icon { opacity: var(--toolbarbutton-disabled-opacity); } } diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/global-shared.css @@ -295,7 +295,7 @@ xul|description { margin-bottom: 4px; } -xul|label[disabled="true"] { +xul|label[disabled] { color: GrayText; } diff --git a/toolkit/themes/shared/in-content/common-shared.css b/toolkit/themes/shared/in-content/common-shared.css @@ -254,8 +254,8 @@ html|select:not([size], [multiple]) > html|option { html|button:enabled:hover, html|select:not([size], [multiple]):enabled:hover, html|input[type="color"]:hover, -xul|button:not([disabled="true"]):hover, -xul|menulist:not([disabled="true"]):hover { +xul|button:not([disabled]):hover, +xul|menulist:not([disabled]):hover { background-color: var(--button-background-color-hover); color: var(--button-text-color-hover); border-color: var(--button-border-color-hover); @@ -264,10 +264,10 @@ xul|menulist:not([disabled="true"]):hover { html|button:enabled:hover:active, html|select:not([size], [multiple]):enabled:hover:active, html|input[type="color"]:enabled:hover:active, -xul|button:not([disabled="true"]):hover:active, +xul|button:not([disabled]):hover:active, xul|button[open], xul|button[open]:hover, -xul|menulist[open="true"]:not([disabled="true"]) { +xul|menulist[open="true"]:not([disabled]) { background-color: var(--button-background-color-active); color: var(--button-text-color-active); border-color: var(--button-border-color-active); @@ -276,8 +276,8 @@ xul|menulist[open="true"]:not([disabled="true"]) { html|button:disabled, html|select:disabled, html|input[type="color"]:disabled, -xul|button[disabled="true"], -xul|menulist[disabled="true"] { +xul|button[disabled], +xul|menulist[disabled] { opacity: 0.4; } @@ -293,8 +293,8 @@ button.primary { html|button[autofocus]:enabled:hover, html|button[type="submit"]:enabled:hover, html|button.primary:enabled:hover, -xul|button[default]:not([disabled="true"]):hover, -xul|button.primary:not([disabled="true"]):hover { +xul|button[default]:not([disabled]):hover, +xul|button.primary:not([disabled]):hover { background-color: var(--button-background-color-primary-hover); color: var(--button-text-color-primary-hover); border-color: var(--button-border-color-primary-hover); @@ -303,8 +303,8 @@ xul|button.primary:not([disabled="true"]):hover { html|button[autofocus]:enabled:hover:active, html|button[type="submit"]:enabled:hover:active, html|button.primary:enabled:hover:active, -xul|button[default]:not([disabled="true"]):hover:active, -xul|button.primary:not([disabled="true"]):hover:active { +xul|button[default]:not([disabled]):hover:active, +xul|button.primary:not([disabled]):hover:active { background-color: var(--button-background-color-primary-active); color: var(--button-text-color-primary-active); border-color: var(--button-border-color-primary-active); @@ -388,20 +388,20 @@ xul|menulist > xul|menupopup xul|menuitem { padding-inline: 10px 30px; } -xul|menulist > xul|menupopup > xul|menu:not([disabled="true"])[_moz-menuactive="true"], -xul|menulist > xul|menupopup > xul|menuitem:not([disabled="true"])[_moz-menuactive="true"] { +xul|menulist > xul|menupopup > xul|menu:not([disabled])[_moz-menuactive="true"], +xul|menulist > xul|menupopup > xul|menuitem:not([disabled])[_moz-menuactive="true"] { color: var(--text-color-list-item-hover); background-color: var(--background-color-list-item-hover); } -xul|menulist > xul|menupopup > xul|menu:not([disabled="true"])[selected="true"], -xul|menulist > xul|menupopup > xul|menuitem:not([disabled="true"])[selected="true"] { +xul|menulist > xul|menupopup > xul|menu:not([disabled])[selected], +xul|menulist > xul|menupopup > xul|menuitem:not([disabled])[selected] { color: var(--text-color-accent-primary-selected); background-color: var(--color-accent-primary-selected); } -xul|menulist > xul|menupopup > xul|menu[disabled="true"], -xul|menulist > xul|menupopup > xul|menuitem[disabled="true"] { +xul|menulist > xul|menupopup > xul|menu[disabled], +xul|menulist > xul|menupopup > xul|menuitem[disabled] { color: #999; /* override the [_moz-menuactive="true"] background color from global/menu.css */ @@ -478,14 +478,14 @@ html|a:visited, html|a:hover, .text-link:hover, -button.text-link:is(:not([disabled="true"]), :enabled):hover, +button.text-link:is(:not([disabled]), :enabled):hover, ::part(support-link):hover { color: var(--link-color-hover); } html|a:hover:active, .text-link:hover:active, -button.text-link:is(:not([disabled="true"]), :enabled):hover:active, +button.text-link:is(:not([disabled]), :enabled):hover:active, ::part(support-link):hover:active { color: var(--link-color-active); text-decoration: none; @@ -545,15 +545,15 @@ xul|*.radio-label-box { /* Disabled checkboxes, radios and labels */ -xul|checkbox[disabled="true"], -xul|radio[disabled="true"], -xul|label[disabled="true"] { +xul|checkbox[disabled], +xul|radio[disabled], +xul|label[disabled] { color: inherit; } -xul|checkbox[disabled="true"] > .checkbox-label-box, -xul|radio[disabled="true"] > .radio-label-box, -xul|label[disabled="true"] { +xul|checkbox[disabled] > .checkbox-label-box, +xul|radio[disabled] > .radio-label-box, +xul|label[disabled] { opacity: 0.5; } diff --git a/toolkit/themes/shared/menu.css b/toolkit/themes/shared/menu.css @@ -101,7 +101,7 @@ menulist > menupopup { } @media (-moz-platform: windows) { - > menuitem[_moz-menuactive][disabled="true"] { + > menuitem[_moz-menuactive][disabled] { color: GrayText; } } @@ -189,10 +189,10 @@ menubar > menu { color: ThreeDShadow; } - &:not([disabled="true"]) { + &:not([disabled]) { color: inherit; } - &[_moz-menuactive]:not([disabled="true"]) { + &[_moz-menuactive]:not([disabled]) { color: -moz-menubarhovertext; *|*:root[lwtheme] & { @@ -226,17 +226,17 @@ menucaption { menu, menuitem { - &:where([disabled="true"]) { + &:where([disabled]) { color: var(--text-color-disabled); text-shadow: none; } - &:where([_moz-menuactive]:not([disabled="true"])) { + &:where([_moz-menuactive]:not([disabled])) { color: -moz-menuhovertext; background-color: -moz-menuhover; } - &:where([_moz-menuactive][disabled="true"]) { + &:where([_moz-menuactive][disabled]) { background-color: -moz-menuhoverdisabled; } } diff --git a/toolkit/themes/shared/menulist.css b/toolkit/themes/shared/menulist.css @@ -47,7 +47,7 @@ } } -:host([native][disabled="true"]) { +:host([native][disabled]) { color: GrayText; } diff --git a/toolkit/themes/shared/radio.css b/toolkit/themes/shared/radio.css @@ -19,7 +19,7 @@ radio { padding-inline: 4px 2px; } - &[disabled="true"] { + &[disabled] { color: GrayText; } } diff --git a/toolkit/themes/shared/splitter.css b/toolkit/themes/shared/splitter.css @@ -65,6 +65,6 @@ splitter[orient="vertical"][state="collapsed"][substate="after"] { cursor: n-resize; } -splitter[disabled="true"] { +splitter[disabled] { cursor: default !important; } diff --git a/toolkit/themes/shared/toolbarbutton.css b/toolkit/themes/shared/toolbarbutton.css @@ -12,7 +12,7 @@ toolbarbutton { &:where(:hover) { color: -moz-buttonhovertext; } - &:where(:hover:active, [checked="true"], [open="true"]) { + &:where(:hover:active, [checked], [open]) { color: -moz-buttonactivetext; } &:where(:focus-visible) { @@ -20,7 +20,7 @@ toolbarbutton { } } - &:where([disabled="true"]) { + &:where([disabled]) { color: GrayText; text-shadow: none; } diff --git a/toolkit/themes/windows/global/button.css b/toolkit/themes/windows/global/button.css @@ -36,7 +36,7 @@ button { outline: auto; } - &:where([disabled="true"]) { + &:where([disabled]) { color: GrayText; border-color: -moz-buttondisabledborder; background-color: -moz-buttondisabledface; diff --git a/toolkit/themes/windows/global/global.css b/toolkit/themes/windows/global/global.css @@ -114,12 +114,12 @@ xul|separator.groove[orient="vertical"] { font-weight: var(--font-weight-bold); } - > menuitem[_moz-menuactive="true"][disabled="true"] { + > menuitem[_moz-menuactive="true"][disabled] { color: GrayText; background-color: unset; } - > menucaption[disabled="true"] { + > menucaption[disabled] { color: GrayText; } } diff --git a/toolkit/themes/windows/global/in-content/common.css b/toolkit/themes/windows/global/in-content/common.css @@ -13,7 +13,7 @@ xul|radio { } /* Override menulist.css */ -xul|menulist[disabled="true"] { +xul|menulist[disabled] { background-color: var(--button-background-color); } diff --git a/toolkit/themes/windows/global/richlistbox.css b/toolkit/themes/windows/global/richlistbox.css @@ -9,10 +9,10 @@ richlistbox { background-color: Field; color: FieldText; border: 1px solid ThreeDShadow; -} -richlistbox[disabled="true"] { - color: GrayText; + &[disabled] { + color: GrayText; + } } richlistitem[selected="true"] { diff --git a/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog_permission.js b/uriloader/exthandler/tests/mochitest/browser_protocol_ask_dialog_permission.js @@ -467,7 +467,7 @@ async function closeDialog(browser, dialog, confirm, scheme) { listItem.click(); } - dialogEl.setAttribute("buttondisabledaccept", false); + dialogEl.removeAttribute("buttondisabledaccept"); dialogEl.acceptDialog(); } else { dialogEl.cancelDialog(); diff --git a/uriloader/exthandler/tests/mochitest/head.js b/uriloader/exthandler/tests/mochitest/head.js @@ -228,7 +228,7 @@ async function acceptNextProtocolPermissionDialog(browser) { let dialogEl = getDialogElementFromSubDialog(dialog); // Bypass the security delay. - dialogEl.setAttribute("buttondisabledaccept", "false"); + dialogEl.removeAttribute("buttondisabledaccept"); dialogEl.acceptDialog(); await dialogWindowClosePromise; diff --git a/widget/Theme.cpp b/widget/Theme.cpp @@ -1548,18 +1548,8 @@ nsITheme::Transparency Theme::GetWidgetTransparency( bool Theme::WidgetAttributeChangeRequiresRepaint(StyleAppearance aAppearance, nsAtom* aAttribute) { - // Check the attribute to see if it's relevant. - // TODO(emilio): The non-native theme doesn't use these attributes. Other - // themes do, but not all of them (and not all of the ones they check are - // here). - return aAttribute == nsGkAtoms::disabled || - aAttribute == nsGkAtoms::checked || - aAttribute == nsGkAtoms::selected || - aAttribute == nsGkAtoms::visuallyselected || - aAttribute == nsGkAtoms::menuactive || - aAttribute == nsGkAtoms::sortDirection || - aAttribute == nsGkAtoms::focused || - aAttribute == nsGkAtoms::_default || aAttribute == nsGkAtoms::open; + return aAttribute == nsGkAtoms::_default || // Used in IsDefaultButton() + aAttribute == nsGkAtoms::open; // Used in GetContentState() } bool Theme::WidgetAppearanceDependsOnWindowFocus(StyleAppearance aAppearance) { diff --git a/widget/Theme.h b/widget/Theme.h @@ -77,7 +77,7 @@ class Theme : protected nsNativeTheme, public nsITheme { StyleAppearance) override; Transparency GetWidgetTransparency(nsIFrame*, StyleAppearance) override; bool WidgetAttributeChangeRequiresRepaint(StyleAppearance, - nsAtom* aAttribute) override; + nsAtom* aAttribute) final; bool WidgetAppearanceDependsOnWindowFocus(StyleAppearance) override; /*bool NeedToClearBackgroundBehindWidget( nsIFrame*, StyleAppearance) override;*/ diff --git a/widget/cocoa/nsMenuItemX.mm b/widget/cocoa/nsMenuItemX.mm @@ -69,14 +69,10 @@ nsMenuItemX::nsMenuItemX(nsMenuX* aParent, const nsString& aLabel, // decide enabled state based on command content if it exists, otherwise do it // based on our own content - bool isEnabled; - if (mCommandElement) { - isEnabled = !mCommandElement->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters); - } else { - isEnabled = !mContent->AsElement()->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters); - } + const bool isEnabled = + mCommandElement + ? !mCommandElement->GetBoolAttr(nsGkAtoms::disabled) + : !mContent->AsElement()->GetBoolAttr(nsGkAtoms::disabled); // set up the native menu item if (mType == eSeparatorMenuItemType) { @@ -88,8 +84,7 @@ nsMenuItemX::nsMenuItemX(nsMenuX* aParent, const nsString& aLabel, action:nil keyEquivalent:@""]; - mIsChecked = mContent->AsElement()->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::checked, nsGkAtoms::_true, eCaseMatters); + mIsChecked = mContent->AsElement()->GetBoolAttr(nsGkAtoms::checked); mNativeMenuItem.enabled = isEnabled; mNativeMenuItem.state = @@ -168,13 +163,7 @@ nsresult nsMenuItemX::SetChecked(bool aIsChecked) { // update the content model. This will also handle unchecking our siblings // if we are a radiomenu - if (mIsChecked) { - mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, - u"true"_ns, true); - } else { - mContent->AsElement()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, - true); - } + mContent->AsElement()->SetBoolAttr(nsGkAtoms::checked, mIsChecked); // update native menu item mNativeMenuItem.state = @@ -243,7 +232,7 @@ nsresult nsMenuItemX::DispatchDOMEvent(const nsString& eventName, void nsMenuItemX::UncheckRadioSiblings(nsIContent* aCheckedContent) { nsAutoString myGroupName; aCheckedContent->AsElement()->GetAttr(nsGkAtoms::name, myGroupName); - if (!myGroupName.Length()) { // no groupname, nothing to do + if (myGroupName.IsEmpty()) { // no groupname, nothing to do return; } @@ -259,8 +248,8 @@ void nsMenuItemX::UncheckRadioSiblings(nsIContent* aCheckedContent) { // if the current sibling is in the same group, clear it if (sibling->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, myGroupName, eCaseMatters)) { - sibling->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, - u"false"_ns, true); + sibling->AsElement()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, + true); } } } @@ -361,10 +350,9 @@ void nsMenuItemX::ObserveAttributeChanged(dom::Document* aDocument, if (aAttribute == nsGkAtoms::checked) { // if we're a radio menu, uncheck our sibling radio items. No need to // do any of this if we're just a normal check menu. + // XXX isn't this done by XULButtonElement as well? if (mType == eRadioMenuItemType && - mContent->AsElement()->AttrValueIs(kNameSpaceID_None, - nsGkAtoms::checked, - nsGkAtoms::_true, eCaseMatters)) { + mContent->AsElement()->GetBoolAttr(nsGkAtoms::checked)) { UncheckRadioSiblings(mContent); } mMenuParent->SetRebuild(true); @@ -392,33 +380,24 @@ void nsMenuItemX::ObserveAttributeChanged(dom::Document* aDocument, } else if (aAttribute == nsGkAtoms::key) { SetKeyEquiv(); } else if (aAttribute == nsGkAtoms::disabled) { - mNativeMenuItem.enabled = !aContent->AsElement()->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, - eCaseMatters); + mNativeMenuItem.enabled = + !aContent->AsElement()->GetBoolAttr(nsGkAtoms::disabled); } } else if (aContent == mCommandElement) { // the only thing that really matters when the menu isn't showing is the // enabled state since it enables/disables keyboard commands if (aAttribute == nsGkAtoms::disabled) { // first we sync our menu item DOM node with the command DOM node - nsAutoString commandDisabled; - nsAutoString menuDisabled; - aContent->AsElement()->GetAttr(nsGkAtoms::disabled, commandDisabled); - mContent->AsElement()->GetAttr(nsGkAtoms::disabled, menuDisabled); - if (!commandDisabled.Equals(menuDisabled)) { - // The menu's disabled state needs to be updated to match the command. - if (commandDisabled.IsEmpty()) { - mContent->AsElement()->UnsetAttr(kNameSpaceID_None, - nsGkAtoms::disabled, true); - } else { - mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, - commandDisabled, true); - } + const bool commandDisabled = + mCommandElement->GetBoolAttr(nsGkAtoms::disabled); + const bool menuDisabled = + mContent->AsElement()->GetBoolAttr(nsGkAtoms::disabled); + if (commandDisabled != menuDisabled) { + mContent->AsElement()->SetBoolAttr(nsGkAtoms::disabled, + commandDisabled); } // now we sync our native menu item with the command DOM node - mNativeMenuItem.enabled = !aContent->AsElement()->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, - eCaseMatters); + mNativeMenuItem.enabled = !commandDisabled; } } else if (aContent == mImageElement && aAttribute == nsGkAtoms::srcset) { SetupIcon(); diff --git a/widget/cocoa/nsNativeThemeCocoa.h b/widget/cocoa/nsNativeThemeCocoa.h @@ -158,8 +158,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { LayoutDeviceIntSize GetMinimumWidgetSize(nsPresContext*, nsIFrame*, StyleAppearance) override; - bool WidgetAttributeChangeRequiresRepaint(StyleAppearance, - nsAtom* aAttribute) override; bool ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame*, StyleAppearance) override; bool ThemeDrawsFocusForWidget(nsIFrame*, StyleAppearance) override; diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm @@ -1719,21 +1719,6 @@ LayoutDeviceIntSize nsNativeThemeCocoa::GetMinimumWidgetSize( NS_OBJC_END_TRY_BLOCK_RETURN(LayoutDeviceIntSize()); } -bool nsNativeThemeCocoa::WidgetAttributeChangeRequiresRepaint( - StyleAppearance aAppearance, nsAtom* aAttribute) { - // Some widget types just never change state. - switch (aAppearance) { - case StyleAppearance::MozWindowTitlebar: - case StyleAppearance::MozSidebar: - case StyleAppearance::Tooltip: - case StyleAppearance::Menupopup: - return false; - default: - break; - } - return Theme::WidgetAttributeChangeRequiresRepaint(aAppearance, aAttribute); -} - bool nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, StyleAppearance aAppearance) { diff --git a/widget/gtk/NativeMenuGtk.cpp b/widget/gtk/NativeMenuGtk.cpp @@ -39,10 +39,8 @@ using GtkMenuPopupAtRect = void (*)(GtkMenu* menu, GdkWindow* rect_window, const GdkEvent* trigger_event); static bool IsDisabled(const dom::Element& aElement) { - return aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, - nsGkAtoms::_true, eCaseMatters) || - aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, - nsGkAtoms::_true, eCaseMatters); + return aElement.GetBoolAttr(nsGkAtoms::disabled) || + aElement.GetBoolAttr(nsGkAtoms::hidden); } static bool NodeIsRelevant(const nsINode& aNode) { return aNode.IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menuseparator, @@ -64,8 +62,7 @@ static Maybe<bool> GetChecked(const dom::Element& aMenuItem) { return Nothing(); } - return Some(aMenuItem.AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, - nsGkAtoms::_true, eCaseMatters)); + return Some(aMenuItem.GetBoolAttr(nsGkAtoms::checked)); } struct Actions { @@ -601,8 +598,7 @@ static void UpdateRadioOrCheck(DbusmenuMenuitem* aItem, DBUSMENU_MENUITEM_TOGGLE_RADIO); } - bool isChecked = aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, - nsGkAtoms::_true, eCaseMatters); + const bool isChecked = aContent->GetBoolAttr(nsGkAtoms::checked); dbusmenu_menuitem_property_set_int( aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, isChecked ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED @@ -610,9 +606,7 @@ static void UpdateRadioOrCheck(DbusmenuMenuitem* aItem, } static void UpdateEnabled(DbusmenuMenuitem* aItem, const nsIContent* aContent) { - bool disabled = aContent->AsElement()->AttrValueIs( - kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters); - + const bool disabled = aContent->AsElement()->GetBoolAttr(nsGkAtoms::disabled); dbusmenu_menuitem_property_set_bool(aItem, DBUSMENU_MENUITEM_PROP_ENABLED, !disabled); } diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp @@ -469,15 +469,6 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize( return {}; } -bool nsNativeThemeGTK::WidgetAttributeChangeRequiresRepaint( - StyleAppearance aAppearance, nsAtom* aAttribute) { - // Some widget types just never change state. - if (aAppearance == StyleAppearance::MozWindowDecorations) { - return false; - } - return Theme::WidgetAttributeChangeRequiresRepaint(aAppearance, aAttribute); -} - bool nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, StyleAppearance aAppearance) { diff --git a/widget/gtk/nsNativeThemeGTK.h b/widget/gtk/nsNativeThemeGTK.h @@ -52,9 +52,6 @@ class nsNativeThemeGTK final : public mozilla::widget::Theme { mozilla::LayoutDeviceIntSize GetMinimumWidgetSize(nsPresContext*, nsIFrame*, StyleAppearance) override; - bool WidgetAttributeChangeRequiresRepaint(StyleAppearance, - nsAtom* aAttribute) override; - bool ThemeSupportsWidget(nsPresContext*, nsIFrame*, StyleAppearance) override; bool ThemeDrawsFocusForWidget(nsIFrame*, StyleAppearance) override; Transparency GetWidgetTransparency(nsIFrame*, StyleAppearance) override; diff --git a/widget/nsNativeTheme.cpp b/widget/nsNativeTheme.cpp @@ -73,49 +73,13 @@ NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed) return flags; } - if (CheckBooleanAttr(aFrame, nsGkAtoms::disabled)) { - flags |= ElementState::DISABLED; - } - switch (aAppearance) { - case StyleAppearance::Radio: { - if (CheckBooleanAttr(aFrame, nsGkAtoms::focused)) { - flags |= ElementState::FOCUS; - nsPIDOMWindowOuter* window = - aFrame->GetContent()->OwnerDoc()->GetWindow(); - if (window && window->ShouldShowFocusRing()) { - flags |= ElementState::FOCUSRING; - } - } - if (CheckBooleanAttr(aFrame, nsGkAtoms::selected) || - CheckBooleanAttr(aFrame, nsGkAtoms::checked)) { - flags |= ElementState::CHECKED; - } - break; - } - case StyleAppearance::Checkbox: { - if (CheckBooleanAttr(aFrame, nsGkAtoms::checked)) { - flags |= ElementState::CHECKED; - } else if (CheckBooleanAttr(aFrame, nsGkAtoms::indeterminate)) { - flags |= ElementState::INDETERMINATE; - } - break; - } case StyleAppearance::Toolbarbutton: if (CheckBooleanAttr(aFrame, nsGkAtoms::open)) { + // TODO: Consider using :open instead of faking :hover:active? flags |= ElementState::HOVER | ElementState::ACTIVE; } break; - case StyleAppearance::Menulist: - case StyleAppearance::NumberInput: - case StyleAppearance::Textfield: - case StyleAppearance::PasswordInput: - case StyleAppearance::Textarea: { - if (CheckBooleanAttr(aFrame, nsGkAtoms::focused)) { - flags |= ElementState::FOCUS | ElementState::FOCUSRING; - } - break; - } default: break; }