summaryrefslogtreecommitdiffstats
path: root/toolkit/content
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content')
-rw-r--r--toolkit/content/aboutLogging.js2
-rw-r--r--toolkit/content/aboutNetError.mjs3
-rw-r--r--toolkit/content/aboutTelemetry.js6
-rw-r--r--toolkit/content/customElements.js62
-rw-r--r--toolkit/content/jar.mn2
-rw-r--r--toolkit/content/license.html5
-rw-r--r--toolkit/content/preferencesBindings.js5
-rw-r--r--toolkit/content/tests/browser/browser_bug1693577.js4
-rw-r--r--toolkit/content/tests/chrome/chrome.toml1
-rw-r--r--toolkit/content/tests/chrome/file_editor_with_autocomplete.js4
-rw-r--r--toolkit/content/tests/chrome/test_autocomplete_with_composition_on_input.html2
-rw-r--r--toolkit/content/tests/chrome/test_editor_for_input_with_autocomplete.html2
-rw-r--r--toolkit/content/tests/chrome/window_largemenu.xhtml15
-rw-r--r--toolkit/content/tests/widgets/mochitest.toml2
-rw-r--r--toolkit/content/tests/widgets/test_moz_button.html132
-rw-r--r--toolkit/content/tests/widgets/test_moz_button_group.html144
-rw-r--r--toolkit/content/tests/widgets/test_moz_message_bar.html28
-rw-r--r--toolkit/content/tests/widgets/test_moz_page_nav.html61
-rw-r--r--toolkit/content/tests/widgets/test_videocontrols_src_change.html56
-rw-r--r--toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html2
-rw-r--r--toolkit/content/widgets.css2
-rw-r--r--toolkit/content/widgets/arrowscrollbox.js4
-rw-r--r--toolkit/content/widgets/autocomplete-richlistitem.js4
-rw-r--r--toolkit/content/widgets/datetimebox.js28
-rw-r--r--toolkit/content/widgets/message-bar.css219
-rw-r--r--toolkit/content/widgets/message-bar.js91
-rw-r--r--toolkit/content/widgets/moz-button-group/moz-button-group.css3
-rw-r--r--toolkit/content/widgets/moz-button-group/moz-button-group.mjs25
-rw-r--r--toolkit/content/widgets/moz-button/moz-button.css26
-rw-r--r--toolkit/content/widgets/moz-button/moz-button.mjs21
-rw-r--r--toolkit/content/widgets/moz-button/moz-button.stories.mjs29
-rw-r--r--toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs12
-rw-r--r--toolkit/content/widgets/moz-page-nav/README.stories.md46
-rw-r--r--toolkit/content/widgets/moz-page-nav/moz-page-nav-button.css23
-rw-r--r--toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs86
-rw-r--r--toolkit/content/widgets/moz-page-nav/moz-page-nav.stories.mjs32
-rw-r--r--toolkit/content/widgets/moz-support-link/moz-support-link.mjs6
-rw-r--r--toolkit/content/widgets/notificationbox.js3
-rw-r--r--toolkit/content/widgets/panel-list/panel-list.js4
-rw-r--r--toolkit/content/widgets/popupnotification.js2
-rw-r--r--toolkit/content/widgets/videocontrols.js17
-rw-r--r--toolkit/content/xul.css14
42 files changed, 683 insertions, 552 deletions
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
@@ -1496,6 +1496,9 @@ licences.
#ifdef MOZ_JXL
<code>third_party/highway/</code><br/>
#endif
+#ifdef MOZ_PERFETTO
+ <code>third_party/perfetto/</code><br/>
+#endif
</p>
<pre>
@@ -3600,7 +3603,6 @@ SOFTWARE.
<li><code>devtools/shared/natural-sort.js</code></li>
<li><code>devtools/shared/node-properties/node-properties.js</code></li>
<li><code>third_party/rust/ordered-float</code></li>
- <li><code>third_party/rust/owning_ref</code></li>
<li><code>third_party/rust/phf</code>,
<code>third_party/rust/phf_codegen</code>,
<code>third_party/rust/phf_generator</code>, and
@@ -3625,6 +3627,7 @@ SOFTWARE.
<li><code>third_party/rust/synstructure</code></li>
<li><code>third_party/rust/void</code></li>
<li><code>js/src/zydis</code> (unless otherwise specified)</li>
+ <li><code>js/src/vm/Float16.h</code>(the code contained in the half namespace)</li>
</ul>
See the individual LICENSE files or headers for copyright owners.</p>
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 @@
}
</style>
<script>
+ const { AddonManager } = ChromeUtils.importESModule(
+ "resource://gre/modules/AddonManager.sys.mjs"
+ );
+ // Always run tests with the light theme for consistency and to avoid from false
+ // test passes arising from the fact that --icon-color and --button-text-color
+ // have the same value in the dark theme.
+ add_setup(async function () {
+ // Developer Edition enables the wrong theme by default. Make sure
+ // the ordinary default theme is enabled.
+ let theme = await AddonManager.getAddonByID("default-theme@mozilla.org");
+ await theme.enable();
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.systemUsesDarkTheme", 0],
+ ],
+ });
+ // Triggers a refresh to ensure new theme applied.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ ok(window.matchMedia("(prefers-color-scheme: light)").matches, "Light theme is active.");
+
+ // Move mouse to the bottom of the test frame to prevent hover on buttons.
+ let mouseTrap = document.getElementById("mouse-trap");
+ await synthesizeMouseAtCenter(mouseTrap, { type: "mousemove" });
+ ok(mouseTrap.matches(":hover"), "The mouse trap is hovered");
+ });
+
function normalizeColor(val, computedStyles) {
if (val.includes("currentColor")) {
val = val.replaceAll("currentColor", computedStyles.color);
@@ -37,7 +64,7 @@
}
function assertButtonPropertiesMatch(el, propertyToCssVar) {
- let elStyles = getComputedStyle(el.buttonEl);
+ let elStyles = el.buttonEl ? getComputedStyle(el.buttonEl) : getComputedStyle(el);
for (let [property, cssVar] of Object.entries(propertyToCssVar)) {
let propertyVal = elStyles[property];
let cssVarVal = cssVar.startsWith("--") ? elStyles.getPropertyValue(cssVar) : cssVar;
@@ -52,13 +79,14 @@
add_task(async function testButtonTypes() {
let [...buttons] = document.querySelectorAll("moz-button");
- let [one, two, three, four, five, six] = buttons;
+ let [one, two, three, four, five, six, seven] = buttons;
await Promise.all(buttons.map(btn => btn.updateComplete));
is(one.textContent, "Test button", "Text is set");
is(two.buttonEl.textContent.trim(), "Test button", "Text is set");
is(three.textContent, "Test button", "Text is set");
+ is(seven.buttonEl.textContent.trim(), "Test button", "Text is set");
assertButtonPropertiesMatch(one, {
backgroundColor: "--button-background-color",
@@ -79,19 +107,24 @@
width: "--button-size-icon",
height: "--button-size-icon",
backgroundColor: "--button-background-color",
- fill: "--icon-color",
+ fill: "--button-text-color",
});
assertButtonPropertiesMatch(five, {
width: "--button-size-icon",
height: "--button-size-icon",
backgroundColor: "transparent",
- fill: "--icon-color",
+ fill: "--button-text-color",
});
assertButtonPropertiesMatch(six, {
width: "--button-size-icon",
height: "--button-size-icon",
backgroundColor: "transparent",
- fill: "--icon-color",
+ fill: "--button-text-color",
+ });
+ assertButtonPropertiesMatch(seven, {
+ backgroundColor: "--button-background-color",
+ color: "--button-text-color",
+ height: "--button-min-height",
});
buttons.forEach(btn => (btn.size = "small"));
@@ -119,6 +152,9 @@
width: "--button-size-icon-small",
height: "--button-size-icon-small",
});
+ assertButtonPropertiesMatch(seven, {
+ height: "--button-min-height-small",
+ });
});
add_task(async function testA11yAttributes() {
@@ -144,14 +180,88 @@
await testProperty("aria-label", "ariaLabel");
});
+ add_task(async function testIconButtons() {
+ let buttons = ["four", "five", "six", "seven", "eight", "nine"].map(
+ className => document.querySelector(`.${className}`)
+ );
+ let [four, five, six, seven, eight, nine] = buttons;
+ await Promise.all(buttons.map(btn => btn.updateComplete));
+
+ function verifyBackgroundIcon(button) {
+ let img = button.shadowRoot.querySelector("img");
+ ok(!img, "button does not use an img element to display an icon");
+ assertButtonPropertiesMatch(button, {
+ fill: "--button-text-color",
+ backgroundSize: "--icon-size-default",
+ });
+ }
+
+ function verifyImageIcon({ button }) {
+ let img = button.shadowRoot.querySelector("img");
+ ok(img, "button uses an inner img element to display an icon");
+ is(img.src, "chrome://global/skin/icons/edit.svg", "button displays the expected icon");
+ assertButtonPropertiesMatch(img, {
+ fill: "--button-text-color",
+ width: "--icon-size-default",
+ height: "--icon-size-default",
+ });
+ }
+
+ verifyBackgroundIcon(four);
+ verifyBackgroundIcon(five);
+ verifyBackgroundIcon(six);
+
+ verifyImageIcon({ button: seven });
+ verifyImageIcon({ button: eight });
+ verifyImageIcon({ button: nine });
+
+ nine.textContent = "With text";
+ // Ensure we've painted before checking styles
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ verifyImageIcon({ button: nine, isLabelled: true });
+
+ nine.textContent = "";
+ // Ensure we've painted before checking styles
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ verifyImageIcon({ button: nine, isLabelled: false });
+ });
</script>
</head>
<body>
- <moz-button class="one">Test button</moz-button>
- <moz-button class="two" label="Test button"></moz-button>
- <moz-button class="three" type="primary">Test button</moz-button>
- <moz-button class="four" type="icon"></moz-button>
- <moz-button class="five" type="icon ghost"></moz-button>
- <moz-button class="six" type="ghost icon"></moz-button>
+ <style>
+ .wrapper {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ gap: 64px;
+ }
+ </style>
+ <div class="wrapper">
+ <div>
+ <moz-button class="one">Test button</moz-button>
+ <moz-button class="two" label="Test button"></moz-button>
+ <moz-button class="three" type="primary">Test button</moz-button>
+ <moz-button class="four" type="icon"></moz-button>
+ <moz-button class="five" type="icon ghost"></moz-button>
+ <moz-button class="six" type="ghost icon"></moz-button>
+ <moz-button
+ class="seven"
+ label="Test button"
+ iconsrc="chrome://global/skin/icons/edit.svg"
+ ></moz-button>
+ <moz-button
+ class="eight"
+ iconsrc="chrome://global/skin/icons/edit.svg"
+ ></moz-button>
+ <!-- Used to verify that empty space doesn't get treated as a label -->
+ <moz-button
+ class="nine"
+ type="ghost"
+ iconsrc="chrome://global/skin/icons/edit.svg"
+ > </moz-button>
+ </div>
+ <button id="mouse-trap">Mouse goes here</button>
+ </div>
</body>
</html>
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 -->
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
<script type="module" src="chrome://global/content/elements/moz-button-group.mjs"></script>
+ <script type="module" src="chrome://global/content/elements/moz-button.mjs"></script>
</head>
<body>
<p id="display"></p>
@@ -20,6 +21,7 @@
</div>
<!-- This is here to ensure the stylesheet is loaded. It gets removed in setup. -->
<moz-button-group></moz-button-group>
+ <moz-button>Test</moz-button>
<pre id="test">
</pre>
@@ -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`
<moz-button-group>
<button slot="primary" id="primary-button">Primary</button>
<button id="secondary-button">Secondary</button>
</moz-button-group>
`,
- renderArea
- );
+ html`
+ <moz-button-group>
+ <moz-button slot="primary" id="primary-button">Primary</moz-button>
+ <moz-button id="secondary-button">Secondary</moz-button>
+ </moz-button-group>
+ `,
+ ];
+ 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`
<moz-button-group>
<button class="primary">Primary</button>
<button class="secondary">Secondary</button>
</moz-button-group>
`,
- 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`
+ <moz-button-group>
+ <moz-button type="primary">Primary</moz-button>
+ <moz-button type="default">Secondary</moz-button>
+ </moz-button-group>
+ `,
+ html`
+ <moz-button-group>
+ <moz-button type="destructive">Delete</moz-button>
+ <moz-button type="default">Secondary</moz-button>
+ </moz-button-group>
+ `,
+ ];
+ 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`
<moz-button-group .platform=${platform}>
<button class="primary">First</button>
<button class="secondary">Secondary</button>
<button default>Default</button>
</moz-button-group>
`,
- 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`
+ <moz-button-group .platform=${platform}>
+ <moz-button type="primary" class="primary">First</moz-button>
+ <moz-button class="secondary">Secondary</moz-button>
+ <moz-button type="primary" default>Default</moz-button>
+ </moz-button-group>
+ `,
+ ];
+
+ 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);
+ }
});
</script>
</body>
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 @@
<!DOCTYPE HTML>
<html>
+
<head>
<meta charset="utf-8">
<title>MozMessageBar tests</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
- <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="module" src="chrome://global/content/elements/moz-message-bar.mjs"></script>
</head>
+
<body>
-<p id="display"></p>
-<div id="content">
- <moz-message-bar id="infoMessage" heading="Heading" message="Test message"></moz-message-bar>
- <moz-message-bar id="infoMessage2" dismissable message="Test message"></moz-message-bar>
- <moz-message-bar id="warningMessage" type="warning" message="Test message"></moz-message-bar>
- <moz-message-bar id="successMessage" type="success" message="Test message"></moz-message-bar>
- <moz-message-bar id="errorMessage" type="error" message="Test message"></moz-message-bar>
-</div>
-<pre id="test">
+ <p id="display"></p>
+ <div id="content">
+ <moz-message-bar id="infoMessage" heading="Heading" message="Test message"></moz-message-bar>
+ <moz-message-bar id="infoMessage2" dismissable message="Test message"></moz-message-bar>
+ <moz-message-bar id="warningMessage" type="warning" message="Test message"></moz-message-bar>
+ <moz-message-bar id="successMessage" type="success" message="Test message"></moz-message-bar>
+ <moz-message-bar id="errorMessage" type="error" message="Test message"></moz-message-bar>
+ </div>
+ <pre id="test">
<script class="testbody" type="application/javascript">
add_task(async function test_component_declaration() {
const mozMessageBar = document.querySelector("#infoMessage");
ok(mozMessageBar, "moz-message-bar component is rendered.");
+ is(mozMessageBar.getAttribute("role"), "alert", "moz-message-bar component is marked up as alert.");
const icon = mozMessageBar.shadowRoot.querySelector(".icon");
const iconUrl = icon.src;
@@ -48,11 +51,11 @@
add_task(async function test_close_button() {
const notDismissableComponent = document.querySelector("#infoMessage");
- let closeButton = notDismissableComponent.closeButtonEl;
+ let closeButton = notDismissableComponent.closeButton;
ok(!closeButton, "Close button doesn't show when the message bar isn't dismissable.");
let dismissableComponent = document.querySelector("#infoMessage2");
- closeButton = dismissableComponent.closeButtonEl;
+ closeButton = dismissableComponent.closeButton;
ok(closeButton, "Close button is shown when the message bar is dismissable.");
closeButton.click();
@@ -89,4 +92,5 @@
</script>
</pre>
</body>
+
</html>
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 {
<moz-page-nav-button view="view-five" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
<span class="view-name">View 5</span>
</moz-page-nav-button>
+ <moz-page-nav-button iconSrc="chrome://mozapps/skin/extensions/category-discover.svg" support-page="test" slot="secondary-nav">
+ <span class="view-name">Support Link</span>
+ </moz-page-nav-button>
+ <moz-page-nav-button iconSrc="chrome://mozapps/skin/extensions/category-discover.svg" href="https://www.example.com" slot="secondary-nav">
+ <span class="view-name">External Link</span>
+ </moz-page-nav-button>
</moz-page-nav>
</div>
</div>
@@ -121,9 +127,45 @@ 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 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ - https://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+
+<head>
+ <title>Video controls test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="head.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+ <p id="display"></p>
+
+ <div id="content">
+ <video width="320" height="240" id="video" controls></video>
+ </div>
+
+ <div id="host"></div>
+
+ <pre id="test">
+<script class="testbody">
+ add_task(async function test_src_change() {
+ const video = document.getElementById("video");
+
+ function changeMediaSource(src) {
+ let loaded = new Promise((resolve) => video.addEventListener("loadeddata", resolve, { once: true }));
+ video.src = src;
+ return loaded;
+ }
+
+ const muteButton = getElementWithinVideo(video, "muteButton");
+
+ await changeMediaSource("video.webm");
+
+ ok(muteButton.hasAttribute("noAudio"), "noAudio attribute is present when the video has no audio track");
+ ok(muteButton.disabled, "Mute button is disabled when the video has no audio track");
+
+ await changeMediaSource("seek_with_sound.webm");
+
+ ok(!muteButton.hasAttribute("noAudio"), "noAudio attribute is not present when the video has an audio track");
+ ok(!muteButton.disabled, "Mute button is enabled when the video has an audio track");
+
+ await changeMediaSource("video.webm");
+
+ ok(muteButton.hasAttribute("noAudio"), "noAudio attribute is present when the video has no audio track")
+ ok(muteButton.disabled, "Mute button is disabled when the video has no audio track");
+ });
+
+</script>
+</pre>
+</body>
+
+</html>
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 `
<html:link rel="stylesheet" href="chrome://global/skin/toolbarbutton.css"/>
<html:link rel="stylesheet" href="chrome://global/skin/arrowscrollbox.css"/>
- <toolbarbutton id="scrollbutton-up" part="scrollbutton-up" keyNav="false" data-l10n-id="overflow-scroll-button-up"/>
+ <toolbarbutton id="scrollbutton-up" part="scrollbutton-up" keyNav="false" data-l10n-id="overflow-scroll-button-backwards"/>
<spacer part="overflow-start-indicator"/>
<box class="scrollbox-clip" part="scrollbox-clip" flex="1">
<scrollbox part="scrollbox" flex="1">
@@ -29,7 +29,7 @@
</scrollbox>
</box>
<spacer part="overflow-end-indicator"/>
- <toolbarbutton id="scrollbutton-down" part="scrollbutton-down" keyNav="false" data-l10n-id="overflow-scroll-button-down"/>
+ <toolbarbutton id="scrollbutton-down" part="scrollbutton-down" keyNav="false" data-l10n-id="overflow-scroll-button-forwards"/>
`;
}
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`
<link
@@ -86,8 +99,12 @@ export default class MozButton extends MozLitElement {
title=${ifDefined(this.title || this.tooltipText)}
aria-label=${ifDefined(this.ariaLabel)}
part="button"
+ class=${classMap({ labelled: this.label || this.hasVisibleLabel })}
>
- <slot>${this.label}</slot>
+ ${this.iconSrc
+ ? html`<img src=${this.iconSrc} role="presentation" />`
+ : ""}
+ <slot @slotchange=${this.checkForLabelText}>${this.label}</slot>
</button>
`;
}
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`
- <style>
- moz-button[type~="icon"]::part(button) {
- background-image: url("${iconUrl}");
- }
- </style>
+const Template = ({ type, size, l10nId, iconSrc, disabled }) => html`
<moz-button
data-l10n-id=${l10nId}
type=${type}
size=${size}
?disabled=${disabled}
+ iconSrc=${ifDefined(iconSrc)}
></moz-button>
`;
@@ -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
-<moz-page-nav heading="This is a nav" style={{ '--page-nav-margin-top': 0, '--page-nav-margin-bottom': 0, height: '200px' }}>
+<moz-page-nav heading="This is a nav" style={{ '--page-nav-margin-top': 0, '--page-nav-margin-bottom': 0, height: '275px' }}>
<moz-page-nav-button
view="view-one"
iconSrc="chrome://browser/skin/preferences/category-general.svg"
@@ -23,13 +23,27 @@ intended to change the selected view, provide a heading, and have links to exter
>
<p style={{ margin: 0 }}>Test 3</p>
</moz-page-nav-button>
+ <moz-page-nav-button
+ support-page="test"
+ iconSrc="chrome://browser/skin/preferences/category-general.svg"
+ slot="secondary-nav"
+ >
+ <p style={{ margin: 0 }}>Support Link</p>
+ </moz-page-nav-button>
+ <moz-page-nav-button
+ href="https://www.example.com"
+ iconSrc="chrome://browser/skin/preferences/category-general.svg"
+ slot="secondary-nav"
+ >
+ <p style={{ margin: 0 }}>External Link</p>
+ </moz-page-nav-button>
</moz-page-nav>
```
## 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
<moz-page-nav>
- <moz-page-nav-button
+ <moz-page-nav-button
view="A name for the first view"
iconSrc="A url for the icon for the first navigation button">
- </moz-page-nav-button>
- <moz-page-nav-button
+ </moz-page-nav-button>
+ <moz-page-nav-button
view="A name for the second view"
iconSrc="A url for the icon for the second navigation button">
- </moz-page-nav-button>
- <moz-page-nav-button
+ </moz-page-nav-button>
+ <moz-page-nav-button
view="A name for the third view"
iconSrc="A url for the icon for the third navigation button">
- </moz-page-nav-button>
+ </moz-page-nav-button>
+
+ <!-- Footer Links -->
+
+ <!-- Support Link -->
+ <moz-page-nav-button
+ support-page="A name for a support link"
+ iconSrc="A url for the icon for the third navigation button"
+ slot="secondary-nav">
+ </moz-page-nav-button>
+
+ <!-- External Link -->
+ <moz-page-nav-button
+ href="A url for an external link"
+ iconSrc="A url for the icon for the third navigation button"
+ slot="secondary-nav">
+ </moz-page-nav-button>
</moz-page-nav>
```
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`
<link
@@ -89,7 +105,10 @@ export default class MozPageNav extends MozLitElement {
></slot>
</div>
<div id="secondary-nav-group" role="group">
- <slot name="secondary-nav" @keydown=${this.handleFocus}></slot>
+ <slot
+ name="secondary-nav"
+ @slotchange=${this.onSecondaryNavChange}
+ ></slot>
</div>
</nav>
`;
@@ -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`
- <link
- rel="stylesheet"
- href="chrome://global/content/elements/moz-page-nav-button.css"
- />
<button
aria-selected=${this.selected}
tabindex=${this.selected ? 0 : -1}
@@ -161,10 +186,51 @@ export class MozPageNavButton extends MozLitElement {
?selected=${this.selected}
@click=${this.activate}
>
- <img class="page-nav-icon" src=${this.iconSrc} />
- <slot></slot>
+ ${this.innerContentTemplate()}
</button>
`;
}
+
+ linkTemplate() {
+ if (this.supportPage) {
+ return html`
+ <a
+ is="moz-support-link"
+ class="moz-page-nav-link"
+ support-page=${this.supportPage}
+ >
+ ${this.innerContentTemplate()}
+ </a>
+ `;
+ }
+ return html`
+ <a href=${this.href} class="moz-page-nav-link" target="_blank">
+ ${this.innerContentTemplate()}
+ </a>
+ `;
+ }
+
+ innerContentTemplate() {
+ return html`
+ ${this.iconSrc
+ ? html`<img
+ class="page-nav-icon"
+ src=${this.iconSrc}
+ role="presentation"
+ />`
+ : ""}
+ <slot></slot>
+ `;
+ }
+
+ render() {
+ return html`
+ <link
+ rel="stylesheet"
+ href="chrome://global/content/elements/moz-page-nav-button.css"
+ />
+ ${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`
<style>
#page {
height: 100%;
@@ -68,10 +70,30 @@ const Template = () => html`
iconSrc="chrome://browser/skin/preferences/category-general.svg"
>
</moz-page-nav-button>
+ ${when(
+ hasFooterLinks,
+ () => html` <moz-page-nav-button
+ support-page="test"
+ data-l10n-id="moz-page-nav-button-four"
+ iconSrc="chrome://browser/skin/preferences/category-general.svg"
+ slot="secondary-nav"
+ >
+ </moz-page-nav-button>
+ <moz-page-nav-button
+ href="https://www.example.com"
+ data-l10n-id="moz-page-nav-button-five"
+ iconSrc="chrome://browser/skin/preferences/category-general.svg"
+ slot="secondary-nav"
+ >
+ </moz-page-nav-button>`
+ )}
</moz-page-nav>
<main></main>
</div>
`;
export const Default = Template.bind({});
-Default.args = {};
+Default.args = { hasFooterLinks: false };
+
+export const WithFooterLinks = Template.bind({});
+WithFooterLinks.args = { hasFooterLinks: true };
diff --git a/toolkit/content/widgets/moz-support-link/moz-support-link.mjs b/toolkit/content/widgets/moz-support-link/moz-support-link.mjs
index 9d2d6ffac2..df6ebac2a5 100644
--- a/toolkit/content/widgets/moz-support-link/moz-support-link.mjs
+++ b/toolkit/content/widgets/moz-support-link/moz-support-link.mjs
@@ -29,6 +29,10 @@ export default class MozSupportLink extends HTMLAnchorElement {
*/
#register() {
if (window.document.nodePrincipal?.isSystemPrincipal) {
+ ChromeUtils.defineESModuleGetters(MozSupportLink, {
+ BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
+ });
+
// eslint-disable-next-line no-shadow
let { XPCOMUtils } = window.XPCOMUtils
? window
@@ -72,7 +76,7 @@ export default class MozSupportLink extends HTMLAnchorElement {
handleEvent(e) {
if (e.type == "click") {
if (window.openTrustedLinkIn) {
- let where = whereToOpenLink(e, false, true);
+ let where = MozSupportLink.BrowserUtils.whereToOpenLink(e, false, true);
if (where == "current") {
where = "tab";
}
diff --git a/toolkit/content/widgets/notificationbox.js b/toolkit/content/widgets/notificationbox.js
index 8cfc7b865c..fc3e553ca4 100644
--- a/toolkit/content/widgets/notificationbox.js
+++ b/toolkit/content/widgets/notificationbox.js
@@ -621,7 +621,7 @@
customElements.define("notification", MozElements.Notification);
async function createNotificationMessageElement() {
- await window.ensureCustomElements("moz-message-bar");
+ document.createElement("moz-message-bar");
let MozMessageBar = await customElements.whenDefined("moz-message-bar");
class NotificationMessage extends MozMessageBar {
static queries = {
@@ -772,7 +772,6 @@
let buttonElem;
if (button.hasOwnProperty("supportPage")) {
- window.ensureCustomElements("moz-support-link");
buttonElem = document.createElement("a", {
is: "moz-support-link",
});
diff --git a/toolkit/content/widgets/panel-list/panel-list.js b/toolkit/content/widgets/panel-list/panel-list.js
index 2e93b4ddc3..a2b6cb1a00 100644
--- a/toolkit/content/widgets/panel-list/panel-list.js
+++ b/toolkit/content/widgets/panel-list/panel-list.js
@@ -439,10 +439,6 @@
// using the mouse. Ignore the first focusin event if it's on the
// triggering target.
this.focusHasChanged = true;
- } else if (!target || !inPanelList) {
- // If the target isn't in the panel, hide. This will close when focus
- // moves out of the panel.
- this.hide();
} else {
// Just record that there was a focusin event.
this.focusHasChanged = true;
diff --git a/toolkit/content/widgets/popupnotification.js b/toolkit/content/widgets/popupnotification.js
index 835151496c..7dfa47728b 100644
--- a/toolkit/content/widgets/popupnotification.js
+++ b/toolkit/content/widgets/popupnotification.js
@@ -116,8 +116,6 @@
MozXULElement.insertFTLIfNeeded("toolkit/global/popupnotification.ftl");
this.appendChild(this.constructor.fragment);
- window.ensureCustomElements("moz-button-group");
-
this.button = this.querySelector(".popup-notification-primary-button");
if (
this.hasAttribute("buttonlabel") ||
diff --git a/toolkit/content/widgets/videocontrols.js b/toolkit/content/widgets/videocontrols.js
index 73a32164aa..8d7b52c344 100644
--- a/toolkit/content/widgets/videocontrols.js
+++ b/toolkit/content/widgets/videocontrols.js
@@ -417,10 +417,9 @@ this.VideoControlsImplWidget = class {
}
// We have to check again if the media has audio here.
- if (!this.isAudioOnly && !this.video.mozHasAudio) {
- this.muteButton.setAttribute("noAudio", "true");
- this.muteButton.disabled = true;
- }
+ let noAudio = !this.isAudioOnly && !this.video.mozHasAudio;
+ this.muteButton.toggleAttribute("noAudio", noAudio);
+ this.muteButton.disabled = noAudio;
}
// The video itself might not be fullscreen, but part of the
@@ -755,7 +754,7 @@ this.VideoControlsImplWidget = class {
);
}
break;
- case "loadedmetadata":
+ case "loadedmetadata": {
// If a <video> doesn't have any video data, treat it as <audio>
// and show the controls (they won't fade back out)
if (
@@ -771,13 +770,13 @@ this.VideoControlsImplWidget = class {
Math.round(this.video.currentTime * 1000),
Math.round(this.video.duration * 1000)
);
- if (!this.isAudioOnly && !this.video.mozHasAudio) {
- this.muteButton.setAttribute("noAudio", "true");
- this.muteButton.disabled = true;
- }
+ let noAudio = !this.isAudioOnly && !this.video.mozHasAudio;
+ this.muteButton.toggleAttribute("noAudio", noAudio);
+ this.muteButton.disabled = noAudio;
this.adjustControlSize();
this.updatePictureInPictureToggleDisplay();
break;
+ }
case "durationchange":
this.updatePictureInPictureToggleDisplay();
break;
diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css
index 908132d598..7f5b7aa295 100644
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -600,20 +600,6 @@ menulist[popuponly] {
padding: 0 !important;
}
-/********** splitter **********/
-
-.tree-splitter {
- margin-inline: -4px;
- width: 8px;
- max-width: 8px;
- min-width: 8px;
- appearance: none !important;
- border: none !important;
- background: none !important;
- order: 2147483646;
- z-index: 2147483646;
-}
-
/******** scrollbar ********/
slider {