From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- toolkit/content/aboutLogging.js | 2 +- toolkit/content/aboutNetError.mjs | 3 +- toolkit/content/aboutTelemetry.js | 6 + toolkit/content/customElements.js | 62 +++--- toolkit/content/jar.mn | 2 - toolkit/content/license.html | 5 +- toolkit/content/preferencesBindings.js | 5 + .../content/tests/browser/browser_bug1693577.js | 4 +- toolkit/content/tests/chrome/chrome.toml | 1 - .../tests/chrome/file_editor_with_autocomplete.js | 4 + ...est_autocomplete_with_composition_on_input.html | 2 - .../test_editor_for_input_with_autocomplete.html | 2 - .../content/tests/chrome/window_largemenu.xhtml | 15 +- toolkit/content/tests/widgets/mochitest.toml | 2 + toolkit/content/tests/widgets/test_moz_button.html | 132 +++++++++++-- .../tests/widgets/test_moz_button_group.html | 144 +++++++++----- .../tests/widgets/test_moz_message_bar.html | 28 +-- .../content/tests/widgets/test_moz_page_nav.html | 61 ++++++ .../widgets/test_videocontrols_src_change.html | 56 ++++++ .../widgets/test_videocontrols_video_noaudio.html | 2 +- toolkit/content/widgets.css | 2 +- toolkit/content/widgets/arrowscrollbox.js | 4 +- .../content/widgets/autocomplete-richlistitem.js | 4 +- toolkit/content/widgets/datetimebox.js | 28 +-- toolkit/content/widgets/message-bar.css | 219 --------------------- toolkit/content/widgets/message-bar.js | 91 --------- .../widgets/moz-button-group/moz-button-group.css | 3 +- .../widgets/moz-button-group/moz-button-group.mjs | 25 ++- toolkit/content/widgets/moz-button/moz-button.css | 26 ++- toolkit/content/widgets/moz-button/moz-button.mjs | 21 +- .../widgets/moz-button/moz-button.stories.mjs | 29 +-- .../widgets/moz-message-bar/moz-message-bar.mjs | 12 +- .../content/widgets/moz-page-nav/README.stories.md | 46 ++++- .../widgets/moz-page-nav/moz-page-nav-button.css | 23 ++- .../content/widgets/moz-page-nav/moz-page-nav.mjs | 86 +++++++- .../widgets/moz-page-nav/moz-page-nav.stories.mjs | 32 ++- .../widgets/moz-support-link/moz-support-link.mjs | 6 +- toolkit/content/widgets/notificationbox.js | 3 +- toolkit/content/widgets/panel-list/panel-list.js | 4 - toolkit/content/widgets/popupnotification.js | 2 - toolkit/content/widgets/videocontrols.js | 17 +- toolkit/content/xul.css | 14 -- 42 files changed, 683 insertions(+), 552 deletions(-) create mode 100644 toolkit/content/tests/widgets/test_videocontrols_src_change.html delete mode 100644 toolkit/content/widgets/message-bar.css delete mode 100644 toolkit/content/widgets/message-bar.js (limited to 'toolkit/content') diff --git a/toolkit/content/aboutLogging.js b/toolkit/content/aboutLogging.js index 360ab366e1..f2f4cabc62 100644 --- a/toolkit/content/aboutLogging.js +++ b/toolkit/content/aboutLogging.js @@ -114,7 +114,7 @@ const gLoggingPresets = { }, "media-playback": { modules: - "HTMLMediaElement:4,HTMLMediaElementEvents:4,cubeb:5,PlatformDecoderModule:5,AudioSink:5,AudioSinkWrapper:5,MediaDecoderStateMachine:4,MediaDecoder:4,MediaFormatReader:5,GMP:5", + "HTMLMediaElement:4,HTMLMediaElementEvents:4,cubeb:5,PlatformDecoderModule:5,AudioSink:5,AudioSinkWrapper:5,MediaDecoderStateMachine:4,MediaDecoder:4,MediaFormatReader:5,GMP:5,EME:5", l10nIds: { label: "about-logging-preset-media-playback-label", description: "about-logging-preset-media-playback-description", diff --git a/toolkit/content/aboutNetError.mjs b/toolkit/content/aboutNetError.mjs index 1c733d5dbb..935111dcca 100644 --- a/toolkit/content/aboutNetError.mjs +++ b/toolkit/content/aboutNetError.mjs @@ -396,7 +396,8 @@ function initPage() { }); longDesc = null; - document.getElementById("openInNewWindowContainer").hidden = false; + document.getElementById("openInNewWindowContainer").hidden = + RPMGetBoolPref("security.xfocsp.hideOpenInNewWindow"); const openInNewWindowButton = document.getElementById( "openInNewWindowButton" diff --git a/toolkit/content/aboutTelemetry.js b/toolkit/content/aboutTelemetry.js index 45a8488820..9cf3c3320d 100644 --- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -352,6 +352,12 @@ var PingPicker = { }, _updateCurrentPingData() { + TelemetryController.ensureInitialized().then(() => + this._doUpdateCurrentPingData() + ); + }, + + _doUpdateCurrentPingData() { const subsession = document.getElementById("show-subsession-data").checked; let ping = TelemetryController.getCurrentPingData(subsession); if (!ping) { diff --git a/toolkit/content/customElements.js b/toolkit/content/customElements.js index ef58963a02..132b069326 100644 --- a/toolkit/content/customElements.js +++ b/toolkit/content/customElements.js @@ -791,7 +791,6 @@ ["button-group", "chrome://global/content/elements/named-deck.js"], ["findbar", "chrome://global/content/elements/findbar.js"], ["menulist", "chrome://global/content/elements/menulist.js"], - ["message-bar", "chrome://global/content/elements/message-bar.js"], ["named-deck", "chrome://global/content/elements/named-deck.js"], ["named-deck-button", "chrome://global/content/elements/named-deck.js"], ["panel-list", "chrome://global/content/elements/panel-list.js"], @@ -806,49 +805,32 @@ "chrome://global/content/elements/autocomplete-input.js", ], ["editor", "chrome://global/content/elements/editor.js"], + ["moz-button", "chrome://global/content/elements/moz-button.mjs"], + [ + "moz-button-group", + "chrome://global/content/elements/moz-button-group.mjs", + ], + ["moz-card", "chrome://global/content/elements/moz-card.mjs"], + ["moz-five-star", "chrome://global/content/elements/moz-five-star.mjs"], + [ + "moz-message-bar", + "chrome://global/content/elements/moz-message-bar.mjs", + ], + ["moz-page-nav", "chrome://global/content/elements/moz-page-nav.mjs"], + [ + "moz-support-link", + "chrome://global/content/elements/moz-support-link.mjs", + ], + ["moz-toggle", "chrome://global/content/elements/moz-toggle.mjs"], ]) { customElements.setElementCreationCallback(tag, () => { - Services.scriptloader.loadSubScript(script, window); + if (script.endsWith(".mjs")) { + ChromeUtils.importESModule(script, { global: "current" }); + } else { + Services.scriptloader.loadSubScript(script, window); + } }); } - // Bug 1813077: This is a workaround until Bug 1803810 lands - // which will give us the ability to load ESMs synchronously - // like the previous Services.scriptloader.loadSubscript() function - function importCustomElementFromESModule(name) { - switch (name) { - case "moz-button": - return import("chrome://global/content/elements/moz-button.mjs"); - case "moz-button-group": - return import( - "chrome://global/content/elements/moz-button-group.mjs" - ); - case "moz-message-bar": - return import("chrome://global/content/elements/moz-message-bar.mjs"); - case "moz-support-link": - return import( - "chrome://global/content/elements/moz-support-link.mjs" - ); - case "moz-toggle": - return import("chrome://global/content/elements/moz-toggle.mjs"); - case "moz-card": - return import("chrome://global/content/elements/moz-card.mjs"); - } - throw new Error(`Unknown custom element name (${name})`); - } - - /* - This function explicitly returns null so that there is no confusion - about which custom elements from ES Modules have been loaded. - */ - window.ensureCustomElements = function (...elementNames) { - return Promise.all( - elementNames - .filter(name => !customElements.get(name)) - .map(name => importCustomElementFromESModule(name)) - ) - .then(() => null) - .catch(console.error); - }; // Immediately load the following elements for (let script of [ diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn index d08037a84a..e555936a73 100644 --- a/toolkit/content/jar.mn +++ b/toolkit/content/jar.mn @@ -87,8 +87,6 @@ toolkit.jar: content/global/elements/findbar.js (widgets/findbar.js) content/global/elements/editor.js (widgets/editor.js) content/global/elements/general.js (widgets/general.js) - content/global/elements/message-bar.css (widgets/message-bar.css) - content/global/elements/message-bar.js (widgets/message-bar.js) content/global/elements/menu.js (widgets/menu.js) content/global/elements/menupopup.js (widgets/menupopup.js) content/global/elements/moz-button.css (widgets/moz-button/moz-button.css) diff --git a/toolkit/content/license.html b/toolkit/content/license.html index fe489f74d1..3f05da0118 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -1495,6 +1495,9 @@ licences. third_party/cups/include
#ifdef MOZ_JXL third_party/highway/
+#endif +#ifdef MOZ_PERFETTO + third_party/perfetto/
#endif

@@ -3600,7 +3603,6 @@ SOFTWARE.
  • devtools/shared/natural-sort.js
  • devtools/shared/node-properties/node-properties.js
  • third_party/rust/ordered-float
  • -
  • third_party/rust/owning_ref
  • third_party/rust/phf, third_party/rust/phf_codegen, third_party/rust/phf_generator, and @@ -3625,6 +3627,7 @@ SOFTWARE.
  • third_party/rust/synstructure
  • third_party/rust/void
  • js/src/zydis (unless otherwise specified)
  • +
  • js/src/vm/Float16.h(the code contained in the half namespace)
  • See the individual LICENSE files or headers for copyright owners.

    diff --git a/toolkit/content/preferencesBindings.js b/toolkit/content/preferencesBindings.js index b2e4070cf5..3dba0bed1e 100644 --- a/toolkit/content/preferencesBindings.js +++ b/toolkit/content/preferencesBindings.js @@ -381,6 +381,11 @@ const Preferences = (window.Preferences = (function () { if (this.locked) { aElement.disabled = true; } + if (aElement.labels?.length) { + for (let label of aElement.labels) { + label.toggleAttribute("disabled", this.locked); + } + } if (!this.isElementEditable(aElement)) { return; diff --git a/toolkit/content/tests/browser/browser_bug1693577.js b/toolkit/content/tests/browser/browser_bug1693577.js index 712749dc89..b85b9f13de 100644 --- a/toolkit/content/tests/browser/browser_bug1693577.js +++ b/toolkit/content/tests/browser/browser_bug1693577.js @@ -12,7 +12,7 @@ add_task(async function test_search_input_popupshowing() { let sidebar = document.getElementById("sidebar"); let loadPromise = BrowserTestUtils.waitForEvent(sidebar, "load", true); - SidebarUI.toggle("viewBookmarksSidebar"); + SidebarController.toggle("viewBookmarksSidebar"); await loadPromise; let inputField = @@ -45,5 +45,5 @@ add_task(async function test_search_input_popupshowing() { popup.hidePopup(); await popuphidden; - SidebarUI.toggle("viewBookmarksSidebar"); + SidebarController.toggle("viewBookmarksSidebar"); }); diff --git a/toolkit/content/tests/chrome/chrome.toml b/toolkit/content/tests/chrome/chrome.toml index 986822ac48..e037f45ecf 100644 --- a/toolkit/content/tests/chrome/chrome.toml +++ b/toolkit/content/tests/chrome/chrome.toml @@ -51,7 +51,6 @@ support-files = [ ] prefs = [ "gfx.font_rendering.fallback.async=false", - "widget.non-native-theme.enabled=false", ] ["test_about_networking.html"] diff --git a/toolkit/content/tests/chrome/file_editor_with_autocomplete.js b/toolkit/content/tests/chrome/file_editor_with_autocomplete.js index acf2c9e9df..0068440785 100644 --- a/toolkit/content/tests/chrome/file_editor_with_autocomplete.js +++ b/toolkit/content/tests/chrome/file_editor_with_autocomplete.js @@ -84,6 +84,10 @@ nsDoTestsForEditorWithAutoComplete.prototype = { Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH ); }); + if (test.popup) { + await waitForCondition(() => this._controller.input.popupOpen); + } + this._target.removeEventListener("beforeinput", onBeforeInput); this._target.removeEventListener("input", onInput); this._checkResult(test, beforeInputEvents, inputEvents); diff --git a/toolkit/content/tests/chrome/test_autocomplete_with_composition_on_input.html b/toolkit/content/tests/chrome/test_autocomplete_with_composition_on_input.html index fbcd44e830..fd484a93d1 100644 --- a/toolkit/content/tests/chrome/test_autocomplete_with_composition_on_input.html +++ b/toolkit/content/tests/chrome/test_autocomplete_with_composition_on_input.html @@ -27,7 +27,6 @@ function runTests() { .QueryInterface(Ci.nsIAutoCompleteInput); var originalFormFillTimeout = formFillController.timeout; - SpecialPowers.attachFormFillControllerTo(window); var target = document.getElementById("input"); // Register a word to the form history. @@ -53,7 +52,6 @@ function runTests() { function() { return target.value; }, function() { formFillController.timeout = originalFormFillTimeout; - SpecialPowers.detachFormFillControllerFrom(window); SimpleTest.finish(); }); }); diff --git a/toolkit/content/tests/chrome/test_editor_for_input_with_autocomplete.html b/toolkit/content/tests/chrome/test_editor_for_input_with_autocomplete.html index 91f0f159b4..f2786d47be 100644 --- a/toolkit/content/tests/chrome/test_editor_for_input_with_autocomplete.html +++ b/toolkit/content/tests/chrome/test_editor_for_input_with_autocomplete.html @@ -51,7 +51,6 @@ async function runTests() { .QueryInterface(Ci.nsIAutoCompleteInput); var originalFormFillTimeout = formFillController.timeout; - SpecialPowers.attachFormFillControllerTo(window); var target = document.getElementById("input"); // Register a word to the form history. @@ -71,7 +70,6 @@ async function runTests() { await tests2.run(); formFillController.timeout = originalFormFillTimeout; - SpecialPowers.detachFormFillControllerFrom(window); SimpleTest.finish(); } diff --git a/toolkit/content/tests/chrome/window_largemenu.xhtml b/toolkit/content/tests/chrome/window_largemenu.xhtml index 8e6b6718b4..413ab248bc 100644 --- a/toolkit/content/tests/chrome/window_largemenu.xhtml +++ b/toolkit/content/tests/chrome/window_largemenu.xhtml @@ -336,14 +336,18 @@ function testPopupMovement() var marginTop = parseFloat(getComputedStyle(popup).marginTop); var panelIsTop = SpecialPowers.getBoolPref("ui.panel.default_level_parent"); - var overlapOSChrome = !platformIsMac() && (!isPanelTest || panelIsTop); + var overlapOSChrome = canOverlapOSChrome() && (!isPanelTest || panelIsTop); popup.moveTo(1, 1); [screenX, screenY] = getScreenXY(popup); var expectedx = 1, expectedy = 1; if (!overlapOSChrome) { - if (screen.availLeft >= 1) expectedx = screen.availLeft; - if (screen.availTop >= 1) expectedy = screen.availTop; + if (screen.availLeft >= 1) { + expectedx = screen.availLeft + marginLeft; + } + if (screen.availTop >= 1) { + expectedy = screen.availTop + marginTop; + } } is(screenX, expectedx, gTests[gTestIndex] + " (1, 1) x"); is(screenY, expectedy, gTests[gTestIndex] + " (1, 1) y"); @@ -398,6 +402,11 @@ function platformIsMac() return navigator.platform.indexOf("Mac") > -1; } +function canOverlapOSChrome() +{ + return navigator.platform.startsWith("Win"); +} + window.arguments[0].SimpleTest.waitForFocus(runTests, window); ]]> diff --git a/toolkit/content/tests/widgets/mochitest.toml b/toolkit/content/tests/widgets/mochitest.toml index efd86f1208..f5bc0505bf 100644 --- a/toolkit/content/tests/widgets/mochitest.toml +++ b/toolkit/content/tests/widgets/mochitest.toml @@ -96,6 +96,8 @@ skip-if = [ ["test_videocontrols_size.html"] +["test_videocontrols_src_change.html"] + ["test_videocontrols_standalone.html"] skip-if = [ "os == 'linux'", # bug 1804621 diff --git a/toolkit/content/tests/widgets/test_moz_button.html b/toolkit/content/tests/widgets/test_moz_button.html index a849ccc956..0e64f939dc 100644 --- a/toolkit/content/tests/widgets/test_moz_button.html +++ b/toolkit/content/tests/widgets/test_moz_button.html @@ -17,6 +17,33 @@ } - Test button - - Test button - - - + +
    +
    + Test button + + Test button + + + + + + + +
    + +
    diff --git a/toolkit/content/tests/widgets/test_moz_button_group.html b/toolkit/content/tests/widgets/test_moz_button_group.html index 92fd7776fa..471d554599 100644 --- a/toolkit/content/tests/widgets/test_moz_button_group.html +++ b/toolkit/content/tests/widgets/test_moz_button_group.html @@ -10,6 +10,7 @@ solution for token variables for the new widgets --> +

    @@ -20,6 +21,7 @@ + Test
         
    @@ -74,54 +76,80 @@ add_setup(async function setup() { ({ html, render} = await import("chrome://global/content/vendor/lit.all.mjs")); document.querySelector("moz-button-group").remove(); + document.querySelector("moz-button").remove(); }); add_task(async function testButtonOrderingSlot() { - render( + let markupOptions = [ html` `, - renderArea - ); + html` + + Primary + Secondary + + `, + ]; + for (let markup of markupOptions) { + render(markup, renderArea); - let buttonGroup = document.querySelector("moz-button-group"); - let primaryButton = document.getElementById("primary-button"); - let secondaryButton = document.getElementById("secondary-button"); + let buttonGroup = document.querySelector("moz-button-group"); + let primaryButton = document.getElementById("primary-button"); + let secondaryButton = document.getElementById("secondary-button"); - buttonGroup.platform = "win"; - await buttonGroup.updateComplete; - await checkButtons(primaryButton, secondaryButton); - buttonGroup.platform = "macosx"; - await buttonGroup.updateComplete; - await checkButtons(secondaryButton, primaryButton); + buttonGroup.platform = "win"; + await buttonGroup.updateComplete; + await checkButtons(primaryButton, secondaryButton); + + buttonGroup.platform = "macosx"; + await buttonGroup.updateComplete; + await checkButtons(secondaryButton, primaryButton); + } }); add_task(async function testPrimaryButtonAutoSlotting() { - render( + let markupOptions = [ html` `, - renderArea - ); - - let buttonGroup = document.querySelector("moz-button-group"); - let primaryButton = buttonGroup.querySelector(".primary"); - let secondaryButton = buttonGroup.querySelector(".secondary"); - buttonGroup.platform = "win"; - await buttonGroup.updateComplete; - is(primaryButton.slot, "primary", "primary button was auto-slotted") - await checkButtons(primaryButton, secondaryButton); - - buttonGroup.platform = "macosx"; - await buttonGroup.updateComplete; - await checkButtons(secondaryButton, primaryButton); + html` + + Primary + Secondary + + `, + html` + + Delete + Secondary + + `, + ]; + for (let markup of markupOptions) { + render(markup, renderArea); + + let buttonGroup = document.querySelector("moz-button-group"); + let primaryButton = buttonGroup.querySelector( + ".primary, [type=primary], [type=destructive]" + ); + let secondaryButton = buttonGroup.querySelector(".secondary, [type=default]"); + buttonGroup.platform = "win"; + await buttonGroup.updateComplete; + is(primaryButton.slot, "primary", "primary button was auto-slotted") + await checkButtons(primaryButton, secondaryButton); + + buttonGroup.platform = "macosx"; + await buttonGroup.updateComplete; + await checkButtons(secondaryButton, primaryButton); + } }); add_task(async function testSubmitButtonAutoSlotting() { @@ -197,38 +225,48 @@ }); add_task(async function testInitialButtonLightDomReordering() { - const renderPlatform = platform => render( - html` + let markupOptions = [ + platform => html` `, - renderArea - ); - - renderPlatform("win"); - let buttonGroup = document.querySelector("moz-button-group"); - await buttonGroup.updateComplete; - let primaryButton = buttonGroup.querySelector(".primary"); - let defaultButton = buttonGroup.querySelector("[default]"); - let secondaryButton = buttonGroup.querySelector(".secondary"); - - is(primaryButton.slot, "primary", "primary button was auto-slotted"); - is(defaultButton.slot, "primary", "default button was auto-slotted"); - await checkButtons(primaryButton, defaultButton, secondaryButton); - - renderPlatform("macosx"); - buttonGroup = document.querySelector("moz-button-group"); - await buttonGroup.updateComplete; - primaryButton = buttonGroup.querySelector(".primary"); - defaultButton = buttonGroup.querySelector("[default]"); - secondaryButton = buttonGroup.querySelector(".secondary"); - - is(primaryButton.slot, "primary", "primary button was auto-slotted"); - is(defaultButton.slot, "primary", "default button was auto-slotted"); - await checkButtons(secondaryButton, primaryButton, defaultButton); + platform => html` + + First + Secondary + Default + + `, + ]; + + for (let markup of markupOptions) { + const renderPlatform = platform => render(markup(platform), renderArea); + + renderPlatform("win"); + let buttonGroup = document.querySelector("moz-button-group"); + await buttonGroup.updateComplete; + let primaryButton = buttonGroup.querySelector(".primary"); + let defaultButton = buttonGroup.querySelector("[default]"); + let secondaryButton = buttonGroup.querySelector(".secondary"); + + is(primaryButton.slot, "primary", "primary button was auto-slotted"); + is(defaultButton.slot, "primary", "default button was auto-slotted"); + await checkButtons(primaryButton, defaultButton, secondaryButton); + + renderPlatform("macosx"); + buttonGroup = document.querySelector("moz-button-group"); + await buttonGroup.updateComplete; + primaryButton = buttonGroup.querySelector(".primary"); + defaultButton = buttonGroup.querySelector("[default]"); + secondaryButton = buttonGroup.querySelector(".secondary"); + + is(primaryButton.slot, "primary", "primary button was auto-slotted"); + is(defaultButton.slot, "primary", "default button was auto-slotted"); + await checkButtons(secondaryButton, primaryButton, defaultButton); + } }); diff --git a/toolkit/content/tests/widgets/test_moz_message_bar.html b/toolkit/content/tests/widgets/test_moz_message_bar.html index 7ee6825ef3..9b48982962 100644 --- a/toolkit/content/tests/widgets/test_moz_message_bar.html +++ b/toolkit/content/tests/widgets/test_moz_message_bar.html @@ -1,27 +1,30 @@ + MozMessageBar tests - + + -

    -
    - - - - - -
    -
    +  

    +
    + + + + + +
    +
       
     
    + diff --git a/toolkit/content/tests/widgets/test_moz_page_nav.html b/toolkit/content/tests/widgets/test_moz_page_nav.html index 604df7c024..e03bfa22e2 100644 --- a/toolkit/content/tests/widgets/test_moz_page_nav.html +++ b/toolkit/content/tests/widgets/test_moz_page_nav.html @@ -37,6 +37,12 @@ body { View 5 + + Support Link + + + External Link + @@ -120,10 +126,46 @@ function isActiveElement(expectedActiveEl) { ) }); + /** + * Tests that footer support links have the expected attributes + */ + add_task(async function test_support_link() { + const supportLinkRootPath = "https://support.mozilla.org/"; + let supportLink = mozPageNav.secondaryNavButtons[0]; + ok(supportLink.linkEl, "The secondary nav button contains the link element."); + ok( + supportLink.linkEl.hasAttribute("is") && supportLink.linkEl.getAttribute("is") === "moz-support-link", + "The support link has the is=moz-support-link attribute." + ); + ok( + supportLink.linkEl.hasAttribute("href") && supportLink.linkEl.getAttribute("href") === `${supportLinkRootPath}test`, + "The support link has the expected href atrribute." + ); + }); + + /** + * Tests that footer external links have the expected attributes + */ + add_task(async function test_external_link() { + const externalLinkPath = "https://www.example.com"; + let externalLink = mozPageNav.secondaryNavButtons[1]; + ok(externalLink.linkEl, "The secondary nav button contains the link element."); + ok(!externalLink.linkEl.hasAttribute("is"), "The external link doesn't have the is=moz-support-link attribute."); + ok( + externalLink.linkEl.hasAttribute("href") && externalLink.linkEl.getAttribute("href") === externalLinkPath, + "The external link has the expected href atrribute." + ); + }); + /** * Tests that categories are keyboard-navigable */ add_task(async function test_keyboard_navigation() { + const tab = async shiftKey => { + info(`Tab${shiftKey ? " + Shift" : ""}`); + synthesizeKey("KEY_Tab", { shiftKey }); + await mozPageNav.updateComplete; + }; const arrowDown = async () => { info("Arrow down"); synthesizeKey("KEY_ArrowDown", {}); @@ -155,6 +197,8 @@ function isActiveElement(expectedActiveEl) { let thirdPageNavButton = mozPageNav.pageNavButtons[2]; let fourthPageNavButton = mozPageNav.pageNavButtons[3]; let fifthPageNavButton = mozPageNav.pageNavButtons[4]; + let supportLink = mozPageNav.secondaryNavButtons[0]; + let externalLink = mozPageNav.secondaryNavButtons[1]; is( firstPageNavButton.view, @@ -196,6 +240,23 @@ function isActiveElement(expectedActiveEl) { mozPageNav.currentView, "The fifth page nav button is still selected" ) + await tab(); + ok( + isActiveElement(supportLink.linkEl), + "The support link is selected" + ) + await tab(); + ok( + isActiveElement(externalLink.linkEl), + "The external link is selected" + ) + await tab(true); + await tab(true); + is( + fifthPageNavButton.view, + mozPageNav.currentView, + "The fifth page nav button is selected" + ) await arrowUp(); is( fourthPageNavButton.view, diff --git a/toolkit/content/tests/widgets/test_videocontrols_src_change.html b/toolkit/content/tests/widgets/test_videocontrols_src_change.html new file mode 100644 index 0000000000..535b9483cd --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_src_change.html @@ -0,0 +1,56 @@ + + + + + + Video controls test + + + + + + + +

    + +
    + +
    + +
    + +
    +
    +
    + + + diff --git a/toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html b/toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html index 99d5bdad01..be28ed24c9 100644 --- a/toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html +++ b/toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html @@ -29,7 +29,7 @@ }); add_task(async function mute_button_icon() { - is(muteButton.getAttribute("noAudio"), "true"); + ok(muteButton.hasAttribute("noAudio"), "Mute button should have noAudio attribute"); ok(muteButton.hasAttribute("disabled"), "Mute button should be disabled"); if (volumeStack) { diff --git a/toolkit/content/widgets.css b/toolkit/content/widgets.css index d7f1b68602..16b6d5cb94 100644 --- a/toolkit/content/widgets.css +++ b/toolkit/content/widgets.css @@ -22,4 +22,4 @@ @import url("chrome://global/skin/tabbox.css"); @import url("chrome://global/skin/toolbar.css"); @import url("chrome://global/skin/toolbarbutton.css"); -@import url("chrome://global/skin/tree.css"); +@import url("chrome://global/skin/tree/tree.css"); diff --git a/toolkit/content/widgets/arrowscrollbox.js b/toolkit/content/widgets/arrowscrollbox.js index 7109891faf..5983435d06 100644 --- a/toolkit/content/widgets/arrowscrollbox.js +++ b/toolkit/content/widgets/arrowscrollbox.js @@ -21,7 +21,7 @@ return ` - + @@ -29,7 +29,7 @@ - + `; } diff --git a/toolkit/content/widgets/autocomplete-richlistitem.js b/toolkit/content/widgets/autocomplete-richlistitem.js index b339ab1e27..0ec8a19243 100644 --- a/toolkit/content/widgets/autocomplete-richlistitem.js +++ b/toolkit/content/widgets/autocomplete-richlistitem.js @@ -832,9 +832,7 @@ setTimeout(() => { let selectedIndex = popup ? popup.selectedIndex : -1; - actor.manager - .getActor("FormAutofill") - .sendAsyncMessage("FormAutofill:PreviewProfile", { selectedIndex }); + actor.previewAutofillProfile(selectedIndex); }, 0); } diff --git a/toolkit/content/widgets/datetimebox.js b/toolkit/content/widgets/datetimebox.js index 04ed398bd7..1c63b09269 100644 --- a/toolkit/content/widgets/datetimebox.js +++ b/toolkit/content/widgets/datetimebox.js @@ -650,6 +650,10 @@ this.DateTimeBoxWidget = class { onKeyDown(aEvent) { this.log("onKeyDown key: " + aEvent.key); + if (aEvent.defaultPrevented) { + return; + } + switch (aEvent.key) { // Toggle the picker on Space/Enter on Calendar button or Space on input, // close on Escape anywhere. @@ -691,21 +695,17 @@ this.DateTimeBoxWidget = class { aEvent.preventDefault(); break; } - if (this.isEditable()) { - // TODO(emilio, bug 1571533): These functions should look at - // defaultPrevented. - // Ctrl+Backspace/Delete on non-macOS and - // Cmd+Backspace/Delete on macOS to clear the field - if (aEvent.getModifierState("Accel")) { - // Clear the input's value - this.clearInputFields(false); - } else { - let targetField = aEvent.originalTarget; - this.clearFieldValue(targetField); - this.setInputValueFromFields(); - } - aEvent.preventDefault(); + // Ctrl+Backspace/Delete on non-macOS and + // Cmd+Backspace/Delete on macOS to clear the field + if (aEvent.getModifierState("Accel")) { + // Clear the input's value + this.clearInputFields(false); + } else { + let targetField = aEvent.originalTarget; + this.clearFieldValue(targetField); + this.setInputValueFromFields(); } + aEvent.preventDefault(); break; } case "ArrowRight": diff --git a/toolkit/content/widgets/message-bar.css b/toolkit/content/widgets/message-bar.css deleted file mode 100644 index eddc5a3ae6..0000000000 --- a/toolkit/content/widgets/message-bar.css +++ /dev/null @@ -1,219 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -:host { - --info-icon-url: url("chrome://global/skin/icons/info-filled.svg"); - --warn-icon-url: url("chrome://global/skin/icons/warning.svg"); - --success-icon-url: url("chrome://global/skin/icons/check.svg"); - --error-icon-url: url("chrome://global/skin/icons/error.svg"); - --close-icon-url: url("chrome://global/skin/icons/close-12.svg"); - --close-fill-color: var(--in-content-icon-color); - --icon-size: 16px; - --close-icon-size: 28px; -} - -:host { - --message-bar-background-color: var(--in-content-box-info-background); - --message-bar-text-color: var(--in-content-text-color); - --message-bar-icon-url: var(--info-icon-url); - /* The default values of --in-content-button* are sufficient, even for dark themes */ -} - -:host([type=warning]) { - --message-bar-icon-url: var(--warn-icon-url); -} - -:host([type=success]) { - --message-bar-icon-url: var(--success-icon-url); -} - -:host([type=error]), -:host([type=critical]) { - --message-bar-icon-url: var(--error-icon-url); -} - -:host { - border: 1px solid transparent; - border-radius: 4px; -} - -/* Make the host to behave as a block by default, but allow hidden to hide it. */ -:host(:not([hidden])) { - display: block; -} - -::slotted(button) { - /* Enforce micro-button width. */ - min-width: -moz-fit-content !important; -} - -/* MessageBar Grid Layout */ - -.container { - background: var(--message-bar-background-color); - color: var(--message-bar-text-color); - - padding: 3px 7px; - position: relative; - - border-radius: 4px; - - display: flex; - /* Ensure that the message bar shadow dom elements are vertically aligned. */ - align-items: center; -} - -:host([align="center"]) .container { - justify-content: center; -} - -.content { - margin: 0 4px; - display: inline-block; - /* Ensure that the message bar content is vertically aligned. */ - align-items: center; - /* Ensure that the message bar content is wrapped. */ - word-break: break-word; -} - -/* MessageBar icon style */ - -.icon { - padding: 4px; - width: var(--icon-size); - height: var(--icon-size); - flex-shrink: 0; -} - -.icon::after { - display: inline-block; - appearance: none; - -moz-context-properties: fill, stroke; - fill: currentColor; - stroke: currentColor; - content: ""; - background-image: var(--message-bar-icon-url); - background-size: var(--icon-size); - width: var(--icon-size); - height: var(--icon-size); -} - -/* Use a spacer to position the close button at the end, but also support - * centering if required. */ -.spacer { - flex-grow: 1; -} - -/* Close icon styles */ - -:host(:not([dismissable])) .close { - display: none; -} - -.close { - background-image: var(--close-icon-url); - background-repeat: no-repeat; - background-position: center center; - -moz-context-properties: fill; - fill: currentColor; - min-width: auto; - min-height: auto; - width: var(--close-icon-size); - height: var(--close-icon-size); - padding: 0; - flex-shrink: 0; - margin: 4px 8px; - background-size: 12px; -} - -@media (prefers-contrast) { - :host { - border-color: CanvasText; - } -} - -@media not (prefers-contrast) { - /* MessageBar colors by message type */ - /* Colors from: https://design.firefox.com/photon/components/message-bars.html#type-specific-style */ - - :host([type=warning]) { - /* Ensure colors within the bar are adjusted and controls are readable */ - color-scheme: light; - - --message-bar-background-color: var(--yellow-50); - --message-bar-text-color: var(--yellow-90); - - --in-content-button-background: var(--yellow-60); - --in-content-button-background-hover: var(--yellow-70); - --in-content-button-background-active: var(--yellow-80); - - --close-fill-color: var(--message-bar-text-color); - } - - :host([type=success]) { - /* Ensure colors within the bar are adjusted and controls are readable */ - color-scheme: light; - - --message-bar-background-color: var(--green-50); - --message-bar-text-color: var(--green-90); - - --in-content-button-background: var(--green-60); - --in-content-button-background-hover: var(--green-70); - --in-content-button-background-active: var(--green-80); - } - - :host([type=error]) { - --message-bar-background-color: var(--red-60); - --message-bar-text-color: #ffffff; - - --in-content-button-background: var(--red-70); - --in-content-button-background-hover: var(--red-80); - --in-content-button-background-active: var(--red-90); - } - - :host([type=info]) .icon { - color: rgb(0,144,237); - } - - :host([type=warning]) .icon { - color: rgb(255,164,54); - } - - :host([type=critical]) .icon { - color: rgb(226,40,80); - } - - .close { - fill: var(--close-fill-color); - } - - @media (prefers-color-scheme: dark) { - /* Don't set the background in prefers-contrast mode or macOS can end up - * with black on black text. */ - :host([type=info]) .icon { - color: rgb(128,235,255); - } - - :host([type=warning]) .icon { - color: rgb(255,189,79); - } - - :host([type=critical]) .icon { - color: rgb(255,154,162); - } - } -} - -strong { - font-weight: 600; -} - -.text-link:hover { - cursor: pointer; -} - -@keyframes spin { - from { transform: rotate(0); } - to { transform: rotate(360deg); } -} diff --git a/toolkit/content/widgets/message-bar.js b/toolkit/content/widgets/message-bar.js deleted file mode 100644 index d38347b40a..0000000000 --- a/toolkit/content/widgets/message-bar.js +++ /dev/null @@ -1,91 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -// This is loaded into chrome windows with the subscript loader. Wrap in -// a block to prevent accidentally leaking globals onto `window`. -{ - class MessageBarElement extends HTMLElement { - constructor() { - super(); - const shadowRoot = this.attachShadow({ mode: "open" }); - window.MozXULElement?.insertFTLIfNeeded( - "toolkit/global/notification.ftl" - ); - document.l10n.connectRoot(this.shadowRoot); - const content = this.constructor.template.content.cloneNode(true); - shadowRoot.append(content); - this.closeButton.addEventListener("click", () => this.dismiss(), { - once: true, - }); - } - - disconnectedCallback() { - this.dispatchEvent(new CustomEvent("message-bar:close")); - } - - get closeButton() { - return this.shadowRoot.querySelector("button.close"); - } - - static get template() { - const template = document.createElement("template"); - - const commonStyles = document.createElement("link"); - commonStyles.rel = "stylesheet"; - commonStyles.href = "chrome://global/skin/in-content/common.css"; - const messageBarStyles = document.createElement("link"); - messageBarStyles.rel = "stylesheet"; - messageBarStyles.href = - "chrome://global/content/elements/message-bar.css"; - template.content.append(commonStyles, messageBarStyles); - - // A container for the entire message bar content, - // most of the css rules needed to provide the - // expected message bar layout is applied on this - // element. - const container = document.createElement("div"); - container.part = "container"; - container.classList.add("container"); - template.content.append(container); - - const icon = document.createElement("span"); - icon.classList.add("icon"); - icon.part = "icon"; - container.append(icon); - - const barcontent = document.createElement("span"); - barcontent.classList.add("content"); - barcontent.append(document.createElement("slot")); - container.append(barcontent); - - const spacer = document.createElement("span"); - spacer.classList.add("spacer"); - container.append(spacer); - - const closeIcon = document.createElement("button"); - closeIcon.classList.add("close", "ghost-button"); - document.l10n.setAttributes(closeIcon, "notification-close-button"); - container.append(closeIcon); - - Object.defineProperty(this, "template", { - value: template, - }); - - return template; - } - - dismiss() { - this.dispatchEvent(new CustomEvent("message-bar:user-dismissed")); - this.close(); - } - - close() { - this.remove(); - } - } - - customElements.define("message-bar", MessageBarElement); -} diff --git a/toolkit/content/widgets/moz-button-group/moz-button-group.css b/toolkit/content/widgets/moz-button-group/moz-button-group.css index ba79d69e12..5b6f7deace 100644 --- a/toolkit/content/widgets/moz-button-group/moz-button-group.css +++ b/toolkit/content/widgets/moz-button-group/moz-button-group.css @@ -11,6 +11,7 @@ margin: 0 !important; } -::slotted(button:not(:first-child, .popup-notification-dropmarker)) { +::slotted(button:not(:first-child, .popup-notification-dropmarker)), +::slotted(moz-button:not(:first-child)) { margin-inline-start: var(--space-small) !important; } diff --git a/toolkit/content/widgets/moz-button-group/moz-button-group.mjs b/toolkit/content/widgets/moz-button-group/moz-button-group.mjs index 8bf553c23d..6858367ab7 100644 --- a/toolkit/content/widgets/moz-button-group/moz-button-group.mjs +++ b/toolkit/content/widgets/moz-button-group/moz-button-group.mjs @@ -50,15 +50,22 @@ export default class MozButtonGroup extends MozLitElement { // Text nodes won't support classList or getAttribute. continue; } - // Bug 1791816: These should check moz-button instead of button. - if ( - child.localName == "button" && - (child.classList.contains("primary") || - child.getAttribute("type") == "submit" || - child.hasAttribute("autofocus") || - child.hasAttribute("default")) - ) { - child.slot = "primary"; + switch (child.localName) { + case "button": + if ( + child.classList.contains("primary") || + child.getAttribute("type") == "submit" || + child.hasAttribute("autofocus") || + child.hasAttribute("default") + ) { + child.slot = "primary"; + } + break; + case "moz-button": + if (child.type == "primary" || child.type == "destructive") { + child.slot = "primary"; + } + break; } } this.#reorderLightDom(); diff --git a/toolkit/content/widgets/moz-button/moz-button.css b/toolkit/content/widgets/moz-button/moz-button.css index 71d57ea93a..4eb6839e06 100644 --- a/toolkit/content/widgets/moz-button/moz-button.css +++ b/toolkit/content/widgets/moz-button/moz-button.css @@ -23,6 +23,10 @@ button { /* Ensure font-size isn't overridden by widget styling (e.g. in forms.css) */ font-size: var(--button-font-size); width: 100%; + display: flex; + justify-content: center; + align-items: center; + gap: var(--space-small); &[size=small] { min-height: var(--button-min-height-small); @@ -125,21 +129,33 @@ button { } } - &[type~=icon] { + &[type~=icon]:not(.labelled) { background-size: var(--icon-size-default); background-position: center; background-repeat: no-repeat; - -moz-context-properties: fill, stroke; - fill: currentColor; - stroke: currentColor; + } + + &[type~=icon]:not(.labelled), + &:not(.labelled):has(img) { width: var(--button-size-icon); height: var(--button-size-icon); padding: var(--button-padding-icon); - color: var(--icon-color); &[size=small] { width: var(--button-size-icon-small); height: var(--button-size-icon-small); } } + + img, + &[type~=icon]:not(.labelled) { + -moz-context-properties: fill, fill-opacity, stroke; + fill: currentColor; + stroke: currentColor; + } + + img { + width: var(--icon-size-default); + height: var(--icon-size-default); + } } diff --git a/toolkit/content/widgets/moz-button/moz-button.mjs b/toolkit/content/widgets/moz-button/moz-button.mjs index a951dbf5d8..9a239d4e56 100644 --- a/toolkit/content/widgets/moz-button/moz-button.mjs +++ b/toolkit/content/widgets/moz-button/moz-button.mjs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { html, ifDefined } from "../vendor/lit.all.mjs"; +import { html, ifDefined, classMap } from "../vendor/lit.all.mjs"; import { MozLitElement } from "../lit-utils.mjs"; /** @@ -19,8 +19,11 @@ import { MozLitElement } from "../lit-utils.mjs"; * @property {string} titleAttribute - Internal, map title attribute to the title JS property. * @property {string} tooltipText - Set the title property, the title attribute will be used first. * @property {string} ariaLabel - The button's arial-label attribute, used in shadow DOM and therefore not as an attribute on moz-button. + * @property {string} iconSrc - Path to the icon that should be displayed in the button. * @property {string} ariaLabelAttribute - Internal, map aria-label attribute to the ariaLabel JS property. + * @property {string} hasVisibleLabel - Internal, tracks whether or not the button has a visible label. * @property {HTMLButtonElement} buttonEl - The internal button element in the shadow DOM. + * @property {HTMLButtonElement} slotEl - The internal slot element in the shadow DOM. * @slot default - The button's content, overrides label property. * @fires click - The click event. */ @@ -44,10 +47,13 @@ export default class MozButton extends MozLitElement { reflect: true, }, ariaLabel: { type: String, state: true }, + iconSrc: { type: String }, + hasVisibleLabel: { type: Boolean, state: true }, }; static queries = { buttonEl: "button", + slotEl: "slot", }; constructor() { @@ -55,6 +61,7 @@ export default class MozButton extends MozLitElement { this.type = "default"; this.size = "default"; this.disabled = false; + this.hasVisibleLabel = !!this.label; } willUpdate(changes) { @@ -73,6 +80,12 @@ export default class MozButton extends MozLitElement { this.buttonEl.click(); } + checkForLabelText() { + this.hasVisibleLabel = this.slotEl + .assignedNodes() + .some(node => node.textContent.trim()); + } + render() { return html` - ${this.label} + ${this.iconSrc + ? html`` + : ""} + ${this.label} `; } diff --git a/toolkit/content/widgets/moz-button/moz-button.stories.mjs b/toolkit/content/widgets/moz-button/moz-button.stories.mjs index 52a459e807..dd8d6369db 100644 --- a/toolkit/content/widgets/moz-button/moz-button.stories.mjs +++ b/toolkit/content/widgets/moz-button/moz-button.stories.mjs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { html } from "../vendor/lit.all.mjs"; +import { html, ifDefined } from "../vendor/lit.all.mjs"; // eslint-disable-next-line import/no-unassigned-import import "./moz-button.mjs"; @@ -22,6 +22,10 @@ export default { options: ["default", "small"], control: { type: "radio" }, }, + type: { + options: ["default", "primary", "destructive", "icon", "icon ghost"], + control: { type: "select" }, + }, }, parameters: { actions: { @@ -40,17 +44,13 @@ moz-button-aria-labelled = }, }; -const Template = ({ type, size, l10nId, iconUrl, disabled }) => html` - +const Template = ({ type, size, l10nId, iconSrc, disabled }) => html` `; @@ -59,7 +59,7 @@ Default.args = { type: "default", size: "default", l10nId: "moz-button-labelled", - iconUrl: "chrome://global/skin/icons/more.svg", + iconSrc: "", disabled: false, }; export const DefaultSmall = Template.bind({}); @@ -67,7 +67,7 @@ DefaultSmall.args = { type: "default", size: "small", l10nId: "moz-button-labelled", - iconUrl: "chrome://global/skin/icons/more.svg", + iconSrc: "", disabled: false, }; export const Primary = Template.bind({}); @@ -85,7 +85,7 @@ Destructive.args = { export const Icon = Template.bind({}); Icon.args = { ...Default.args, - type: "icon", + iconSrc: "chrome://global/skin/icons/more.svg", l10nId: "moz-button-titled", }; export const IconSmall = Template.bind({}); @@ -96,5 +96,12 @@ IconSmall.args = { export const IconGhost = Template.bind({}); IconGhost.args = { ...Icon.args, - type: "icon ghost", + type: "ghost", +}; +export const IconText = Template.bind({}); +IconText.args = { + type: "default", + size: "default", + iconSrc: "chrome://global/skin/icons/edit-copy.svg", + l10nId: "moz-button-labelled", }; diff --git a/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs b/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs index 8f0c997149..9a186b41c9 100644 --- a/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs +++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs @@ -49,10 +49,10 @@ const messageTypeToIconData = { export default class MozMessageBar extends MozLitElement { static queries = { - actionsSlotEl: "slot[name=actions]", + actionsSlot: "slot[name=actions]", actionsEl: ".actions", - closeButtonEl: "moz-button.close", - supportLinkSlotEl: "slot[name=support-link]", + closeButton: "moz-button.close", + supportLinkSlot: "slot[name=support-link]", }; static properties = { @@ -72,13 +72,13 @@ export default class MozMessageBar extends MozLitElement { } onSlotchange() { - let actions = this.actionsSlotEl.assignedNodes(); + let actions = this.actionsSlot.assignedNodes(); this.actionsEl.classList.toggle("active", actions.length); } connectedCallback() { super.connectedCallback(); - this.setAttribute("role", "status"); + this.setAttribute("role", "alert"); } disconnectedCallback() { @@ -87,7 +87,7 @@ export default class MozMessageBar extends MozLitElement { } get supportLinkEls() { - return this.supportLinkSlotEl.assignedElements(); + return this.supportLinkSlot.assignedElements(); } iconTemplate() { diff --git a/toolkit/content/widgets/moz-page-nav/README.stories.md b/toolkit/content/widgets/moz-page-nav/README.stories.md index 800d446478..c2f1b37bb5 100644 --- a/toolkit/content/widgets/moz-page-nav/README.stories.md +++ b/toolkit/content/widgets/moz-page-nav/README.stories.md @@ -4,7 +4,7 @@ intended to change the selected view, provide a heading, and have links to external resources. ```html story - +

    Test 3

    + +

    Support Link

    +
    + +

    External Link

    +
    ``` ## When to use * Use moz-page-nav for single-page navigation to switch between different views. -* moz-page-nav will also support footer buttons for external support links in the future (See [bug 1877826](https://bugzilla.mozilla.org/show_bug.cgi?id=1877826)) +* moz-page-nav also supports footer buttons for external and support links * This component will be used in about: pages such as about:firefoxview, about:preferences, about:addons, about:debugging, etc. ## When not to use @@ -53,18 +67,34 @@ And used as follows: ```html - - - + - - + - + + + + + + + + + + + ``` diff --git a/toolkit/content/widgets/moz-page-nav/moz-page-nav-button.css b/toolkit/content/widgets/moz-page-nav/moz-page-nav-button.css index 5d00198d65..781398056a 100644 --- a/toolkit/content/widgets/moz-page-nav/moz-page-nav-button.css +++ b/toolkit/content/widgets/moz-page-nav/moz-page-nav-button.css @@ -4,11 +4,13 @@ :host { border-radius: var(--border-radius-small); + font-size: var(--font-size-large); &:focus-visible { outline-offset: var(--page-nav-focus-outline-inset); } } +a[href], button { background-color: var(--page-nav-button-background-color); border: 1px solid var(--page-nav-border-color); @@ -28,11 +30,17 @@ button { padding: var(--page-nav-button-padding); } +a[href] { + text-decoration: none; + box-sizing: border-box; +} + button:hover { cursor: pointer; } @media not (prefers-contrast) { + a[href], button { position: relative; } @@ -55,6 +63,7 @@ button:hover { background-color: var(--icon-color); } + a[href]:hover, button:hover, button[selected]:hover { background-color: var(--page-nav-button-background-color-hover); @@ -72,6 +81,7 @@ button:hover { } } +a[href]:focus-visible, button:focus-visible, button[selected]:focus-visible { outline: var(--focus-outline); @@ -86,13 +96,24 @@ button[selected]:focus-visible { fill: currentColor; } +:host(.secondary-nav-item) { + font-size: var(--font-size-small); + + & .page-nav-icon { + height: var(--icon-size-default); + width: var(--icon-size-default); + } +} + @media (prefers-contrast) { + a[href], button { transition: none; border-color: ButtonText; background-color: var(--button-background-color); } + a[href]:hover, button:hover { color: SelectedItem; } @@ -105,13 +126,13 @@ button[selected]:focus-visible { } slot { - font-size: var(--font-size-large); margin: 0; padding-inline-start: 0; user-select: none; } @media (max-width: 52rem) { + a[href], button { grid-template-columns: min-content; justify-content: center; diff --git a/toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs b/toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs index f998ee735f..c720e76e26 100644 --- a/toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs +++ b/toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs @@ -4,6 +4,8 @@ import { html } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "chrome://global/content/elements/moz-support-link.mjs"; /** * A grouping of navigation buttons that is displayed at the page level, @@ -35,6 +37,14 @@ export default class MozPageNav extends MozLitElement { ); } + get secondaryNavButtons() { + return this.secondaryNavGroupSlot + .assignedNodes() + .filter( + node => node?.localName === "moz-page-nav-button" && !node.hidden + ); + } + onChangeView(e) { this.currentView = e.target.view; } @@ -69,6 +79,12 @@ export default class MozPageNav extends MozLitElement { } } + onSecondaryNavChange() { + this.secondaryNavGroupSlot.assignedElements()?.forEach(el => { + el.classList.add("secondary-nav-item"); + }); + } + render() { return html`
    - +
    `; @@ -114,16 +133,18 @@ customElements.define("moz-page-nav", MozPageNav); * A navigation button intended to change the selected view within a page. * * @tagname moz-page-nav-button + * @property {string} href - (optional) The url for an external link if not a support page URL * @property {string} iconSrc - The chrome:// url for the icon used for the button. - * @property {string} l10nId - The fluent ID for the button's text * @property {boolean} selected - Whether or not the button is currently selected. + * @property {string} supportPage - (optional) The short name for the support page a secondary link should launch to * @slot [default] - Used to append the l10n string to the button. */ export class MozPageNavButton extends MozLitElement { static properties = { iconSrc: { type: String }, - l10nId: { type: String }, + href: { type: String }, selected: { type: Boolean }, + supportPage: { type: String, attribute: "support-page" }, }; connectedCallback() { @@ -133,6 +154,7 @@ export class MozPageNavButton extends MozLitElement { static queries = { buttonEl: "button", + linkEl: "a", }; get view() { @@ -148,12 +170,15 @@ export class MozPageNavButton extends MozLitElement { ); } - render() { + itemTemplate() { + if (this.href || this.supportPage) { + return this.linkTemplate(); + } + return this.buttonTemplate(); + } + + buttonTemplate() { return html` - `; } + + linkTemplate() { + if (this.supportPage) { + return html` + + ${this.innerContentTemplate()} + + `; + } + return html` + + ${this.innerContentTemplate()} + + `; + } + + innerContentTemplate() { + return html` + ${this.iconSrc + ? html`` + : ""} + + `; + } + + render() { + return html` + + ${this.itemTemplate()} + `; + } } customElements.define("moz-page-nav-button", MozPageNavButton); diff --git a/toolkit/content/widgets/moz-page-nav/moz-page-nav.stories.mjs b/toolkit/content/widgets/moz-page-nav/moz-page-nav.stories.mjs index 4ac7b455cf..d1c565efe9 100644 --- a/toolkit/content/widgets/moz-page-nav/moz-page-nav.stories.mjs +++ b/toolkit/content/widgets/moz-page-nav/moz-page-nav.stories.mjs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { html } from "../vendor/lit.all.mjs"; +import { html, when } from "../vendor/lit.all.mjs"; // eslint-disable-next-line import/no-unassigned-import import "./moz-page-nav.mjs"; @@ -21,15 +21,17 @@ moz-page-nav-button-two = View 2 .title = View 2 moz-page-nav-button-three = View 3 .title = View 3 -moz-page-link-one = Support Page - .title = Support Page +moz-page-nav-button-four = Support Link + .title = Support Link +moz-page-nav-button-five = External Link + .title = External Link moz-page-nav-heading = .heading = Heading `, }, }; -const Template = () => html` +const Template = ({ hasFooterLinks }) => html`