summaryrefslogtreecommitdiffstats
path: root/toolkit/content
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content')
-rw-r--r--toolkit/content/aboutLogging.js6
-rw-r--r--toolkit/content/aboutNetError.mjs12
-rw-r--r--toolkit/content/aboutSupport.js2
-rw-r--r--toolkit/content/aboutSupport.xhtml4
-rw-r--r--toolkit/content/aboutwebrtc/aboutWebrtc.mjs2
-rw-r--r--toolkit/content/contentAreaUtils.js3
-rw-r--r--toolkit/content/customElements.js2
-rw-r--r--toolkit/content/jar.mn5
-rw-r--r--toolkit/content/license.html83
-rw-r--r--toolkit/content/tests/browser/browser_about_logging.js22
-rw-r--r--toolkit/content/tests/browser/browser_default_audio_filename.js2
-rw-r--r--toolkit/content/tests/browser/browser_default_image_filename.js2
-rw-r--r--toolkit/content/tests/browser/browser_default_image_filename_redirect.js2
-rw-r--r--toolkit/content/tests/browser/browser_saveImageURL.js2
-rw-r--r--toolkit/content/tests/browser/browser_save_folder_standalone_image.js2
-rw-r--r--toolkit/content/tests/browser/browser_save_resend_postdata.js2
-rw-r--r--toolkit/content/tests/browser/datetime/browser.toml5
-rw-r--r--toolkit/content/tests/browser/datetime/browser_datetime_change_event.js46
-rw-r--r--toolkit/content/tests/browser/datetime/head.js12
-rw-r--r--toolkit/content/tests/chrome/chrome.toml2
-rw-r--r--toolkit/content/tests/chrome/test_autocomplete_mac_caret.xhtml4
-rw-r--r--toolkit/content/tests/chrome/test_menulist_initial_selection.xhtml55
-rw-r--r--toolkit/content/tests/chrome/window_tooltip.xhtml6
-rw-r--r--toolkit/content/tests/mochitest/test_autocomplete_change_after_focus.html2
-rw-r--r--toolkit/content/tests/widgets/chrome.toml5
-rw-r--r--toolkit/content/tests/widgets/test_moz_button.html158
-rw-r--r--toolkit/content/tests/widgets/test_moz_page_nav.html306
-rw-r--r--toolkit/content/tests/widgets/test_videocontrols.html2
-rw-r--r--toolkit/content/widgets/arrowscrollbox.js15
-rw-r--r--toolkit/content/widgets/autocomplete-input.js10
-rw-r--r--toolkit/content/widgets/autocomplete-popup.js6
-rw-r--r--toolkit/content/widgets/autocomplete-richlistitem.js4
-rw-r--r--toolkit/content/widgets/browser-custom-element.js4
-rw-r--r--toolkit/content/widgets/datetimebox.js2
-rw-r--r--toolkit/content/widgets/dialog.js4
-rw-r--r--toolkit/content/widgets/editor.js6
-rw-r--r--toolkit/content/widgets/findbar.js12
-rw-r--r--toolkit/content/widgets/menu.js2
-rw-r--r--toolkit/content/widgets/menulist.js3
-rw-r--r--toolkit/content/widgets/menupopup.js23
-rw-r--r--toolkit/content/widgets/moz-button-group/moz-button-group.mjs2
-rw-r--r--toolkit/content/widgets/moz-button/moz-button.css142
-rw-r--r--toolkit/content/widgets/moz-button/moz-button.mjs90
-rw-r--r--toolkit/content/widgets/moz-button/moz-button.stories.mjs100
-rw-r--r--toolkit/content/widgets/moz-input-box.js2
-rw-r--r--toolkit/content/widgets/moz-label/README.stories.md4
-rw-r--r--toolkit/content/widgets/moz-label/moz-label.mjs2
-rw-r--r--toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs2
-rw-r--r--toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs6
-rw-r--r--toolkit/content/widgets/moz-page-nav/moz-page-nav-button.css123
-rw-r--r--toolkit/content/widgets/moz-page-nav/moz-page-nav.css76
-rw-r--r--toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs170
-rw-r--r--toolkit/content/widgets/moz-page-nav/moz-page-nav.stories.mjs77
-rw-r--r--toolkit/content/widgets/moz-support-link/moz-support-link.mjs2
-rw-r--r--toolkit/content/widgets/moz-toggle/moz-toggle.css39
-rw-r--r--toolkit/content/widgets/named-deck.js2
-rw-r--r--toolkit/content/widgets/notificationbox.js2
-rw-r--r--toolkit/content/widgets/panel-list/README.stories.md22
-rw-r--r--toolkit/content/widgets/panel-list/panel-list.css4
-rw-r--r--toolkit/content/widgets/panel-list/panel-list.js59
-rw-r--r--toolkit/content/widgets/panel-list/panel-list.stories.mjs45
-rw-r--r--toolkit/content/widgets/radio.js6
-rw-r--r--toolkit/content/widgets/richlistbox.js2
-rw-r--r--toolkit/content/widgets/search-textbox.js2
-rw-r--r--toolkit/content/widgets/tabbox.js8
-rw-r--r--toolkit/content/widgets/text.js2
-rw-r--r--toolkit/content/widgets/textrecognition.js2
-rw-r--r--toolkit/content/widgets/tree.js8
-rw-r--r--toolkit/content/widgets/videocontrols.js80
-rw-r--r--toolkit/content/widgets/wizard.js2
-rw-r--r--toolkit/content/xul.css89
71 files changed, 1625 insertions, 394 deletions
diff --git a/toolkit/content/aboutLogging.js b/toolkit/content/aboutLogging.js
index 5aaf0f9ecc..daff10fbec 100644
--- a/toolkit/content/aboutLogging.js
+++ b/toolkit/content/aboutLogging.js
@@ -50,7 +50,7 @@ function moduleEnvVarPresent() {
* as markers.
*
* [1]: The keys of the `presets` object defined in
- * https://searchfox.org/mozilla-central/source/devtools/client/performance-new/shared/background.jsm.js
+ * https://searchfox.org/mozilla-central/source/devtools/client/performance-new/shared/background.sys.mjs
*/
const gOsSpecificLoggingPresets = (() => {
@@ -74,7 +74,7 @@ const gOsSpecificLoggingPresets = (() => {
const gLoggingPresets = {
networking: {
modules:
- "timestamp,sync,nsHttp:5,cache2:5,nsSocketTransport:5,nsHostResolver:5",
+ "timestamp,sync,nsHttp:5,cache2:5,nsSocketTransport:5,nsHostResolver:5,EarlyHint:5",
l10nIds: {
label: "about-logging-preset-networking-label",
description: "about-logging-preset-networking-description",
@@ -228,7 +228,6 @@ function populatePresets() {
$("#log-modules").value = gLoggingPresets[dropdown.value].modules;
}
setPresetAndDescription(dropdown.value);
- setLogModules();
Services.prefs.setCharPref("logging.config.preset", dropdown.value);
};
@@ -376,7 +375,6 @@ function parseURL() {
$("#set-log-modules-button").disabled = true;
$("#logging-preset-dropdown").disabled = true;
someElementsDisabled = true;
- setLogModules();
updateLogModules();
}
if (outputTypeOverriden) {
diff --git a/toolkit/content/aboutNetError.mjs b/toolkit/content/aboutNetError.mjs
index 83f40fc479..554553fd62 100644
--- a/toolkit/content/aboutNetError.mjs
+++ b/toolkit/content/aboutNetError.mjs
@@ -430,11 +430,21 @@ function initPage() {
tryAgain.hidden = true;
break;
- // Pinning errors are of type nssFailure2
+ // TLS errors and non-overridable certificate errors (e.g. pinning
+ // failures) are of type nssFailure2.
case "nssFailure2": {
learnMore.hidden = false;
const errorCode = document.getNetErrorInfo().errorCodeString;
+ RPMRecordTelemetryEvent(
+ "security.ui.tlserror",
+ "load",
+ "abouttlserror",
+ errorCode,
+ {
+ is_frame: (window.parent != window).toString(),
+ }
+ );
switch (errorCode) {
case "SSL_ERROR_UNSUPPORTED_VERSION":
case "SSL_ERROR_PROTOCOL_VERSION_ALERT": {
diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js
index f668fd671f..f9f35e7e76 100644
--- a/toolkit/content/aboutSupport.js
+++ b/toolkit/content/aboutSupport.js
@@ -1179,7 +1179,7 @@ var snapshotFormatters = {
$.new("td", cdmInfo.keySystemName),
$.new("td", getVideoRobustness(rvArray)),
$.new("td", getAudioRobustness(rvArray)),
- $.new("td", getCapabilities(rvArray)),
+ $.new("td", getCapabilities(rvArray), null, { colspan: "4" }),
$.new("td", cdmInfo.clearlead ? "Yes" : "No"),
$.new("td", cdmInfo.isHDCP22Compatible ? "Yes" : "No"),
]);
diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml
index d3de7d0019..d19fb64d56 100644
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -567,13 +567,13 @@
<tbody id="media-content-decryption-modules-tbody">
<tr>
- <th colspan="6" class="title-column" data-l10n-id="media-content-decryption-modules-title"/>
+ <th colspan="9" class="title-column" data-l10n-id="media-content-decryption-modules-title"/>
</tr>
<tr>
<th data-l10n-id="media-key-system-name"/>
<th data-l10n-id="media-video-robustness"/>
<th data-l10n-id="media-audio-robustness"/>
- <th data-l10n-id="media-cdm-capabilities"/>
+ <th colspan="4" data-l10n-id="media-cdm-capabilities"/>
<th data-l10n-id="media-cdm-clear-lead"/>
<th data-l10n-id="media-hdcp-22-compatible"/>
</tr>
diff --git a/toolkit/content/aboutwebrtc/aboutWebrtc.mjs b/toolkit/content/aboutwebrtc/aboutWebrtc.mjs
index 3c41a4aa66..7e2c92a4bd 100644
--- a/toolkit/content/aboutwebrtc/aboutWebrtc.mjs
+++ b/toolkit/content/aboutwebrtc/aboutWebrtc.mjs
@@ -230,7 +230,7 @@ class SavePage extends Control {
]);
let FilePicker = makeFilePickerService();
const lazyFileUtils = lazy.FileUtils;
- FilePicker.init(window, dialogTitle, FilePicker.modeSave);
+ FilePicker.init(window.browsingContext, dialogTitle, FilePicker.modeSave);
FilePicker.defaultString = LOGFILE_NAME_DEFAULT;
const rv = await new Promise(r => FilePicker.open(r));
if (rv != FilePicker.returnOK && rv != FilePicker.returnReplace) {
diff --git a/toolkit/content/contentAreaUtils.js b/toolkit/content/contentAreaUtils.js
index d9ee83026e..983fd9890d 100644
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -11,7 +11,6 @@ var { XPCOMUtils } = ChromeUtils.importESModule(
ChromeUtils.defineESModuleGetters(this, {
BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs",
- Deprecated: "resource://gre/modules/Deprecated.sys.mjs",
DownloadLastDir: "resource://gre/modules/DownloadLastDir.sys.mjs",
DownloadPaths: "resource://gre/modules/DownloadPaths.sys.mjs",
Downloads: "resource://gre/modules/Downloads.sys.mjs",
@@ -689,7 +688,7 @@ function promiseTargetFile(
let fp = makeFilePicker();
let titleKey = aFpP.fpTitleKey || "SaveLinkTitle";
fp.init(
- window,
+ window.browsingContext,
ContentAreaUtils.stringBundle.GetStringFromName(titleKey),
Ci.nsIFilePicker.modeSave
);
diff --git a/toolkit/content/customElements.js b/toolkit/content/customElements.js
index 30958a3a31..b0a8f33fe6 100644
--- a/toolkit/content/customElements.js
+++ b/toolkit/content/customElements.js
@@ -818,6 +818,8 @@
// 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"
diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn
index 8b18c94525..d08037a84a 100644
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -91,10 +91,15 @@ toolkit.jar:
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)
+ content/global/elements/moz-button.mjs (widgets/moz-button/moz-button.mjs)
content/global/elements/moz-button-group.css (widgets/moz-button-group/moz-button-group.css)
content/global/elements/moz-button-group.mjs (widgets/moz-button-group/moz-button-group.mjs)
content/global/elements/moz-card.css (widgets/moz-card/moz-card.css)
content/global/elements/moz-card.mjs (widgets/moz-card/moz-card.mjs)
+ content/global/elements/moz-page-nav.css (widgets/moz-page-nav/moz-page-nav.css)
+ content/global/elements/moz-page-nav-button.css (widgets/moz-page-nav/moz-page-nav-button.css)
+ content/global/elements/moz-page-nav.mjs (widgets/moz-page-nav/moz-page-nav.mjs)
content/global/elements/moz-five-star.css (widgets/moz-five-star/moz-five-star.css)
content/global/elements/moz-five-star.mjs (widgets/moz-five-star/moz-five-star.mjs)
content/global/elements/moz-input-box.js (widgets/moz-input-box.js)
diff --git a/toolkit/content/license.html b/toolkit/content/license.html
index 9e0721906b..e9d2642354 100644
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -104,7 +104,6 @@
<li><a href="about:license#jquery">jQuery License</a></li>
<li><a href="about:license#k_exp">k_exp License</a></li>
<li><a href="about:license#khronos">Khronos group License</a></li>
- <li><a href="about:license#kiss_fft">Kiss FFT License</a></li>
#ifdef MOZ_USE_LIBCXX
<li><a href="about:license#libc++">libc++ License</a></li>
#endif
@@ -154,9 +153,6 @@
<li><a href="about:license#validator">Validator License</a></li>
<li><a href="about:license#vtune">VTune License</a></li>
<li><a href="about:license#webrtc">WebRTC License</a></li>
-#ifdef MOZ_DEFAULT_BROWSER_AGENT
- <li><a href="about:license#wintoast">WinToast License</a></li>
-#endif
<li><a href="about:license#x264">x264 License</a></li>
<li><a href="about:license#xiph">Xiph.org Foundation License</a></li>
</ul>
@@ -2041,7 +2037,6 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
<li><code>gfx/ots/</code></li>
<li><code>gfx/ycbcr/</code></li>
<li><code>ipc/chromium/</code></li>
- <li><code>media/openmax_dl/</code></li>
<li><code>toolkit/components/reputationservice/</code></li>
<li><code>toolkit/components/url-classifier/chromium/</code></li>
<li><code>tools/profiler/</code></li>
@@ -3116,80 +3111,6 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
</pre>
-
- <hr>
-
- <h1><a id="khronos"></a>Khronos group License</h1>
-
- <p>This license applies to the following files:</p>
-
- <ul>
- <li><code>media/openmax_dl/dl/api/omxtypes.h</code></li>
- <li><code>media/openmax_dl/dl/sp/api/omxSP.h</code></li>
- </ul>
-
-<pre>
-Copyright 2005-2008 The Khronos Group Inc. All Rights Reserved.
-
-These materials are protected by copyright laws and contain material
-proprietary to the Khronos Group, Inc. You may use these materials
-for implementing Khronos specifications, without altering or removing
-any trademark, copyright or other notice from the specification.
-
-Khronos Group makes no, and expressly disclaims any, representations
-or warranties, express or implied, regarding these materials, including,
-without limitation, any implied warranties of merchantability or fitness
-for a particular purpose or non-infringement of any intellectual property.
-Khronos Group makes no, and expressly disclaims any, warranties, express
-or implied, regarding the correctness, accuracy, completeness, timeliness,
-and reliability of these materials.
-
-Under no circumstances will the Khronos Group, or any of its Promoters,
-Contributors or Members or their respective partners, officers, directors,
-employees, agents or representatives be liable for any damages, whether
-direct, indirect, special or consequential damages for lost revenues,
-lost profits, or otherwise, arising from or in connection with these
-materials.
-
-Khronos and OpenMAX are trademarks of the Khronos Group Inc.
-</pre>
-
- <hr>
-
- <h1><a id="kiss_fft"></a>Kiss FFT License</h1>
-
- <p>This license applies to files in the directory
- <code>media/kiss_fft/</code>.</p>
-
-<pre>
-Copyright (c) 2003-2010 Mark Borgerding
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the author nor the names of any contributors may be used to
- endorse or promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-</pre>
-
<hr>
#ifdef MOZ_USE_LIBCXX
@@ -3695,9 +3616,6 @@ 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>
-#ifdef MOZ_DEFAULT_BROWSER_AGENT
- <li><code>third_party/WinToast</code> unless otherwise specified</li>
-#endif
</ul>
See the individual LICENSE files or headers for copyright owners.</p>
@@ -5645,6 +5563,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
they are referred to below as "Distributable Code":
<ul>
<li><var>msvc*.dll</var> (C and C++ runtime libraries)</li>
+ <li><var>vcruntime*.dll</var> (Visual C++ Runtime)</li>
</ul>
</p>
diff --git a/toolkit/content/tests/browser/browser_about_logging.js b/toolkit/content/tests/browser/browser_about_logging.js
index f458b36e0d..fdf8eab57b 100644
--- a/toolkit/content/tests/browser/browser_about_logging.js
+++ b/toolkit/content/tests/browser/browser_about_logging.js
@@ -110,15 +110,12 @@ add_task(async function testURLParameters() {
!$("#some-elements-unavailable").hidden,
"If modules are selected via URL, a warning should be displayed."
);
- var inPageSorted = $("#current-log-modules")
- .innerText.split(",")
- .sort()
- .join(",");
- var inURLSorted = modulesInURL.split(",").sort().join(",");
+ var inInputSorted = $("#log-modules").value.split(",").sort().join(",");
+ var modulesSorted = modulesInURL.split(",").sort().join(",");
Assert.equal(
- inPageSorted,
- inURLSorted,
- "When selecting modules via URL params, the same modules are reflected in the page."
+ modulesSorted,
+ inInputSorted,
+ "When selecting modules via URL params, the log modules aren't immediately set"
);
});
}
@@ -135,19 +132,16 @@ add_task(async function testURLParameters() {
!$("#some-elements-unavailable").hidden,
"If a preset is selected via URL, a warning should be displayed."
);
- var inPageSorted = $("#current-log-modules")
- .innerText.split(",")
- .sort()
- .join(",");
+ var inInputSorted = $("#log-modules").value.split(",").sort().join(",");
var presetSorted = content
.presets()
[presetInURL].modules.split(",")
.sort()
.join(",");
Assert.equal(
- inPageSorted,
+ inInputSorted,
presetSorted,
- "When selecting a preset via URL params, the correct log modules are reflected in the page."
+ "When selecting a preset via URL params, the correct log modules are reflected in the input."
);
});
}
diff --git a/toolkit/content/tests/browser/browser_default_audio_filename.js b/toolkit/content/tests/browser/browser_default_audio_filename.js
index c32dda6878..2732c0e434 100644
--- a/toolkit/content/tests/browser/browser_default_audio_filename.js
+++ b/toolkit/content/tests/browser/browser_default_audio_filename.js
@@ -2,7 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init(window);
+MockFilePicker.init(window.browsingContext);
registerCleanupFunction(function () {
MockFilePicker.cleanup();
});
diff --git a/toolkit/content/tests/browser/browser_default_image_filename.js b/toolkit/content/tests/browser/browser_default_image_filename.js
index 9add704664..0f7847020c 100644
--- a/toolkit/content/tests/browser/browser_default_image_filename.js
+++ b/toolkit/content/tests/browser/browser_default_image_filename.js
@@ -2,7 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init(window);
+MockFilePicker.init(window.browsingContext);
const DATA_IMAGE_GIF_URL =
"data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7";
registerCleanupFunction(function () {
diff --git a/toolkit/content/tests/browser/browser_default_image_filename_redirect.js b/toolkit/content/tests/browser/browser_default_image_filename_redirect.js
index a3fdd2d19e..82926b3d44 100644
--- a/toolkit/content/tests/browser/browser_default_image_filename_redirect.js
+++ b/toolkit/content/tests/browser/browser_default_image_filename_redirect.js
@@ -7,7 +7,7 @@
*/
let MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init(window);
+MockFilePicker.init(window.browsingContext);
add_task(async function () {
// This URL will redirect to doggy.png.
const URL_FIREBIRD =
diff --git a/toolkit/content/tests/browser/browser_saveImageURL.js b/toolkit/content/tests/browser/browser_saveImageURL.js
index c936b8ef84..0f7bf4b117 100644
--- a/toolkit/content/tests/browser/browser_saveImageURL.js
+++ b/toolkit/content/tests/browser/browser_saveImageURL.js
@@ -5,7 +5,7 @@ const IMAGE_PAGE =
var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init(window);
+MockFilePicker.init(window.browsingContext);
MockFilePicker.returnValue = MockFilePicker.returnCancel;
registerCleanupFunction(function () {
diff --git a/toolkit/content/tests/browser/browser_save_folder_standalone_image.js b/toolkit/content/tests/browser/browser_save_folder_standalone_image.js
index ce45d04fdc..073e71a88b 100644
--- a/toolkit/content/tests/browser/browser_save_folder_standalone_image.js
+++ b/toolkit/content/tests/browser/browser_save_folder_standalone_image.js
@@ -43,7 +43,7 @@ async function clearHistoryAndWait() {
*/
let MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init(window);
+MockFilePicker.init(window.browsingContext);
add_task(async function () {
const IMAGE_URL =
diff --git a/toolkit/content/tests/browser/browser_save_resend_postdata.js b/toolkit/content/tests/browser/browser_save_resend_postdata.js
index 5eb1b1c904..3f3e729dab 100644
--- a/toolkit/content/tests/browser/browser_save_resend_postdata.js
+++ b/toolkit/content/tests/browser/browser_save_resend_postdata.js
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init(window);
+MockFilePicker.init(window.browsingContext);
/**
* Test for bug 471962 <https://bugzilla.mozilla.org/show_bug.cgi?id=471962>:
diff --git a/toolkit/content/tests/browser/datetime/browser.toml b/toolkit/content/tests/browser/datetime/browser.toml
index 6e8580ddc4..747014f386 100644
--- a/toolkit/content/tests/browser/datetime/browser.toml
+++ b/toolkit/content/tests/browser/datetime/browser.toml
@@ -8,8 +8,9 @@ skip-if = [
"os == 'linux' && fission && socketprocess_networking && !debug", # high frequency intermittent, Bug 1673140
]
+["browser_datetime_change_event.js"]
+
["browser_datetime_datepicker.js"]
-fail-if = ["a11y_checks"] # Bug 1854538 clicked td.outside may not be accessible
# This file was skipped before new tests were written based on it in Bug 1676068
skip-if = [
"tsan", # Frequently times out on TSan
@@ -46,7 +47,6 @@ skip-if = [
]
["browser_datetime_datepicker_min_max.js"]
-fail-if = ["a11y_checks"] # Bug 1854538 clicked TD may not be accessible
skip-if = [
"tsan", # Frequently times out on TSan
"os == 'win' && asan", # fails on asan
@@ -61,7 +61,6 @@ skip-if = [
]
["browser_datetime_datepicker_mousenav.js"]
-fail-if = ["a11y_checks"] # Bug 1854538 clicked td.weekend.outside may not be accessible
skip-if = [
"tsan", # Frequently times out on TSan
"os == 'win' && asan", # fails on asan
diff --git a/toolkit/content/tests/browser/datetime/browser_datetime_change_event.js b/toolkit/content/tests/browser/datetime/browser_datetime_change_event.js
new file mode 100644
index 0000000000..920653778a
--- /dev/null
+++ b/toolkit/content/tests/browser/datetime/browser_datetime_change_event.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function open_change_and_expect_one_change_event(page) {
+ await helper.openPicker(page);
+
+ let changeEventPromise = helper.promiseChange();
+
+ // Click the first item (top-left corner) of the calendar
+ helper.click(helper.getElement(DAYS_VIEW).children[0]);
+ await changeEventPromise;
+
+ await helper.closePicker();
+
+ let changeEvents = await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ function () {
+ return content.wrappedJSObject.changeEventCount;
+ }
+ );
+ is(changeEvents, 1, "Should've got one change event");
+ await helper.tearDown();
+}
+
+add_task(async function test_change_event_simple() {
+ await open_change_and_expect_one_change_event(`data:text/html,
+ <!doctype html>
+ <script>
+ var changeEventCount = 0;
+ </script>
+ <input type="date" id="date" onchange="changeEventCount++">
+ `);
+});
+
+add_task(async function test_change_event_with_mutation() {
+ await open_change_and_expect_one_change_event(`data:text/html,
+ <!doctype html>
+ <script>
+ var changeEventCount = 0;
+ </script>
+ <input type="date" id="date" onchange="this.value = ''; changeEventCount++">
+ `);
+});
diff --git a/toolkit/content/tests/browser/datetime/head.js b/toolkit/content/tests/browser/datetime/head.js
index bbef72873c..46e2c78af5 100644
--- a/toolkit/content/tests/browser/datetime/head.js
+++ b/toolkit/content/tests/browser/datetime/head.js
@@ -113,15 +113,19 @@ class DateTimeTestHelper {
EventUtils.synthesizeMouseAtCenter(element, {}, this.frame.contentWindow);
}
- /**
- * Close the panel and the tab
- */
- async tearDown() {
+ async closePicker() {
if (this.panel.state != "closed") {
let pickerClosePromise = this.promisePickerClosed();
this.panel.hidePopup();
await pickerClosePromise;
}
+ }
+
+ /**
+ * Close the panel and the tab
+ */
+ async tearDown() {
+ await this.closePicker();
BrowserTestUtils.removeTab(this.tab);
this.tab = null;
}
diff --git a/toolkit/content/tests/chrome/chrome.toml b/toolkit/content/tests/chrome/chrome.toml
index 70fa12c4b6..3391a2923d 100644
--- a/toolkit/content/tests/chrome/chrome.toml
+++ b/toolkit/content/tests/chrome/chrome.toml
@@ -224,6 +224,8 @@ support-files = [
["test_menulist_in_popup.xhtml"]
+["test_menulist_initial_selection.xhtml"]
+
["test_menulist_keynav.xhtml"]
["test_menulist_null_value.xhtml"]
diff --git a/toolkit/content/tests/chrome/test_autocomplete_mac_caret.xhtml b/toolkit/content/tests/chrome/test_autocomplete_mac_caret.xhtml
index b49f8a1d5e..005c6ebffe 100644
--- a/toolkit/content/tests/chrome/test_autocomplete_mac_caret.xhtml
+++ b/toolkit/content/tests/chrome/test_autocomplete_mac_caret.xhtml
@@ -57,9 +57,9 @@ function checkKeyCaretTest(key, expectedStart, expectedEnd, result, testid)
keypressFired = true;
}
}
- SpecialPowers.addSystemEventListener(window, "keypress", listener, false);
+ SpecialPowers.wrap(window).addEventListener("keypress", listener, { mozSystemGroup: true });
synthesizeKey(key, {});
- SpecialPowers.removeSystemEventListener(window, "keypress", listener, false);
+ SpecialPowers.wrap(window).removeEventListener("keypress", listener, { mozSystemGroup: true });
is(keypressFired, result, `${testid} keypress event should${result ? "" : " not"} be fired`);
is(autocomplete.selectionStart, expectedStart, testid + " selectionStart");
is(autocomplete.selectionEnd, expectedEnd, testid + " selectionEnd");
diff --git a/toolkit/content/tests/chrome/test_menulist_initial_selection.xhtml b/toolkit/content/tests/chrome/test_menulist_initial_selection.xhtml
new file mode 100644
index 0000000000..19e9beae67
--- /dev/null
+++ b/toolkit/content/tests/chrome/test_menulist_initial_selection.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Menulist Initial Selection Test"
+ onload="setTimeout(runTest, 0)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+<script>
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+async function runTest() {
+ const panel = document.querySelector("panel");
+ const menulist1 = document.getElementById("menulist1");
+ const menulist2 = document.getElementById("menulist2");
+
+ const panelShown = new Promise(r => panel.addEventListener("popupshown", r, { once: true }));
+ info("opening panel");
+ panel.openPopup(null, { x: 0, y: 0 });
+ await panelShown;
+ info("panel opened");
+
+ is(menulist1.value, "1", "menulist1 should have the first menuitem's value");
+ is(menulist1.label, "One", "menulist1 should have the first menuitem's label");
+
+ is(menulist2.value, "", "menulist2 should not be selected to the first item's value");
+ is(menulist2.label, "None", "menulist2 should not be selected to the first item's value");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+
+<panel>
+ <menulist id="menulist1" value="" label="None">
+ <menupopup id="menulistpopup">
+ <menuitem value="1" label="One"/>
+ <menuitem value="2" label="Two"/>
+ <menuitem value="3" label="Three"/>
+ </menupopup>
+ </menulist>
+ <menulist id="menulist2" value="" label="None" noinitialselection="true">
+ <menupopup id="menulistpopup">
+ <menuitem value="1" label="One"/>
+ <menuitem value="2" label="Two"/>
+ <menuitem value="3" label="Three"/>
+ </menupopup>
+ </menulist>
+</panel>
+
+</window>
diff --git a/toolkit/content/tests/chrome/window_tooltip.xhtml b/toolkit/content/tests/chrome/window_tooltip.xhtml
index 6a573f0bd9..b78075de45 100644
--- a/toolkit/content/tests/chrome/window_tooltip.xhtml
+++ b/toolkit/content/tests/chrome/window_tooltip.xhtml
@@ -15,12 +15,12 @@
<box id="parent" tooltiptext="Box Tooltip" style="margin: 10px">
<button id="withtext" label="Tooltip Text" tooltiptext="Button Tooltip"
- style="-moz-appearance: none; padding: 0;"/>
- <button id="without" label="No Tooltip" style="-moz-appearance: none; padding: 0;"/>
+ style="appearance: none; padding: 0;"/>
+ <button id="without" label="No Tooltip" style="appearance: none; padding: 0;"/>
<!-- remove the native theme and borders to avoid some platform
specific sizing differences -->
<button id="withtooltip" label="Tooltip Element" tooltip="thetooltip"
- class="plain" style="-moz-appearance: none; padding: 0;"/>
+ class="plain" style="appearance: none; padding: 0;"/>
</box>
<script class="testbody" type="application/javascript">
diff --git a/toolkit/content/tests/mochitest/test_autocomplete_change_after_focus.html b/toolkit/content/tests/mochitest/test_autocomplete_change_after_focus.html
index fe4a6ee67c..271cf50a79 100644
--- a/toolkit/content/tests/mochitest/test_autocomplete_change_after_focus.html
+++ b/toolkit/content/tests/mochitest/test_autocomplete_change_after_focus.html
@@ -77,7 +77,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=998893
resolve();
}
- SpecialPowers.addSystemEventListener(field, "keypress", handleEnter, true);
+ SpecialPowers.wrap(field).addEventListener("keypress", handleEnter, { capture: true, mozSystemGroup: true });
});
field.focus();
diff --git a/toolkit/content/tests/widgets/chrome.toml b/toolkit/content/tests/widgets/chrome.toml
index af2c778947..18fe0d153a 100644
--- a/toolkit/content/tests/widgets/chrome.toml
+++ b/toolkit/content/tests/widgets/chrome.toml
@@ -22,6 +22,8 @@ skip-if = ["os == 'linux'"] # Bug 1116215
["test_menubar.xhtml"]
skip-if = ["os == 'mac'"]
+["test_moz_button.html"]
+
["test_moz_button_group.html"]
["test_moz_card.html"]
@@ -32,6 +34,8 @@ skip-if = ["os == 'mac'"]
["test_moz_message_bar.html"]
+["test_moz_page_nav.html"]
+
["test_moz_support_link.html"]
["test_moz_toggle.html"]
@@ -65,4 +69,5 @@ skip-if = [
"os == 'android'",
"os == 'linux' && debug", # Bug 1765783
]
+
["test_videocontrols_onclickplay.html"]
diff --git a/toolkit/content/tests/widgets/test_moz_button.html b/toolkit/content/tests/widgets/test_moz_button.html
new file mode 100644
index 0000000000..473b2d1a1c
--- /dev/null
+++ b/toolkit/content/tests/widgets/test_moz_button.html
@@ -0,0 +1,158 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>MozButton Tests</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="module" src="chrome://global/content/elements/moz-button.mjs"></script>
+ <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" href="chrome://global/skin/design-system/tokens-brand.css">
+ <link rel="stylesheet" href="chrome://global/skin/design-system/text-and-typography.css">
+<style>
+.four::part(button),
+.five::part(button),
+.six::part(button) {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16' fill='context-fill' fill-opacity='context-fill-opacity'%3E%3Cpath d='M3 7 1.5 7l-.5.5L1 9l.5.5 1.5 0 .5-.5 0-1.5z'/%3E%3Cpath d='m8.75 7-1.5 0-.5.5 0 1.5.5.5 1.5 0 .5-.5 0-1.5z'/%3E%3Cpath d='M14.5 7 13 7l-.5.5 0 1.5.5.5 1.5 0L15 9l0-1.5z'/%3E%3C/svg%3E");
+}
+</style>
+ <script>
+ function normalizeColor(val, computedStyles) {
+ if (val.includes("currentColor")) {
+ val = val.replaceAll("currentColor", computedStyles.color);
+ }
+ if (val.startsWith("light-dark")) {
+ let [, light, dark] = val.match(/light-dark\(([^,]+),\s*([^)]+)\)/);
+ if (light && dark) {
+ val = window.matchMedia("(prefers-color-scheme: dark)").matches ? dark : light;
+ }
+ }
+ try {
+ let { r, g, b, a } = InspectorUtils.colorToRGBA(val);
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
+ } catch (e) {
+ info(val);
+ throw e;
+ }
+ }
+
+ function assertButtonPropertiesMatch(el, propertyToCssVar) {
+ let elStyles = getComputedStyle(el.buttonEl);
+ for (let [property, cssVar] of Object.entries(propertyToCssVar)) {
+ let propertyVal = elStyles[property];
+ let cssVarVal = cssVar.startsWith("--") ? elStyles.getPropertyValue(cssVar) : cssVar;
+ if (propertyVal.startsWith("rgb") || propertyVal.startsWith("#") || propertyVal.startsWith("color")) {
+ propertyVal = normalizeColor(propertyVal, elStyles);
+ cssVarVal = normalizeColor(cssVarVal, elStyles);
+ }
+ info(`${propertyVal} == ${cssVarVal}`);
+ is(propertyVal, cssVarVal, `${property} should be ${cssVar}`);
+ }
+ }
+
+ add_task(async function testButtonTypes() {
+ let [...buttons] = document.querySelectorAll("moz-button");
+ let [one, two, three, four, five, six] = 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");
+
+ assertButtonPropertiesMatch(one, {
+ backgroundColor: "--button-background-color",
+ color: "--button-text-color",
+ height: "--button-min-height",
+ });
+ assertButtonPropertiesMatch(two, {
+ backgroundColor: "--button-background-color",
+ color: "--button-text-color",
+ height: "--button-min-height",
+ });
+ assertButtonPropertiesMatch(three, {
+ backgroundColor: "--button-background-color-primary",
+ color: "--button-text-color-primary",
+ height: "--button-min-height",
+ });
+
+ assertButtonPropertiesMatch(four, {
+ width: "--button-size-icon",
+ height: "--button-size-icon",
+ backgroundColor: "--button-background-color",
+ fill: "--button-text-color",
+ });
+ assertButtonPropertiesMatch(five, {
+ width: "--button-size-icon",
+ height: "--button-size-icon",
+ backgroundColor: "transparent",
+ fill: "--button-text-color",
+ });
+ assertButtonPropertiesMatch(six, {
+ width: "--button-size-icon",
+ height: "--button-size-icon",
+ backgroundColor: "transparent",
+ fill: "--button-text-color",
+ });
+
+ buttons.forEach(btn => (btn.size = "small"));
+
+ await Promise.all(buttons.map(btn => btn.updateComplete));
+
+ assertButtonPropertiesMatch(one, {
+ height: "--button-min-height-small",
+ });
+ assertButtonPropertiesMatch(two, {
+ height: "--button-min-height-small",
+ });
+ assertButtonPropertiesMatch(three, {
+ height: "--button-min-height-small",
+ });
+ assertButtonPropertiesMatch(four, {
+ width: "--button-size-icon-small",
+ height: "--button-size-icon-small",
+ });
+ assertButtonPropertiesMatch(five, {
+ width: "--button-size-icon-small",
+ height: "--button-size-icon-small",
+ });
+ assertButtonPropertiesMatch(six, {
+ width: "--button-size-icon-small",
+ height: "--button-size-icon-small",
+ });
+ });
+
+ add_task(async function testA11yAttributes() {
+ let button = document.querySelector("moz-button");
+
+ async function testProperty(propName, jsPropName = propName) {
+ let propValue = `${propName} value`;
+ ok(!button.buttonEl.hasAttribute(propName), `No ${propName} on inner button`);
+ button.setAttribute(propName, propValue);
+
+ await button.updateComplete;
+
+ ok(!button.hasAttribute(propName), `moz-button ${propName} cleared`);
+ is(button.buttonEl.getAttribute(propName), propValue, `${propName} added to inner button`);
+
+ button[jsPropName] = null;
+ await button.updateComplete;
+
+ ok(!button.buttonEl.hasAttribute(propName), `${propName} cleared by setting property`);
+ }
+
+ await testProperty("title");
+ await testProperty("aria-label", "ariaLabel");
+ });
+
+ </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>
+</body>
+</html>
diff --git a/toolkit/content/tests/widgets/test_moz_page_nav.html b/toolkit/content/tests/widgets/test_moz_page_nav.html
new file mode 100644
index 0000000000..604df7c024
--- /dev/null
+++ b/toolkit/content/tests/widgets/test_moz_page_nav.html
@@ -0,0 +1,306 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>MozPageNav 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://global/skin/in-content/common.css">
+ <script type="module" src="chrome://global/content/elements/moz-page-nav.mjs"></script>
+</head>
+<style>
+body {
+ display: flex;
+}
+#navigation {
+ width: var(--page-nav-width);
+}
+</style>
+<body>
+ <p id="display"></p>
+ <div id="content">
+ <div id="navigation">
+ <moz-page-nav heading="Heading">
+ <moz-page-nav-button view="view-one" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
+ <span class="view-name">View 1</span>
+ </moz-page-nav-button>
+ <moz-page-nav-button view="view-two" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
+ <span class="view-name">View 2</span>
+ </moz-page-nav-button>
+ <moz-page-nav-button view="view-three" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
+ <span class="view-name">View 3</span>
+ </moz-page-nav-button>
+ <moz-page-nav-button view="view-four" iconSrc="chrome://mozapps/skin/extensions/category-discover.svg">
+ <span class="view-name">View 4</span>
+ </moz-page-nav-button>
+ <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>
+ </div>
+ </div>
+<pre id="test"></pre>
+<script>
+ Services.scriptloader.loadSubScript(
+ "chrome://browser/content/utilityOverlay.js",
+ this
+ );
+ const { BrowserTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+ );
+
+const mozPageNav = document.querySelector("moz-page-nav");
+
+function isActiveElement(expectedActiveEl) {
+ return expectedActiveEl.getRootNode().activeElement == expectedActiveEl;
+ }
+
+ /**
+ * Tests that the first page nav button is selected by default
+ */
+ add_task(async function test_first_item_selected_by_default() {
+ is(
+ mozPageNav.pageNavButtons.length,
+ 5,
+ "Five page nav buttons are in the navigation"
+ );
+
+ ok(
+ mozPageNav.pageNavButtons[0].view === mozPageNav.currentView,
+ "The first page nav button is selected by default"
+ )
+ });
+
+ /**
+ * Tests that views are selected when clicked
+ */
+ add_task(async function test_select_view() {
+ let gBrowser = BrowserWindowTracker.getTopWindow().top.gBrowser;
+ let secondViewButton = mozPageNav.pageNavButtons[1];
+ let viewChanged = BrowserTestUtils.waitForEvent(
+ gBrowser,
+ "change-view"
+ );
+
+ secondViewButton.buttonEl.click();
+ await viewChanged;
+
+ ok(
+ secondViewButton.view === mozPageNav.currentView,
+ "The second page nav button is selected"
+ )
+
+ let thirdPageNavButton = mozPageNav.pageNavButtons[2];
+ viewChanged = BrowserTestUtils.waitForEvent(
+ gBrowser,
+ "change-view"
+ );
+
+ thirdPageNavButton.buttonEl.click();
+ await viewChanged;
+
+ ok(
+ thirdPageNavButton.view === mozPageNav.currentView,
+ "The third page nav button is selected"
+ )
+
+ let firstPageNavButton = mozPageNav.pageNavButtons[0];
+ viewChanged = BrowserTestUtils.waitForEvent(
+ gBrowser,
+ "change-view"
+ );
+
+ firstPageNavButton.buttonEl.click();
+ await viewChanged;
+
+ ok(
+ firstPageNavButton.view === mozPageNav.currentView,
+ "The first page nav button is selected"
+ )
+ });
+
+ /**
+ * Tests that categories are keyboard-navigable
+ */
+ add_task(async function test_keyboard_navigation() {
+ const arrowDown = async () => {
+ info("Arrow down");
+ synthesizeKey("KEY_ArrowDown", {});
+ await mozPageNav.updateComplete;
+ };
+ const arrowUp = async () => {
+ info("Arrow up");
+ synthesizeKey("KEY_ArrowUp", {});
+ await mozPageNav.updateComplete;
+ };
+ const arrowLeft = async () => {
+ info("Arrow left");
+ synthesizeKey("KEY_ArrowLeft", {});
+ await mozPageNav.updateComplete;
+ };
+ const arrowRight = async () => {
+ info("Arrow right");
+ synthesizeKey("KEY_ArrowRight", {});
+ await mozPageNav.updateComplete;
+ };
+
+ // Setting this pref allows the test to run as expected with a keyboard on MacOS
+ await SpecialPowers.pushPrefEnv({
+ set: [["accessibility.tabfocus", 7]],
+ });
+
+ let firstPageNavButton = mozPageNav.pageNavButtons[0];
+ let secondPageNavButton = mozPageNav.pageNavButtons[1];
+ let thirdPageNavButton = mozPageNav.pageNavButtons[2];
+ let fourthPageNavButton = mozPageNav.pageNavButtons[3];
+ let fifthPageNavButton = mozPageNav.pageNavButtons[4];
+
+ is(
+ firstPageNavButton.view,
+ mozPageNav.currentView,
+ "The first page nav button is selected"
+ )
+ firstPageNavButton.buttonEl.focus();
+ await arrowDown();
+ ok(
+ isActiveElement(secondPageNavButton),
+ "The second page nav button is the active element after first arrow down"
+ );
+ is(
+ secondPageNavButton.view,
+ mozPageNav.currentView,
+ "The second page nav button is selected"
+ )
+ await arrowDown();
+ is(
+ thirdPageNavButton.view,
+ mozPageNav.currentView,
+ "The third page nav button is selected"
+ )
+ await arrowDown();
+ is(
+ fourthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fourth page nav button is selected"
+ )
+ await arrowDown();
+ is(
+ fifthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fifth page nav button is selected"
+ )
+ await arrowDown();
+ is(
+ fifthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fifth page nav button is still selected"
+ )
+ await arrowUp();
+ is(
+ fourthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fourth page nav button is selected"
+ )
+ await arrowUp();
+ is(
+ thirdPageNavButton.view,
+ mozPageNav.currentView,
+ "The third page nav button is selected"
+ )
+ await arrowUp();
+ is(
+ secondPageNavButton.view,
+ mozPageNav.currentView,
+ "The second page nav button is selected"
+ )
+ await arrowUp();
+ is(
+ firstPageNavButton.view,
+ mozPageNav.currentView,
+ "The first page nav button is selected"
+ )
+ await arrowUp();
+ is(
+ firstPageNavButton.view,
+ mozPageNav.currentView,
+ "The first page nav button is still selected"
+ )
+
+ // Test navigation with arrow left/right keys
+ is(
+ firstPageNavButton.view,
+ mozPageNav.currentView,
+ "The first page nav button is selected"
+ )
+ firstPageNavButton.buttonEl.focus();
+ await arrowRight();
+ ok(
+ isActiveElement(secondPageNavButton),
+ "The second page nav button is the active element after first arrow right"
+ );
+ is(
+ secondPageNavButton.view,
+ mozPageNav.currentView,
+ "The second page nav button is selected"
+ )
+ await arrowRight();
+ is(
+ thirdPageNavButton.view,
+ mozPageNav.currentView,
+ "The third page nav button is selected"
+ )
+ await arrowRight();
+ is(
+ fourthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fourth page nav button is selected"
+ )
+ await arrowRight();
+ is(
+ fifthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fifth page nav button is selected"
+ )
+ await arrowRight();
+ is(
+ fifthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fifth page nav button is still selected"
+ )
+ await arrowLeft();
+ is(
+ fourthPageNavButton.view,
+ mozPageNav.currentView,
+ "The fourth page nav button is selected"
+ )
+ await arrowLeft();
+ is(
+ thirdPageNavButton.view,
+ mozPageNav.currentView,
+ "The third page nav button is selected"
+ )
+ await arrowLeft();
+ is(
+ secondPageNavButton.view,
+ mozPageNav.currentView,
+ "The second page nav button is selected"
+ )
+ await arrowLeft();
+ is(
+ firstPageNavButton.view,
+ mozPageNav.currentView,
+ "The first page nav button is selected"
+ )
+ await arrowLeft();
+ is(
+ firstPageNavButton.view,
+ mozPageNav.currentView,
+ "The first page nav button is still selected"
+ )
+
+ await SpecialPowers.popPrefEnv();
+ });
+</script>
+</body>
+</html>
diff --git a/toolkit/content/tests/widgets/test_videocontrols.html b/toolkit/content/tests/widgets/test_videocontrols.html
index 32cd23df6a..076b4350fd 100644
--- a/toolkit/content/tests/widgets/test_videocontrols.html
+++ b/toolkit/content/tests/widgets/test_videocontrols.html
@@ -67,7 +67,7 @@ let expectingEventPromise;
async function isMuteButtonMuted() {
const muteButton = getElementWithinVideo(video, "muteButton");
await new Promise(SimpleTest.executeSoon);
- return muteButton.getAttribute("muted") === "true";
+ return muteButton.hasAttribute("muted");
}
async function isVolumeSliderShowingCorrectVolume(expectedVolume) {
diff --git a/toolkit/content/widgets/arrowscrollbox.js b/toolkit/content/widgets/arrowscrollbox.js
index 28de96c8d7..7109891faf 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"/>
+ <toolbarbutton id="scrollbutton-up" part="scrollbutton-up" keyNav="false" data-l10n-id="overflow-scroll-button-up"/>
<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"/>
+ <toolbarbutton id="scrollbutton-down" part="scrollbutton-down" keyNav="false" data-l10n-id="overflow-scroll-button-down"/>
`;
}
@@ -43,6 +43,8 @@
this._scrollButtonDown =
this.shadowRoot.getElementById("scrollbutton-down");
+ MozXULElement.insertFTLIfNeeded("toolkit/global/arrowscrollbox.ftl");
+
this._arrowScrollAnim = {
scrollbox: this,
requestHandle: 0,
@@ -134,6 +136,8 @@
}
this.hasConnected = true;
+ document.l10n.connectRoot(this.shadowRoot);
+
if (!this.hasAttribute("smoothscroll")) {
this.smoothScroll = Services.prefs.getBoolPref(
"toolkit.scrollbox.smoothScroll",
@@ -639,6 +643,7 @@
this._scrollTimer.cancel();
this._scrollTimer = null;
}
+ document.l10n.disconnectRoot(this.shadowRoot);
}
on_wheel(event) {
@@ -749,7 +754,7 @@
}
}
- on_touchend(event) {
+ on_touchend() {
this._touchStart = -1;
}
@@ -804,12 +809,12 @@
this._updateScrollButtonsDisabledState();
}
- on_scroll(event) {
+ on_scroll() {
this._isScrolling = true;
this._updateScrollButtonsDisabledState();
}
- on_scrollend(event) {
+ on_scrollend() {
this._isScrolling = false;
this._destination = 0;
this._direction = 0;
diff --git a/toolkit/content/widgets/autocomplete-input.js b/toolkit/content/widgets/autocomplete-input.js
index 36105ba4d7..a6fb4b5067 100644
--- a/toolkit/content/widgets/autocomplete-input.js
+++ b/toolkit/content/widgets/autocomplete-input.js
@@ -40,7 +40,7 @@
this.addEventListener(
"compositionstart",
- event => {
+ () => {
if (
this.mController.input.wrappedJSObject == this.nsIAutocompleteInput
) {
@@ -52,7 +52,7 @@
this.addEventListener(
"compositionend",
- event => {
+ () => {
if (
this.mController.input.wrappedJSObject == this.nsIAutocompleteInput
) {
@@ -64,7 +64,7 @@
this.addEventListener(
"focus",
- event => {
+ () => {
this.attachController();
if (
window.gBrowser &&
@@ -82,7 +82,7 @@
this.addEventListener(
"blur",
- event => {
+ () => {
if (!this._dontBlur) {
if (this.forceComplete && this.mController.matchCount >= 1) {
// If forceComplete is requested, we need to call the enter processing
@@ -625,7 +625,7 @@
return value;
}
- onInput(aEvent) {
+ onInput() {
if (
!this.mIgnoreInput &&
this.mController.input.wrappedJSObject == this.nsIAutocompleteInput
diff --git a/toolkit/content/widgets/autocomplete-popup.js b/toolkit/content/widgets/autocomplete-popup.js
index f033511e07..a13ba1bc62 100644
--- a/toolkit/content/widgets/autocomplete-popup.js
+++ b/toolkit/content/widgets/autocomplete-popup.js
@@ -572,7 +572,7 @@
}
setListeners() {
- this.addEventListener("popupshowing", event => {
+ this.addEventListener("popupshowing", () => {
// If normalMaxRows wasn't already set by the input, then set it here
// so that we restore the correct number when the popup is hidden.
@@ -584,14 +584,14 @@
this.mPopupOpen = true;
});
- this.addEventListener("popupshown", event => {
+ this.addEventListener("popupshown", () => {
if (this._adjustHeightOnPopupShown) {
this._adjustHeightOnPopupShown = false;
this.adjustHeight();
}
});
- this.addEventListener("popuphiding", event => {
+ this.addEventListener("popuphiding", () => {
var isListActive = true;
if (this.selectedIndex == -1) {
isListActive = false;
diff --git a/toolkit/content/widgets/autocomplete-richlistitem.js b/toolkit/content/widgets/autocomplete-richlistitem.js
index ccbd37e132..fddd5b4029 100644
--- a/toolkit/content/widgets/autocomplete-richlistitem.js
+++ b/toolkit/content/widgets/autocomplete-richlistitem.js
@@ -21,7 +21,7 @@
* This overrides listitem's mousedown handler because we want to set the
* selected item even when the shift or accel keys are pressed.
*/
- this.addEventListener("mousedown", event => {
+ this.addEventListener("mousedown", () => {
// Call this.control only once since it's not a simple getter.
let control = this.control;
if (!control || control.disabled) {
@@ -587,7 +587,7 @@
/**
* Override _getSearchTokens to have the Learn More text emphasized
*/
- _getSearchTokens(aSearch) {
+ _getSearchTokens() {
return [this._learnMoreString.toLowerCase()];
}
}
diff --git a/toolkit/content/widgets/browser-custom-element.js b/toolkit/content/widgets/browser-custom-element.js
index e9b29034fa..887f59c742 100644
--- a/toolkit/content/widgets/browser-custom-element.js
+++ b/toolkit/content/widgets/browser-custom-element.js
@@ -872,7 +872,7 @@
this.webProgress.removeProgressListener(aListener);
}
- onPageHide(aEvent) {
+ onPageHide() {
// If we're browsing from the tab crashed UI to a URI that keeps
// this browser non-remote, we'll handle that here.
lazy.SessionStore?.maybeExitCrashedState(this);
@@ -1919,7 +1919,7 @@
// Called immediately after changing remoteness. If this method returns
// `true`, Gecko will assume frontend handled resuming the load, and will
// not attempt to resume the load itself.
- afterChangeRemoteness(browser, redirectLoadSwitchId) {
+ afterChangeRemoteness() {
/* no-op unless replaced */
return false;
}
diff --git a/toolkit/content/widgets/datetimebox.js b/toolkit/content/widgets/datetimebox.js
index 28b32fddfa..04ed398bd7 100644
--- a/toolkit/content/widgets/datetimebox.js
+++ b/toolkit/content/widgets/datetimebox.js
@@ -5,7 +5,7 @@
"use strict";
// This is a UA widget. It runs in per-origin UA widget scope,
-// to be loaded by UAWidgetsChild.jsm.
+// to be loaded by UAWidgetsChild.sys.mjs.
/*
* This is the class of entry. It will construct the actual implementation
diff --git a/toolkit/content/widgets/dialog.js b/toolkit/content/widgets/dialog.js
index 52eb2168f8..c4b25c2f48 100644
--- a/toolkit/content/widgets/dialog.js
+++ b/toolkit/content/widgets/dialog.js
@@ -146,7 +146,7 @@
if (document.readyState == "complete") {
this._postLoadInit();
} else {
- window.addEventListener("load", event => this._postLoadInit());
+ window.addEventListener("load", () => this._postLoadInit());
}
}
@@ -521,7 +521,7 @@
}
var btn = this.getButton(this.defaultButton);
- if (btn) {
+ if (btn && !btn.hidden) {
this._doButtonCommand(this.defaultButton);
}
}
diff --git a/toolkit/content/widgets/editor.js b/toolkit/content/widgets/editor.js
index 9e5ffb542e..8e014f77af 100644
--- a/toolkit/content/widgets/editor.js
+++ b/toolkit/content/widgets/editor.js
@@ -16,13 +16,13 @@
"nsIURIContentListener",
"nsISupportsWeakReference",
]),
- doContent(contentType, isContentPreferred, request, contentHandler) {
+ doContent() {
return false;
},
- isPreferred(contentType, desiredContentType) {
+ isPreferred() {
return false;
},
- canHandleContent(contentType, isContentPreferred, desiredContentType) {
+ canHandleContent() {
return false;
},
loadCookie: null,
diff --git a/toolkit/content/widgets/findbar.js b/toolkit/content/widgets/findbar.js
index 3cbce11771..cdfbd315a7 100644
--- a/toolkit/content/widgets/findbar.js
+++ b/toolkit/content/widgets/findbar.js
@@ -164,7 +164,7 @@
window.addEventListener("unload", this.destroy);
- this._findField.addEventListener("input", event => {
+ this._findField.addEventListener("input", () => {
// We should do nothing during composition. E.g., composing string
// before converting may matches a forward word of expected word.
// After that, even if user converts the composition string to the
@@ -230,17 +230,17 @@
}
});
- this._findField.addEventListener("blur", event => {
+ this._findField.addEventListener("blur", () => {
// Note: This code used to remove the selection
// if it matched an editable.
this.browser.finder.enableSelection();
});
- this._findField.addEventListener("focus", event => {
+ this._findField.addEventListener("focus", () => {
this._updateBrowserWithState();
});
- this._findField.addEventListener("compositionstart", event => {
+ this._findField.addEventListener("compositionstart", () => {
// Don't close the find toolbar while IME is composing.
let findbar = this;
findbar._isIMEComposing = true;
@@ -251,7 +251,7 @@
}
});
- this._findField.addEventListener("compositionend", event => {
+ this._findField.addEventListener("compositionend", () => {
this._isIMEComposing = false;
if (this.findMode != this.FIND_NORMAL) {
this._setFindCloseTimeout();
@@ -1307,7 +1307,7 @@
}
}
- onHighlightFinished(result) {
+ onHighlightFinished() {
// Noop.
}
diff --git a/toolkit/content/widgets/menu.js b/toolkit/content/widgets/menu.js
index f787747a01..1a55d799b6 100644
--- a/toolkit/content/widgets/menu.js
+++ b/toolkit/content/widgets/menu.js
@@ -129,7 +129,7 @@
};
// The <menucaption> element is used for rendering <html:optgroup> inside of <html:select>,
- // See SelectParentHelper.jsm.
+ // See SelectParentHelper.sys.mjs.
class MozMenuCaption extends MozMenuBaseMixin(MozXULElement) {
static get inheritedAttributes() {
return {
diff --git a/toolkit/content/widgets/menulist.js b/toolkit/content/widgets/menulist.js
index 4e66c030f3..3672d4ccf1 100644
--- a/toolkit/content/widgets/menulist.js
+++ b/toolkit/content/widgets/menulist.js
@@ -289,6 +289,9 @@
}
setInitialSelection() {
+ if (this.getAttribute("noinitialselection") === "true") {
+ return;
+ }
var popup = this.menupopup;
if (popup) {
var arr = popup.getElementsByAttribute("selected", "true");
diff --git a/toolkit/content/widgets/menupopup.js b/toolkit/content/widgets/menupopup.js
index 31801d6a33..c7448d4f05 100644
--- a/toolkit/content/widgets/menupopup.js
+++ b/toolkit/content/widgets/menupopup.js
@@ -11,16 +11,12 @@
"resource://gre/modules/AppConstants.sys.mjs"
);
- // For the non-native context menu styling, we need to know if we need
- // a gutter for checkboxes. To do this, check whether there are any
- // radio/checkbox type menuitems in a menupopup when showing it. We use a
- // system bubbling event listener to ensure we run *after* the "normal"
- // popupshowing listeners, so (visibility) changes they make to their items
- // take effect first, before we check for checkable menuitems.
- Services.els.addSystemEventListener(
- document,
+ document.addEventListener(
"popupshowing",
function (e) {
+ // For the non-native context menu styling, we need to know if we need
+ // a gutter for checkboxes. To do this, check whether there are any
+ // radio/checkbox type menuitems in a menupopup when showing it.
if (e.target.nodeName == "menupopup") {
let haveCheckableChild = e.target.querySelector(
`:scope > menuitem:not([hidden]):is([type=checkbox],[type=radio]${
@@ -33,7 +29,10 @@
e.target.toggleAttribute("needsgutter", haveCheckableChild);
}
},
- false
+ // we use a system bubbling event listener to ensure we run *after* the
+ // "normal" popupshowing listeners, so (visibility) changes they make to
+ // their items take effect first, before we check for checkable menuitems.
+ { mozSystemGroup: true }
);
class MozMenuPopup extends MozElements.MozElementMixin(XULPopupElement) {
@@ -74,13 +73,13 @@
initShadowDOM() {
// Retarget events from shadow DOM arrowscrollbox to the host.
- this.scrollBox.addEventListener("scroll", ev =>
+ this.scrollBox.addEventListener("scroll", () =>
this.dispatchEvent(new Event("scroll"))
);
- this.scrollBox.addEventListener("overflow", ev =>
+ this.scrollBox.addEventListener("overflow", () =>
this.dispatchEvent(new Event("overflow"))
);
- this.scrollBox.addEventListener("underflow", ev =>
+ this.scrollBox.addEventListener("underflow", () =>
this.dispatchEvent(new Event("underflow"))
);
}
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 0706f94762..8bf553c23d 100644
--- a/toolkit/content/widgets/moz-button-group/moz-button-group.mjs
+++ b/toolkit/content/widgets/moz-button-group/moz-button-group.mjs
@@ -44,7 +44,7 @@ export default class MozButtonGroup extends MozLitElement {
}
}
- onSlotchange(e) {
+ onSlotchange() {
for (let child of this.defaultSlotEl.assignedNodes()) {
if (!(child instanceof Element)) {
// Text nodes won't support classList or getAttribute.
diff --git a/toolkit/content/widgets/moz-button/moz-button.css b/toolkit/content/widgets/moz-button/moz-button.css
new file mode 100644
index 0000000000..47567df41d
--- /dev/null
+++ b/toolkit/content/widgets/moz-button/moz-button.css
@@ -0,0 +1,142 @@
+/* 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 {
+ display: inline-block;
+}
+
+button {
+ appearance: none;
+ min-height: var(--button-min-height);
+ color: var(--button-text-color);
+ border: var(--button-border);
+ border-radius: var(--button-border-radius);
+ background-color: var(--button-background-color);
+ padding: var(--button-padding);
+ /* HTML button gets `font: -moz-button` from UA styles,
+ * but we want it to match the root font styling. */
+ font: inherit;
+ font-weight: var(--button-font-weight);
+ /* Ensure font-size isn't overridden by widget styling (e.g. in forms.css) */
+ font-size: var(--button-font-size);
+ width: 100%;
+
+ &[size=small] {
+ min-height: var(--button-min-height-small);
+ font-size: var(--button-font-size-small);
+ }
+
+ &:hover {
+ background-color: var(--button-background-color-hover);
+ border-color: var(--button-border-color-hover);
+ color: var(--button-text-color-hover);
+ }
+
+ &:hover:active {
+ background-color: var(--button-background-color-active);
+ border-color: var(--button-border-color-active);
+ color: var(--button-text-color-active);
+ }
+
+ &:disabled {
+ background-color: var(--button-background-color-disabled);
+ border-color: var(--button-border-color-disabled);
+ color: var(--button-text-color-disabled);
+ opacity: var(--button-opacity-disabled);
+ }
+
+ &:focus-visible {
+ outline: var(--focus-outline);
+ outline-offset: var(--focus-outline-offset);
+ }
+
+ &[type="primary"] {
+ background-color: var(--button-background-color-primary);
+ border-color: var(--button-border-color-primary);
+ color: var(--button-text-color-primary);
+
+ &:hover {
+ background-color: var(--button-background-color-primary-hover);
+ border-color: var(--button-border-color-primary-hover);
+ color: var(--button-text-color-primary-hover);
+ }
+
+ &:hover:active {
+ background-color: var(--button-background-color-primary-active);
+ border-color: var(--button-border-color-primary-active);
+ color: var(--button-text-color-primary-active);
+ }
+
+ &:disabled {
+ background-color: var(--button-background-color-primary-disabled);
+ border-color: var(--button-border-color-primary-disabled);
+ color: var(--button-text-color-primary-disabled);
+ }
+ }
+
+ &[type="destructive"] {
+ background-color: var(--button-background-color-destructive);
+ border-color: var(--button-border-color-destructive);
+ color: var(--button-text-color-destructive);
+
+ &:hover {
+ background-color: var(--button-background-color-destructive-hover);
+ border-color: var(--button-border-color-destructive-hover);
+ color: var(--button-text-color-destructive-hover);
+ }
+
+ &:hover:active {
+ background-color: var(--button-background-color-destructive-active);
+ border-color: var(--button-border-color-destructive-active);
+ color: var(--button-text-color-destructive-active);
+ }
+
+ &:disabled {
+ background-color: var(--button-background-color-destructive-disabled);
+ border-color: var(--button-border-color-destructive-disabled);
+ color: var(--button-text-color-destructive-disabled);
+ }
+ }
+
+ &[type~=ghost] {
+ background-color: var(--button-background-color-ghost);
+ border-color: var(--button-border-color-ghost);
+ color: var(--button-text-color-ghost);
+
+ &:hover {
+ background-color: var(--button-background-color-ghost-hover);
+ border-color: var(--button-border-color-ghost-hover);
+ color: var(--button-text-color-ghost-hover);
+ }
+
+ &:hover:active {
+ background-color: var(--button-background-color-ghost-active);
+ border-color: var(--button-border-color-ghost-active);
+ color: var(--button-text-color-ghost-active);
+ }
+
+ &:disabled {
+ background-color: var(--button-background-color-ghost-disabled);
+ border-color: var(--button-border-color-ghost-disabled);
+ color: var(--button-text-color-ghost-disabled);
+ }
+ }
+
+ &[type~=icon] {
+ background-size: var(--icon-size-default);
+ background-position: center;
+ background-repeat: no-repeat;
+ -moz-context-properties: fill, stroke;
+ fill: currentColor;
+ stroke: currentColor;
+ width: var(--button-size-icon);
+ height: var(--button-size-icon);
+ padding: var(--button-padding-icon);
+
+ &[size=small] {
+ width: var(--button-size-icon-small);
+ height: var(--button-size-icon-small);
+ }
+ }
+}
diff --git a/toolkit/content/widgets/moz-button/moz-button.mjs b/toolkit/content/widgets/moz-button/moz-button.mjs
new file mode 100644
index 0000000000..3e7c151e61
--- /dev/null
+++ b/toolkit/content/widgets/moz-button/moz-button.mjs
@@ -0,0 +1,90 @@
+/* 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/. */
+
+import { html, ifDefined } from "../vendor/lit.all.mjs";
+import { MozLitElement } from "../lit-utils.mjs";
+
+/**
+ * A button with multiple types and two sizes.
+ *
+ * @tagname moz-button
+ * @property {string} label - The button's label, will be overridden by slotted content.
+ * @property {string} type - The button type.
+ * Options: default, primary, destructive, icon, icon ghost, ghost.
+ * @property {string} size - The button size.
+ * Options: default, small.
+ * @property {boolean} disabled - The disabled state.
+ * @property {string} title - The button's title attribute, used in shadow DOM and therefore not as an attribute on moz-button.
+ * @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} ariaLabelAttribute - Internal, map aria-label attribute to the ariaLabel JS property.
+ * @property {HTMLButtonElement} buttonEl - The internal button element in the shadow DOM.
+ * @slot default - The button's content, overrides label property.
+ * @fires click - The click event.
+ */
+export default class MozButton extends MozLitElement {
+ static shadowRootOptions = {
+ ...MozLitElement.shadowRootOptions,
+ delegatesFocus: true,
+ };
+
+ static properties = {
+ label: { type: String, reflect: true },
+ type: { type: String, reflect: true },
+ size: { type: String, reflect: true },
+ disabled: { type: Boolean, reflect: true },
+ title: { type: String, state: true },
+ titleAttribute: { type: String, attribute: "title", reflect: true },
+ tooltipText: { type: String },
+ ariaLabelAttribute: {
+ type: String,
+ attribute: "aria-label",
+ reflect: true,
+ },
+ ariaLabel: { type: String, state: true },
+ };
+
+ static queries = {
+ buttonEl: "button",
+ };
+
+ constructor() {
+ super();
+ this.type = "default";
+ this.size = "default";
+ this.disabled = false;
+ }
+
+ willUpdate(changes) {
+ if (changes.has("titleAttribute")) {
+ this.title = this.titleAttribute;
+ this.titleAttribute = null;
+ }
+ if (changes.has("ariaLabelAttribute")) {
+ this.ariaLabel = this.ariaLabelAttribute;
+ this.ariaLabelAttribute = null;
+ }
+ }
+
+ render() {
+ return html`
+ <link
+ rel="stylesheet"
+ href="chrome://global/content/elements/moz-button.css"
+ />
+ <button
+ type=${this.type}
+ size=${this.size}
+ ?disabled=${this.disabled}
+ title=${ifDefined(this.title || this.tooltipText)}
+ aria-label=${ifDefined(this.ariaLabel)}
+ part="button"
+ >
+ <slot>${this.label}</slot>
+ </button>
+ `;
+ }
+}
+customElements.define("moz-button", MozButton);
diff --git a/toolkit/content/widgets/moz-button/moz-button.stories.mjs b/toolkit/content/widgets/moz-button/moz-button.stories.mjs
new file mode 100644
index 0000000000..52a459e807
--- /dev/null
+++ b/toolkit/content/widgets/moz-button/moz-button.stories.mjs
@@ -0,0 +1,100 @@
+/* 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/. */
+
+import { html } from "../vendor/lit.all.mjs";
+// eslint-disable-next-line import/no-unassigned-import
+import "./moz-button.mjs";
+
+export default {
+ title: "UI Widgets/Moz Button",
+ component: "moz-button",
+ argTypes: {
+ l10nId: {
+ options: [
+ "moz-button-labelled",
+ "moz-button-titled",
+ "moz-button-aria-labelled",
+ ],
+ control: { type: "select" },
+ },
+ size: {
+ options: ["default", "small"],
+ control: { type: "radio" },
+ },
+ },
+ parameters: {
+ actions: {
+ handles: ["click"],
+ },
+ status: "in-development",
+ fluent: `
+moz-button-labelled = Button
+moz-button-primary = Primary
+moz-button-destructive = Destructive
+moz-button-titled =
+ .title = View logins
+moz-button-aria-labelled =
+ .aria-label = View logins
+`,
+ },
+};
+
+const Template = ({ type, size, l10nId, iconUrl, disabled }) => html`
+ <style>
+ moz-button[type~="icon"]::part(button) {
+ background-image: url("${iconUrl}");
+ }
+ </style>
+ <moz-button
+ data-l10n-id=${l10nId}
+ type=${type}
+ size=${size}
+ ?disabled=${disabled}
+ ></moz-button>
+`;
+
+export const Default = Template.bind({});
+Default.args = {
+ type: "default",
+ size: "default",
+ l10nId: "moz-button-labelled",
+ iconUrl: "chrome://global/skin/icons/more.svg",
+ disabled: false,
+};
+export const DefaultSmall = Template.bind({});
+DefaultSmall.args = {
+ type: "default",
+ size: "small",
+ l10nId: "moz-button-labelled",
+ iconUrl: "chrome://global/skin/icons/more.svg",
+ disabled: false,
+};
+export const Primary = Template.bind({});
+Primary.args = {
+ ...Default.args,
+ type: "primary",
+ l10nId: "moz-button-primary",
+};
+export const Destructive = Template.bind({});
+Destructive.args = {
+ ...Default.args,
+ type: "destructive",
+ l10nId: "moz-button-destructive",
+};
+export const Icon = Template.bind({});
+Icon.args = {
+ ...Default.args,
+ type: "icon",
+ l10nId: "moz-button-titled",
+};
+export const IconSmall = Template.bind({});
+IconSmall.args = {
+ ...Icon.args,
+ size: "small",
+};
+export const IconGhost = Template.bind({});
+IconGhost.args = {
+ ...Icon.args,
+ type: "icon ghost",
+};
diff --git a/toolkit/content/widgets/moz-input-box.js b/toolkit/content/widgets/moz-input-box.js
index 4704db6dc5..6e7b7b3f29 100644
--- a/toolkit/content/widgets/moz-input-box.js
+++ b/toolkit/content/widgets/moz-input-box.js
@@ -92,7 +92,7 @@
});
if (this.spellcheck) {
- this.menupopup.addEventListener("popuphiding", event => {
+ this.menupopup.addEventListener("popuphiding", () => {
if (this.spellCheckerUI) {
this.spellCheckerUI.clearSuggestionsFromMenu();
this.spellCheckerUI.clearDictionaryListFromMenu();
diff --git a/toolkit/content/widgets/moz-label/README.stories.md b/toolkit/content/widgets/moz-label/README.stories.md
index a3492ebefa..f5e4e2dd14 100644
--- a/toolkit/content/widgets/moz-label/README.stories.md
+++ b/toolkit/content/widgets/moz-label/README.stories.md
@@ -3,10 +3,10 @@
`moz-label` is an extension of the built-in `HTMLLabelElement` that provides accesskey styling and formatting as well as some click handling logic.
```html story
-<label is="moz-label" accesskey="c" for="check">
+<label is="moz-label" accesskey="c" for="check" style={{ display: "inline-block" }}>
This is a label with an accesskey:
</label>
-<input id="check" type="checkbox" defaultChecked />
+<input id="check" type="checkbox" defaultChecked style={{ display: "inline-block" }} />
```
Accesskey underlining is enabled by default on Windows and Linux. It is also enabled in Storybook on Mac for demonstrative purposes, but is usually controlled by the `ui.key.menuAccessKey` preference.
diff --git a/toolkit/content/widgets/moz-label/moz-label.mjs b/toolkit/content/widgets/moz-label/moz-label.mjs
index 7812436ecd..cd9144e7cc 100644
--- a/toolkit/content/widgets/moz-label/moz-label.mjs
+++ b/toolkit/content/widgets/moz-label/moz-label.mjs
@@ -103,7 +103,7 @@ class MozTextLabel extends HTMLLabelElement {
this.formatAccessKey();
}
- _onClick(event) {
+ _onClick() {
let controlElement = this.labeledControlElement;
if (!controlElement || this.disabled) {
return;
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 58f41c28e4..d83de5d29f 100644
--- a/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs
+++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs
@@ -69,7 +69,7 @@ export default class MozMessageBar extends MozLitElement {
this.dismissable = false;
}
- onSlotchange(e) {
+ onSlotchange() {
let actions = this.actionsSlotEl.assignedNodes();
this.actionsEl.classList.toggle("active", actions.length);
}
diff --git a/toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs b/toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs
index 65803eed9f..6f19c45aee 100644
--- a/toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs
+++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.stories.mjs
@@ -121,3 +121,9 @@ WithSupportLink.args = {
hasSupportLink: true,
hasActionButton: false,
};
+
+export const WithHeading = Template.bind({});
+WithHeading.args = {
+ ...Default.args,
+ l10nId: "moz-message-bar-message-heading",
+};
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
new file mode 100644
index 0000000000..2975bb1a7c
--- /dev/null
+++ b/toolkit/content/widgets/moz-page-nav/moz-page-nav-button.css
@@ -0,0 +1,123 @@
+/* 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 {
+ border-radius: var(--border-radius-small);
+ &:focus-visible {
+ outline-offset: var(--page-nav-focus-outline-inset);
+ }
+}
+
+button {
+ background-color: var(--page-nav-button-background-color);
+ border: 1px solid var(--page-nav-border-color);
+ -moz-context-properties: fill, fill-opacity;
+ fill: currentColor;
+ display: grid;
+ grid-template-columns: min-content 1fr;
+ gap: 12px;
+ align-items: center;
+ font-size: inherit;
+ width: 100%;
+ font-weight: normal;
+ border-radius: var(--page-nav-button-border-radius);
+ color: var(--page-nav-button-text-color);
+ text-align: start;
+ transition: background-color 150ms;
+ padding: var(--page-nav-button-padding);
+}
+
+button:hover {
+ cursor: pointer;
+}
+
+@media not (prefers-contrast) {
+ button {
+ border-inline-start: 2px solid transparent;
+ border-inline-end: none;
+ border-block: none;
+ }
+
+ button:hover,
+ button[selected]:hover {
+ background-color: var(--page-nav-button-background-color-hover);
+ }
+
+ button[selected]:hover {
+ border-inline-start-color: inherit;
+ }
+
+ button[selected],
+ button[selected]:hover {
+ border-inline-start: 2px solid;
+ }
+
+ button[selected]:not(:focus-visible) {
+ border-start-start-radius: 0;
+ border-end-start-radius: 0;
+ }
+
+ button[selected]:not(:hover) {
+ color: var(--color-accent-primary);
+ background-color: color-mix(in srgb, var(--page-nav-button-background-color-selected) 5%, transparent);
+ border-inline-start-color: var(--color-accent-primary);
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ button[selected] {
+ background-color: color-mix(in srgb, var(--page-nav-button-background-color-selected) 12%, transparent);
+ }
+}
+
+button:focus-visible,
+button[selected]:focus-visible {
+ outline: var(--focus-outline);
+ outline-offset: var(--focus-outline-offset);
+ border-radius: var(--border-radius-small);
+}
+
+.page-nav-icon {
+ height: 20px;
+ width: 20px;
+ -moz-context-properties: fill;
+ fill: currentColor;
+}
+
+@media (prefers-contrast) {
+ button {
+ transition: none;
+ border-color: ButtonText;
+ background-color: var(--button-background-color);
+ }
+
+ button:hover {
+ color: SelectedItem;
+ }
+
+ button[selected] {
+ color: SelectedItemText;
+ background-color: SelectedItem;
+ border-color: SelectedItem;
+ }
+}
+
+slot {
+ font-size: var(--font-size-large);
+ margin: 0;
+ padding-inline-start: 0;
+ user-select: none;
+}
+
+@media (max-width: 52rem) {
+ button {
+ grid-template-columns: min-content;
+ justify-content: center;
+ margin-inline: 0;
+ }
+
+ slot {
+ display: none;
+ }
+}
diff --git a/toolkit/content/widgets/moz-page-nav/moz-page-nav.css b/toolkit/content/widgets/moz-page-nav/moz-page-nav.css
new file mode 100644
index 0000000000..49000f622d
--- /dev/null
+++ b/toolkit/content/widgets/moz-page-nav/moz-page-nav.css
@@ -0,0 +1,76 @@
+/* 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 {
+ --page-nav-button-border-radius: var(--button-border-radius);
+ --page-nav-button-text-color: var(--button-text-color);
+ --page-nav-button-background-color: transparent;
+ --page-nav-button-background-color-hover: var(--button-background-color-hover);
+ --page-nav-button-background-color-selected: var(--color-accent-primary);
+ --page-nav-button-padding: var(--space-small);
+ --page-nav-margin-top: 72px;
+ --page-nav-margin-bottom: 36px;
+ --page-nav-gap: 25px;
+ --page-nav-button-gap: var(--space-xsmall);
+ --page-nav-border-color: var(--border-color);
+ --page-nav-focus-outline-inset: var(--focus-outline-inset);
+ --page-nav-width: 240px;
+ margin-inline-start: 42px;
+ position: sticky;
+ top: 0;
+ height: 100vh;
+ width: var(--page-nav-width);
+
+ @media (prefers-reduced-motion) {
+ /* (See Bug 1610081) Setting border-inline-end to add clear differentiation between side navigation and main content area */
+ border-inline-end: 1px solid var(--page-nav-border-color);
+ }
+
+ @media (max-width: 52rem) {
+ grid-template-rows: 1fr auto;
+ --page-nav-width: 118px;
+ }
+}
+
+nav {
+ display: grid;
+ grid-template-rows: min-content 1fr auto;
+ gap: var(--page-nav-gap);
+ margin-block: var(--page-nav-margin-top) var(--page-nav-margin-bottom);
+ height: calc(100vh - var(--page-nav-margin-top) - var(--page-nav-margin-bottom));
+ @media (max-width: 52rem) {
+ grid-template-rows: 1fr auto;
+ }
+}
+
+.page-nav-header {
+ /* Align the header text/icon with the page nav button icons */
+ margin-inline-start: var(--page-nav-button-padding);
+ font-size: var(--font-size-xlarge);
+ font-weight: var(--font-weight-bold);
+ margin-block: 0;
+
+ @media (max-width: 52rem) {
+ display: none;
+ }
+}
+
+.primary-nav-group,
+#secondary-nav-group {
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-auto-rows: min-content;
+ gap: var(--page-nav-button-gap);
+
+ @media (max-width: 52rem) {
+ justify-content: center;
+ grid-template-columns: min-content;
+ }
+}
+
+@media (prefers-contrast) {
+ .primary-nav-group {
+ gap: var(--space-small);
+ }
+}
diff --git a/toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs b/toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs
new file mode 100644
index 0000000000..f998ee735f
--- /dev/null
+++ b/toolkit/content/widgets/moz-page-nav/moz-page-nav.mjs
@@ -0,0 +1,170 @@
+/* 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/. */
+
+import { html } from "chrome://global/content/vendor/lit.all.mjs";
+import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
+
+/**
+ * A grouping of navigation buttons that is displayed at the page level,
+ * intended to change the selected view, provide a heading, and have links
+ * to external resources.
+ *
+ * @tagname moz-page-nav
+ * @property {string} currentView - The currently selected view.
+ * @property {string} heading - A heading to be displayed at the top of the navigation.
+ * @slot [default] - Used to append moz-page-nav-button elements to the navigation.
+ */
+export default class MozPageNav extends MozLitElement {
+ static properties = {
+ currentView: { type: String },
+ heading: { type: String },
+ };
+
+ static queries = {
+ headingEl: "#page-nav-header",
+ primaryNavGroupSlot: ".primary-nav-group slot",
+ secondaryNavGroupSlot: "#secondary-nav-group slot",
+ };
+
+ get pageNavButtons() {
+ return this.primaryNavGroupSlot
+ .assignedNodes()
+ .filter(
+ node => node?.localName === "moz-page-nav-button" && !node.hidden
+ );
+ }
+
+ onChangeView(e) {
+ this.currentView = e.target.view;
+ }
+
+ handleFocus(e) {
+ if (e.key == "ArrowDown" || e.key == "ArrowRight") {
+ e.preventDefault();
+ this.focusNextView();
+ } else if (e.key == "ArrowUp" || e.key == "ArrowLeft") {
+ e.preventDefault();
+ this.focusPreviousView();
+ }
+ }
+
+ focusPreviousView() {
+ let pageNavButtons = this.pageNavButtons;
+ let currentIndex = pageNavButtons.findIndex(b => b.selected);
+ let prev = pageNavButtons[currentIndex - 1];
+ if (prev) {
+ prev.activate();
+ prev.buttonEl.focus();
+ }
+ }
+
+ focusNextView() {
+ let pageNavButtons = this.pageNavButtons;
+ let currentIndex = pageNavButtons.findIndex(b => b.selected);
+ let next = pageNavButtons[currentIndex + 1];
+ if (next) {
+ next.activate();
+ next.buttonEl.focus();
+ }
+ }
+
+ render() {
+ return html`
+ <link
+ rel="stylesheet"
+ href="chrome://global/content/elements/moz-page-nav.css"
+ />
+ <nav>
+ <h1 class="page-nav-header" id="page-nav-header">${this.heading}</h1>
+ <div
+ class="primary-nav-group"
+ role="tablist"
+ aria-orientation="vertical"
+ aria-labelledby="page-nav-header"
+ >
+ <slot
+ @change-view=${this.onChangeView}
+ @keydown=${this.handleFocus}
+ ></slot>
+ </div>
+ <div id="secondary-nav-group" role="group">
+ <slot name="secondary-nav" @keydown=${this.handleFocus}></slot>
+ </div>
+ </nav>
+ `;
+ }
+
+ updated() {
+ let isViewSelected = false;
+ let assignedPageNavButtons = this.pageNavButtons;
+ for (let button of assignedPageNavButtons) {
+ button.selected = button.view == this.currentView;
+ isViewSelected = isViewSelected || button.selected;
+ }
+ if (!isViewSelected && assignedPageNavButtons.length) {
+ // Current page nav has no matching view, reset to the first view.
+ assignedPageNavButtons[0].activate();
+ }
+ }
+}
+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} 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.
+ * @slot [default] - Used to append the l10n string to the button.
+ */
+export class MozPageNavButton extends MozLitElement {
+ static properties = {
+ iconSrc: { type: String },
+ l10nId: { type: String },
+ selected: { type: Boolean },
+ };
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.setAttribute("role", "none");
+ }
+
+ static queries = {
+ buttonEl: "button",
+ };
+
+ get view() {
+ return this.getAttribute("view");
+ }
+
+ activate() {
+ this.dispatchEvent(
+ new CustomEvent("change-view", {
+ bubbles: true,
+ composed: true,
+ })
+ );
+ }
+
+ render() {
+ 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}
+ role="tab"
+ ?selected=${this.selected}
+ @click=${this.activate}
+ >
+ <img class="page-nav-icon" src=${this.iconSrc} />
+ <slot></slot>
+ </button>
+ `;
+ }
+}
+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
new file mode 100644
index 0000000000..4ac7b455cf
--- /dev/null
+++ b/toolkit/content/widgets/moz-page-nav/moz-page-nav.stories.mjs
@@ -0,0 +1,77 @@
+/* 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/. */
+
+import { html } from "../vendor/lit.all.mjs";
+// eslint-disable-next-line import/no-unassigned-import
+import "./moz-page-nav.mjs";
+
+export default {
+ title: "UI Widgets/Page Nav",
+ component: "moz-page-nav",
+ parameters: {
+ status: "in-development",
+ actions: {
+ handles: ["change-view"],
+ },
+ fluent: `
+moz-page-nav-button-one = View 1
+ .title = View 1
+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-heading =
+ .heading = Heading
+ `,
+ },
+};
+
+const Template = () => html`
+ <style>
+ #page {
+ height: 100%;
+ display: flex;
+
+ @media (max-width: 52rem) {
+ grid-template-columns: 82px 1fr;
+ }
+ }
+ moz-page-nav {
+ margin-inline-start: 10px;
+ --page-nav-margin-top: 10px;
+
+ @media (max-width: 52rem) {
+ margin-inline-start: 0;
+ }
+ }
+ </style>
+ <div id="page">
+ <moz-page-nav data-l10n-id="moz-page-nav-heading" data-l10n-attrs="heading">
+ <moz-page-nav-button
+ view="view-one"
+ data-l10n-id="moz-page-nav-button-one"
+ iconSrc="chrome://browser/skin/preferences/category-general.svg"
+ >
+ </moz-page-nav-button>
+ <moz-page-nav-button
+ view="view-two"
+ data-l10n-id="moz-page-nav-button-two"
+ iconSrc="chrome://browser/skin/preferences/category-general.svg"
+ >
+ </moz-page-nav-button>
+ <moz-page-nav-button
+ view="view-three"
+ data-l10n-id="moz-page-nav-button-three"
+ iconSrc="chrome://browser/skin/preferences/category-general.svg"
+ >
+ </moz-page-nav-button>
+ </moz-page-nav>
+ <main></main>
+ </div>
+`;
+
+export const Default = Template.bind({});
+Default.args = {};
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 23f18ac434..9d2d6ffac2 100644
--- a/toolkit/content/widgets/moz-support-link/moz-support-link.mjs
+++ b/toolkit/content/widgets/moz-support-link/moz-support-link.mjs
@@ -82,7 +82,7 @@ export default class MozSupportLink extends HTMLAnchorElement {
}
}
- attributeChangedCallback(attrName, oldVal, newVal) {
+ attributeChangedCallback(attrName) {
if (attrName === "support-page" || attrName === "utm-content") {
this.#setHref();
}
diff --git a/toolkit/content/widgets/moz-toggle/moz-toggle.css b/toolkit/content/widgets/moz-toggle/moz-toggle.css
index 8b67a81878..2005544181 100644
--- a/toolkit/content/widgets/moz-toggle/moz-toggle.css
+++ b/toolkit/content/widgets/moz-toggle/moz-toggle.css
@@ -38,11 +38,15 @@
--toggle-background-color-pressed-hover: var(--color-accent-primary-hover);
--toggle-background-color-pressed-active: var(--color-accent-primary-active);
--toggle-border-color: var(--border-interactive-color);
+ --toggle-border-color-hover: var(--toggle-border-color);
+ --toggle-border-color-active: var(--toggle-border-color);
--toggle-border-radius: var(--border-radius-circle);
--toggle-border-width: var(--border-width);
--toggle-height: var(--size-item-small);
--toggle-width: var(--size-item-large);
--toggle-dot-background-color: var(--toggle-border-color);
+ --toggle-dot-background-color-hover: var(--toggle-dot-background-color);
+ --toggle-dot-background-color-active: var(--toggle-dot-background-color);
--toggle-dot-background-color-on-pressed: var(--color-canvas);
--toggle-dot-margin: 1px;
--toggle-dot-height: calc(var(--toggle-height) - 2 * var(--toggle-dot-margin) - 2 * var(--toggle-border-width));
@@ -141,17 +145,6 @@
background-color: var(--toggle-background-color-disabled);
}
- .toggle-button {
- --toggle-dot-background-color: var(--color-accent-primary);
- --toggle-dot-background-color-hover: var(--color-accent-primary-hover);
- --toggle-dot-background-color-active: var(--color-accent-primary-active);
- --toggle-dot-background-color-on-pressed: var(--button-background-color);
- --toggle-background-color-disabled: var(--button-background-color-disabled);
- --toggle-border-color-hover: var(--border-interactive-color-hover);
- --toggle-border-color-active: var(--border-interactive-color-active);
- --toggle-border-color-disabled: var(--border-interactive-color-disabled);
- }
-
.toggle-button:enabled:hover {
border-color: var(--toggle-border-color-hover);
}
@@ -175,6 +168,24 @@
border-color: var(--toggle-dot-background-color-hover);
}
+ .toggle-button:hover::before,
+ .toggle-button:active::before {
+ background-color: var(--toggle-dot-background-color-hover);
+ }
+}
+
+@media (forced-colors) {
+ .toggle-button {
+ --toggle-dot-background-color: var(--color-accent-primary);
+ --toggle-dot-background-color-hover: var(--color-accent-primary-hover);
+ --toggle-dot-background-color-active: var(--color-accent-primary-active);
+ --toggle-dot-background-color-on-pressed: var(--button-background-color);
+ --toggle-background-color-disabled: var(--button-background-color-disabled);
+ --toggle-border-color-hover: var(--border-interactive-color-hover);
+ --toggle-border-color-active: var(--border-interactive-color-active);
+ --toggle-border-color-disabled: var(--border-interactive-color-disabled);
+ }
+
.toggle-button[aria-pressed="true"]:enabled::after {
border: 1px solid var(--button-background-color);
content: '';
@@ -189,10 +200,4 @@
.toggle-button[aria-pressed="true"]:enabled:active::after {
border-color: var(--toggle-border-color-active);
}
-
- .toggle-button:hover::before,
- .toggle-button:hover:active::before,
- .toggle-button:active::before {
- background-color: var(--toggle-dot-background-color-hover);
- }
}
diff --git a/toolkit/content/widgets/named-deck.js b/toolkit/content/widgets/named-deck.js
index 6b3b7a8835..42c96e278d 100644
--- a/toolkit/content/widgets/named-deck.js
+++ b/toolkit/content/widgets/named-deck.js
@@ -156,7 +156,7 @@
this.getRootNode().removeEventListener("keypress", this);
}
- attributeChangedCallback(name, oldVal, newVal) {
+ attributeChangedCallback(name) {
if (name == "orientation") {
if (this.isVertical) {
this.setAttribute("aria-orientation", this.orientation);
diff --git a/toolkit/content/widgets/notificationbox.js b/toolkit/content/widgets/notificationbox.js
index f23fb03a74..0161588853 100644
--- a/toolkit/content/widgets/notificationbox.js
+++ b/toolkit/content/widgets/notificationbox.js
@@ -622,7 +622,7 @@
async function createNotificationMessageElement() {
await window.ensureCustomElements("moz-message-bar");
- let MozMessageBar = customElements.get("moz-message-bar");
+ let MozMessageBar = await customElements.whenDefined("moz-message-bar");
class NotificationMessage extends MozMessageBar {
static queries = {
...MozMessageBar.queries,
diff --git a/toolkit/content/widgets/panel-list/README.stories.md b/toolkit/content/widgets/panel-list/README.stories.md
index b8800e2b5f..3e8617958e 100644
--- a/toolkit/content/widgets/panel-list/README.stories.md
+++ b/toolkit/content/widgets/panel-list/README.stories.md
@@ -6,7 +6,7 @@ children and optional `hr` elements as separators. The `panel-list` will anchor
itself to the target of the initiating event when opened with
`panelList.toggle(event)`.
-Note: Nested menus are not currently supported. XUL is currently required to
+Note: XUL is currently required to
support accesskey underlining (although using `moz-label` could change that).
Shortcuts are not displayed automatically in the `panel-item`.
@@ -229,3 +229,23 @@ grow larger than its containing window if needed.
</html:panel-list>
</panel>
```
+
+### Submenus
+
+`panel-list` supports nested submenus. Submenus can be created by nesting a second `panel-list` in a `panel-item`'s `submenu` slot and specifying a `submenu` attribute on that `panel-item` that points to the nested list's ID. For example:
+
+```html
+<panel-list>
+ <panel-item>No submenu</panel-item>
+ <panel-item>No submenu</panel-item>
+ <panel-item submenu="example-submenu">
+ Has a submenu
+ <panel-list slot="submenu" id="example-submenu">
+ <panel-item>I'm a submenu item!</panel-item>
+ <panel-item>I'm also a submenu item!</panel-item>
+ </panel-list>
+ </panel-item>
+</panel-list>
+```
+
+As of February 2024 submenus are only in use in Firefox View and support for nesting beyond one submenu may be limited.
diff --git a/toolkit/content/widgets/panel-list/panel-list.css b/toolkit/content/widgets/panel-list/panel-list.css
index 4358fc0cf8..619e6919a3 100644
--- a/toolkit/content/widgets/panel-list/panel-list.css
+++ b/toolkit/content/widgets/panel-list/panel-list.css
@@ -26,6 +26,10 @@
box-sizing: border-box;
}
+:host([has-submenu]) {
+ overflow-y: visible;
+}
+
:host(:not([slot=submenu])) {
max-height: 100%;
}
diff --git a/toolkit/content/widgets/panel-list/panel-list.js b/toolkit/content/widgets/panel-list/panel-list.js
index 1cc1f865c3..2e93b4ddc3 100644
--- a/toolkit/content/widgets/panel-list/panel-list.js
+++ b/toolkit/content/widgets/panel-list/panel-list.js
@@ -308,7 +308,7 @@
}
addHideListeners() {
- if (this.hasAttribute("stay-open") && !this.lastAnchorNode.hasSubmenu) {
+ if (this.hasAttribute("stay-open") && !this.lastAnchorNode?.hasSubmenu) {
// This is intended for inspection in Storybook.
return;
}
@@ -631,31 +631,12 @@
this.#defaultSlot = document.createElement("slot");
this.#defaultSlot.style.display = "none";
- if (this.hasSubmenu) {
- this.icon = document.createElement("div");
- this.icon.setAttribute("class", "submenu-icon");
- this.label.setAttribute("class", "submenu-label");
-
- this.button.setAttribute("class", "submenu-container");
- this.button.appendChild(this.icon);
-
- this.submenuSlot = document.createElement("slot");
- this.submenuSlot.name = "submenu";
-
- this.shadowRoot.append(
- style,
- this.button,
- this.#defaultSlot,
- this.submenuSlot
- );
- } else {
- this.shadowRoot.append(
- style,
- this.button,
- supportLinkSlot,
- this.#defaultSlot
- );
- }
+ this.shadowRoot.append(
+ style,
+ this.button,
+ supportLinkSlot,
+ this.#defaultSlot
+ );
}
connectedCallback() {
@@ -664,6 +645,10 @@
this._l10nRootConnected = true;
}
+ this.panel =
+ this.getRootNode()?.host?.closest("panel-list") ||
+ this.closest("panel-list");
+
if (!this.#initialized) {
this.#initialized = true;
// When click listeners are added to the panel-item it creates a node in
@@ -683,18 +668,28 @@
});
if (this.hasSubmenu) {
+ this.panel.setAttribute("has-submenu", "");
+ this.icon = document.createElement("div");
+ this.icon.setAttribute("class", "submenu-icon");
+ this.label.setAttribute("class", "submenu-label");
+
+ this.button.setAttribute("class", "submenu-container");
+ this.button.appendChild(this.icon);
+
+ this.submenuSlot = document.createElement("slot");
+ this.submenuSlot.name = "submenu";
+
+ this.shadowRoot.append(this.submenuSlot);
+
this.setSubmenuContents();
}
}
- this.panel =
- this.getRootNode()?.host?.closest("panel-list") ||
- this.closest("panel-list");
-
if (this.panel) {
this.panel.addEventListener("hidden", this);
this.panel.addEventListener("shown", this);
}
+
if (this.hasSubmenu) {
this.addEventListener("mouseenter", this);
this.addEventListener("mouseleave", this);
@@ -762,7 +757,9 @@
setSubmenuContents() {
this.submenuPanel = this.submenuSlot.assignedNodes()[0];
- this.shadowRoot.append(this.submenuPanel);
+ if (this.submenuPanel) {
+ this.shadowRoot.append(this.submenuPanel);
+ }
}
get disabled() {
diff --git a/toolkit/content/widgets/panel-list/panel-list.stories.mjs b/toolkit/content/widgets/panel-list/panel-list.stories.mjs
index 9c5a4cbe1f..db0ab7597c 100644
--- a/toolkit/content/widgets/panel-list/panel-list.stories.mjs
+++ b/toolkit/content/widgets/panel-list/panel-list.stories.mjs
@@ -22,6 +22,9 @@ panel-list-checked = Checked
panel-list-badged = Badged, look at me
panel-list-passwords = Passwords
panel-list-settings = Settings
+submenu-item-one = Submenu Item One
+submenu-item-two = Submenu Item Two
+submenu-item-three = Submenu Item Three
`,
},
};
@@ -36,7 +39,7 @@ function openMenu(event) {
}
}
-const Template = ({ isOpen, items, wideAnchor }) =>
+const Template = ({ isOpen, items, wideAnchor, hasSubMenu }) =>
html`
<style>
panel-item[icon="passwords"]::part(button) {
@@ -93,22 +96,36 @@ const Template = ({ isOpen, items, wideAnchor }) =>
?open=${isOpen}
?min-width-from-anchor=${wideAnchor}
>
- ${items.map(i =>
- i == "<hr>"
+ ${items.map((item, index) => {
+ // Always showing submenu on the first item for simplicity.
+ let showSubMenu = hasSubMenu && index == 0;
+ let subMenuId = showSubMenu ? "example-sub-menu" : undefined;
+ return item == "<hr>"
? html` <hr /> `
: html`
<panel-item
- icon=${i.icon ?? ""}
- ?checked=${i.checked}
- ?badged=${i.badged}
- accesskey=${ifDefined(i.accesskey)}
- data-l10n-id=${i.l10nId ?? i}
- ></panel-item>
- `
- )}
+ icon=${item.icon ?? ""}
+ ?checked=${item.checked}
+ ?badged=${item.badged}
+ accesskey=${ifDefined(item.accesskey)}
+ data-l10n-id=${item.l10nId ?? item}
+ submenu=${ifDefined(subMenuId)}
+ >
+ ${showSubMenu ? subMenuTemplate() : ""}
+ </panel-item>
+ `;
+ })}
</panel-list>
`;
+const subMenuTemplate = () => html`
+ <panel-list slot="submenu" id="example-sub-menu">
+ <panel-item data-l10n-id="submenu-item-one"></panel-item>
+ <panel-item data-l10n-id="submenu-item-two"></panel-item>
+ <panel-item data-l10n-id="submenu-item-three"></panel-item>
+ </panel-list>
+`;
+
export const Simple = Template.bind({});
Simple.args = {
isOpen: false,
@@ -145,3 +162,9 @@ Wide.args = {
...Simple.args,
wideAnchor: true,
};
+
+export const SubMenu = Template.bind({});
+SubMenu.args = {
+ ...Simple.args,
+ hasSubMenu: true,
+};
diff --git a/toolkit/content/widgets/radio.js b/toolkit/content/widgets/radio.js
index 482323acb9..41e8a945ba 100644
--- a/toolkit/content/widgets/radio.js
+++ b/toolkit/content/widgets/radio.js
@@ -197,7 +197,7 @@
* @param {DOMNode} child
* The <radio> element that got removed
*/
- radioUnattached(child) {
+ radioUnattached() {
// Just invalidate the cache, next time it's fetched it'll get rebuilt.
this._radioChildren = null;
}
@@ -481,13 +481,13 @@
constructor() {
super();
- this.addEventListener("click", event => {
+ this.addEventListener("click", () => {
if (!this.disabled) {
this.control.selectedItem = this;
}
});
- this.addEventListener("mousedown", event => {
+ this.addEventListener("mousedown", () => {
if (!this.disabled) {
this.control.focusedItem = this;
}
diff --git a/toolkit/content/widgets/richlistbox.js b/toolkit/content/widgets/richlistbox.js
index 904ef9ceec..01d970e6ed 100644
--- a/toolkit/content/widgets/richlistbox.js
+++ b/toolkit/content/widgets/richlistbox.js
@@ -126,7 +126,7 @@
}
});
- this.addEventListener("focus", event => {
+ this.addEventListener("focus", () => {
if (this.getRowCount() > 0) {
if (this.currentIndex == -1) {
this.currentIndex = this.getIndexOfFirstVisibleRow();
diff --git a/toolkit/content/widgets/search-textbox.js b/toolkit/content/widgets/search-textbox.js
index abdcfa2999..b254b2796d 100644
--- a/toolkit/content/widgets/search-textbox.js
+++ b/toolkit/content/widgets/search-textbox.js
@@ -214,7 +214,7 @@
}
}
- on_mousedown(event) {
+ on_mousedown() {
if (!this.hasAttribute("focused")) {
this.setSelectionRange(0, 0);
this.focus();
diff --git a/toolkit/content/widgets/tabbox.js b/toolkit/content/widgets/tabbox.js
index 997e8413f2..b1b2ddecce 100644
--- a/toolkit/content/widgets/tabbox.js
+++ b/toolkit/content/widgets/tabbox.js
@@ -24,15 +24,15 @@
}
connectedCallback() {
- Services.els.addSystemEventListener(document, "keydown", this, false);
+ document.addEventListener("keydown", this, { mozSystemGroup: true });
window.addEventListener("unload", this.disconnectedCallback, {
once: true,
});
}
disconnectedCallback() {
+ document.removeEventListener("keydown", this, { mozSystemGroup: true });
window.removeEventListener("unload", this.disconnectedCallback);
- Services.els.removeSystemEventListener(document, "keydown", this, false);
}
set handleCtrlTab(val) {
@@ -729,7 +729,7 @@
direction = 1,
wrap = false,
startWithAdjacent = true,
- filter = tab => true,
+ filter = () => true,
} = opts;
let tab = startTab;
@@ -804,7 +804,7 @@
}
}
- _canAdvanceToTab(aTab) {
+ _canAdvanceToTab() {
return true;
}
diff --git a/toolkit/content/widgets/text.js b/toolkit/content/widgets/text.js
index ca10f1489e..7bbf6db4cc 100644
--- a/toolkit/content/widgets/text.js
+++ b/toolkit/content/widgets/text.js
@@ -67,7 +67,7 @@
this.formatAccessKey();
}
- _onClick(event) {
+ _onClick() {
let controlElement = this.labeledControlElement;
if (!controlElement || this.disabled) {
return;
diff --git a/toolkit/content/widgets/textrecognition.js b/toolkit/content/widgets/textrecognition.js
index 887d576770..c517f7bfb1 100644
--- a/toolkit/content/widgets/textrecognition.js
+++ b/toolkit/content/widgets/textrecognition.js
@@ -4,7 +4,7 @@
"use strict";
// This is a UA widget. It runs in per-origin UA widget scope,
-// to be loaded by UAWidgetsChild.jsm.
+// to be loaded by UAWidgetsChild.sys.mjs.
this.TextRecognitionWidget = class {
/**
diff --git a/toolkit/content/widgets/tree.js b/toolkit/content/widgets/tree.js
index 322e42586e..4993bef0c2 100644
--- a/toolkit/content/widgets/tree.js
+++ b/toolkit/content/widgets/tree.js
@@ -515,7 +515,7 @@
}
}
- _onDragMouseUp(aEvent) {
+ _onDragMouseUp() {
var col = document.treecolDragging;
if (!col) {
return;
@@ -786,7 +786,7 @@
}
});
- this.addEventListener("touchend", event => {
+ this.addEventListener("touchend", () => {
this._touchY = -1;
});
@@ -840,7 +840,7 @@
}
});
- this.addEventListener("focus", event => {
+ this.addEventListener("focus", () => {
this.focused = true;
if (this.currentIndex == -1 && this.view.rowCount > 0) {
this.currentIndex = this.getFirstVisibleRow();
@@ -1651,7 +1651,7 @@
this.ensureRowIsVisible(edge);
}
- _handleEnter(event) {
+ _handleEnter() {
if (this._editingColumn) {
this.stopEditing(true);
this.focus();
diff --git a/toolkit/content/widgets/videocontrols.js b/toolkit/content/widgets/videocontrols.js
index 21c8946e60..73a32164aa 100644
--- a/toolkit/content/widgets/videocontrols.js
+++ b/toolkit/content/widgets/videocontrols.js
@@ -5,7 +5,7 @@
"use strict";
// This is a UA widget. It runs in per-origin UA widget scope,
-// to be loaded by UAWidgetsChild.jsm.
+// to be loaded by UAWidgetsChild.sys.mjs.
/*
* This is the class of entry. It will construct the actual implementation
@@ -64,11 +64,8 @@ this.VideoControlsWidget = class {
// the underlying element state hasn't changed in ways that we
// care about. This can happen if the property is set again
// without a value change.
- if (
- this.impl &&
- this.impl.constructor == newImpl &&
- this.impl.elementStateMatches(this.element)
- ) {
+ if (this.impl && this.impl.constructor == newImpl) {
+ this.impl.onchange();
return;
}
if (this.impl) {
@@ -458,10 +455,10 @@ this.VideoControlsImplWidget = class {
this.statusIcon.setAttribute("type", "error");
this.updateErrorText();
this.setupStatusFader(true);
- } else if (VideoControlsWidget.isPictureInPictureVideo(this.video)) {
- this.setShowPictureInPictureMessage(true);
}
+ this.updatePictureInPictureMessage();
+
if (this.video.readyState >= this.video.HAVE_METADATA) {
// According to the spec[1], at the HAVE_METADATA (or later) state, we know
// the video duration and dimensions, which means we can calculate whether or
@@ -934,6 +931,8 @@ this.VideoControlsImplWidget = class {
// Since this event come from the layout, this is the only place
// we are sure of that probing into layout won't trigger or force
// reflow.
+ // FIXME(emilio): We should rewrite this to just use
+ // ResizeObserver, probably.
this.reflowTriggeringCallValidator.isReflowTriggeringPropsAllowed = true;
this.updateReflowedDimensions();
this.reflowTriggeringCallValidator.isReflowTriggeringPropsAllowed = false;
@@ -1095,7 +1094,10 @@ this.VideoControlsImplWidget = class {
);
},
- setShowPictureInPictureMessage(showMessage) {
+ updatePictureInPictureMessage() {
+ let showMessage =
+ !this.hasError() &&
+ VideoControlsWidget.isPictureInPictureVideo(this.video);
this.pictureInPictureOverlay.hidden = !showMessage;
this.isShowingPictureInPictureMessage = showMessage;
},
@@ -1188,7 +1190,7 @@ this.VideoControlsImplWidget = class {
}
},
- onScrubberInput(e) {
+ onScrubberInput() {
const duration = Math.round(this.video.duration * 1000); // in ms
let time = this.scrubber.value;
@@ -1200,7 +1202,7 @@ this.VideoControlsImplWidget = class {
this.pauseVideoDuringDragging();
},
- onScrubberChange(e) {
+ onScrubberChange() {
this.scrubber.isDragging = false;
if (this.isPausedByDragging) {
@@ -1815,12 +1817,7 @@ this.VideoControlsImplWidget = class {
updateMuteButtonState() {
var muted = this.isEffectivelyMuted;
-
- if (muted) {
- this.muteButton.setAttribute("muted", "true");
- } else {
- this.muteButton.removeAttribute("muted");
- }
+ this.muteButton.toggleAttribute("muted", muted);
var id = muted
? "videocontrols-unmute-button"
@@ -2026,12 +2023,7 @@ this.VideoControlsImplWidget = class {
},
setCastingButtonState() {
- if (this.isCastingOn) {
- this.castingButton.setAttribute("enabled", "true");
- } else {
- this.castingButton.removeAttribute("enabled");
- }
-
+ this.castingButton.toggleAttribute("enabled", this.isCastingOn);
this.adjustControlSize();
},
@@ -2058,22 +2050,15 @@ this.VideoControlsImplWidget = class {
},
setClosedCaptionButtonState() {
- if (this.isClosedCaptionOn) {
- this.closedCaptionButton.setAttribute("enabled", "true");
- } else {
- this.closedCaptionButton.removeAttribute("enabled");
- }
-
+ this.closedCaptionButton.toggleAttribute(
+ "enabled",
+ this.isClosedCaptionOn
+ );
let ttItems = this.textTrackList.childNodes;
for (let tti of ttItems) {
const idx = +tti.getAttribute("index");
-
- if (idx == this.currentTextTrackIndex) {
- tti.setAttribute("aria-checked", "true");
- } else {
- tti.setAttribute("aria-checked", "false");
- }
+ tti.setAttribute("aria-checked", idx == this.currentTextTrackIndex);
}
this.adjustControlSize();
@@ -2804,10 +2789,6 @@ this.VideoControlsImplWidget = class {
if (this.Utils.isTouchControls) {
this.TouchUtils.init(this.shadowRoot, this.Utils);
}
- this.shadowRoot.firstChild.dispatchEvent(
- new this.window.CustomEvent("VideoBindingAttached")
- );
-
this._setupEventListeners();
}
@@ -2920,9 +2901,9 @@ this.VideoControlsImplWidget = class {
this.l10n.translateRoots();
}
- elementStateMatches(element) {
- let elementInPiP = VideoControlsWidget.isPictureInPictureVideo(element);
- return this.isShowingPictureInPictureMessage == elementInPiP;
+ onchange() {
+ this.Utils.updatePictureInPictureMessage();
+ this.shadowRoot.firstChild.removeAttribute("flipped");
}
teardown() {
@@ -3080,14 +3061,9 @@ this.NoControlsMobileImplWidget = class {
},
};
this.Utils.init(this.shadowRoot);
- this.Utils.video.dispatchEvent(
- new this.window.CustomEvent("MozNoControlsVideoBindingAttached")
- );
}
- elementStateMatches(element) {
- return true;
- }
+ onchange() {}
teardown() {
this.Utils.terminate();
@@ -3135,9 +3111,7 @@ this.NoControlsPictureInPictureImplWidget = class {
this.shadowRoot.firstElementChild.setAttribute("localedir", direction);
}
- elementStateMatches(element) {
- return true;
- }
+ onchange() {}
teardown() {}
@@ -3312,9 +3286,7 @@ this.NoControlsDesktopImplWidget = class {
this.Utils.init(this.shadowRoot, this.prefs);
}
- elementStateMatches(element) {
- return true;
- }
+ onchange() {}
teardown() {
this.Utils.terminate();
diff --git a/toolkit/content/widgets/wizard.js b/toolkit/content/widgets/wizard.js
index 6eb4bcb517..c4285fada5 100644
--- a/toolkit/content/widgets/wizard.js
+++ b/toolkit/content/widgets/wizard.js
@@ -359,7 +359,7 @@
this._wizardButtons.onPageChange();
}
- _advanceFocusToPage(aPage) {
+ _advanceFocusToPage() {
if (!this._hasLoaded) {
return;
}
diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css
index e8635d4525..ead9997295 100644
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -359,17 +359,15 @@ menubar > menu:empty {
/********* menupopup, panel, & tooltip ***********/
menupopup,
-panel {
- flex-direction: column;
-}
-
-menupopup,
panel,
tooltip {
position: fixed;
-moz-top-layer: top;
width: fit-content;
height: fit-content;
+ /* Make sure that popups are interactable when shown, since they escape the
+ * usual layering rules */
+ -moz-inert: none;
/* Popups can't have overflow */
contain: paint;
z-index: 2147483647;
@@ -438,87 +436,6 @@ tooltip:not([position]) {
}
}
-@media (-moz-panel-animations) and (prefers-reduced-motion: no-preference) {
-@media (-moz-platform: macos) {
- /* On Mac, use the properties "-moz-window-transform" and "-moz-window-opacity"
- instead of "transform" and "opacity" for these animations.
- The -moz-window* properties apply to the whole window including the window's
- shadow, and they don't affect the window's "shape", so the system doesn't
- have to recompute the shadow shape during the animation. This makes them a
- lot faster. In fact, Gecko no longer triggers shadow shape recomputations
- for repaints.
- These properties are not implemented on other platforms. */
- panel[type="arrow"]:not([animate="false"]) {
- transition-property: -moz-window-transform, -moz-window-opacity;
- transition-duration: 0.18s, 0.18s;
- transition-timing-function:
- var(--animation-easing-function), ease-out;
- }
-
- /* Only do the fade-in animation on pre-Big Sur to avoid missing shadows on
- * Big Sur, see bug 1672091. */
- @media (-moz-mac-big-sur-theme: 0) {
- panel[type="arrow"]:not([animate="false"]) {
- -moz-window-opacity: 0;
- -moz-window-transform: translateY(-70px);
- }
-
- panel[type="arrow"][side="bottom"]:not([animate="false"]) {
- -moz-window-transform: translateY(70px);
- }
- }
-
- /* [animate] is here only so that this rule has greater specificity than the
- * rule right above */
- panel[type="arrow"][animate][animate="open"] {
- -moz-window-opacity: 1.0;
- transition-duration: 0.18s, 0.18s;
- -moz-window-transform: none;
- transition-timing-function:
- var(--animation-easing-function), ease-in-out;
- }
-
- panel[type="arrow"][animate][animate="cancel"] {
- -moz-window-opacity: 0;
- -moz-window-transform: none;
- }
-} /* end of macOS rules */
-
-@media not (-moz-platform: macos) {
- panel[type="arrow"]:not([animate="false"]) {
- opacity: 0;
- transform: translateY(-70px);
- transition-property: transform, opacity;
- transition-duration: 0.18s, 0.18s;
- transition-timing-function:
- var(--animation-easing-function), ease-out;
- will-change: transform, opacity;
- }
-
- panel[type="arrow"][side="bottom"]:not([animate="false"]) {
- transform: translateY(70px);
- }
-
- /* [animate] is here only so that this rule has greater specificity than the
- * rule right above */
- panel[type="arrow"][animate][animate="open"] {
- opacity: 1.0;
- transition-duration: 0.18s, 0.18s;
- transform: none;
- transition-timing-function:
- var(--animation-easing-function), ease-in-out;
- }
-
- panel[type="arrow"][animate][animate="cancel"] {
- transform: none;
- }
-} /* end of non-macOS rules */
-}
-
-panel[type="arrow"][animating] {
- pointer-events: none;
-}
-
/******** tree ******/
treecolpicker {