From 086c044dc34dfc0f74fbe41f4ecb402b2cd34884 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:33 +0200 Subject: Merging upstream version 125.0.1. Signed-off-by: Daniel Baumann --- .../tooltiptext/TooltipTextProvider.sys.mjs | 227 ++++++++++----------- 1 file changed, 107 insertions(+), 120 deletions(-) (limited to 'toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs') diff --git a/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs b/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs index 0ddd594cb6..96a3e8dd5f 100644 --- a/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs +++ b/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs @@ -4,6 +4,37 @@ export function TooltipTextProvider() {} +function getFileInputTitleText(tipElement) { + let files = tipElement.files; + let bundle = Services.strings.createBundle( + "chrome://global/locale/layout/HtmlForm.properties" + ); + if (!files.length) { + return bundle.GetStringFromName( + tipElement.multiple ? "NoFilesSelected" : "NoFileSelected" + ); + } + let titleText = files[0].name; + // For UX and performance (jank) reasons we cap the number of + // files that we list in the tooltip to 20 plus a "and xxx more" + // line, or to 21 if exactly 21 files were picked. + const TRUNCATED_FILE_COUNT = 20; + let count = Math.min(files.length, TRUNCATED_FILE_COUNT); + for (let i = 1; i < count; ++i) { + titleText += "\n" + files[i].name; + } + if (files.length == TRUNCATED_FILE_COUNT + 1) { + titleText += "\n" + files[TRUNCATED_FILE_COUNT].name; + } else if (files.length > TRUNCATED_FILE_COUNT + 1) { + const l10n = new Localization(["toolkit/global/htmlForm.ftl"], true); + const andXMoreStr = l10n.formatValueSync("input-file-and-more-files", { + fileCount: files.length - TRUNCATED_FILE_COUNT, + }); + titleText += "\n" + andXMoreStr; + } + return titleText; +} + TooltipTextProvider.prototype = { getNodeText(tipElement, textOut, directionOut) { // Don't show the tooltip if the tooltip node is a document or browser. @@ -29,142 +60,98 @@ TooltipTextProvider.prototype = { var titleText = null; var XLinkTitleText = null; - var SVGTitleText = null; - var XULtooltiptextText = null; var lookingForSVGTitle = true; var direction = tipElement.ownerDocument.dir; - // If the element is invalid per HTML5 Forms specifications and has no title, - // show the constraint validation error message. - if ( - (defView.HTMLInputElement.isInstance(tipElement) || - defView.HTMLTextAreaElement.isInstance(tipElement) || - defView.HTMLSelectElement.isInstance(tipElement) || - defView.HTMLButtonElement.isInstance(tipElement)) && - !tipElement.hasAttribute("title") && - (!tipElement.form || !tipElement.form.noValidate) - ) { - // If the element is barred from constraint validation or valid, - // the validation message will be the empty string. - titleText = tipElement.validationMessage || null; - } + for (; tipElement; tipElement = tipElement.flattenedTreeParentNode) { + if (tipElement.nodeType != defView.Node.ELEMENT_NODE) { + continue; + } + if (tipElement.namespaceURI == XUL_NS) { + lookingForSVGTitle = false; + // NOTE: getAttribute behaves differently for XUL so we can't rely on + // it returning null, see bug 232598. + titleText = tipElement.hasAttribute("tooltiptext") + ? tipElement.getAttribute("tooltiptext") + : null; + } else if (!defView.SVGElement.isInstance(tipElement)) { + lookingForSVGTitle = false; + titleText = tipElement.getAttribute("title"); + } - // If the element is an without a title, we should show - // the current file selection. - if ( - !titleText && - defView.HTMLInputElement.isInstance(tipElement) && - tipElement.type == "file" && - !tipElement.hasAttribute("title") - ) { - let files = tipElement.files; - - try { - var bundle = Services.strings.createBundle( - "chrome://global/locale/layout/HtmlForm.properties" - ); - if (!files.length) { - if (tipElement.multiple) { - titleText = bundle.GetStringFromName("NoFilesSelected"); - } else { - titleText = bundle.GetStringFromName("NoFileSelected"); - } - } else { - titleText = files[0].name; - // For UX and performance (jank) reasons we cap the number of - // files that we list in the tooltip to 20 plus a "and xxx more" - // line, or to 21 if exactly 21 files were picked. - const TRUNCATED_FILE_COUNT = 20; - let count = Math.min(files.length, TRUNCATED_FILE_COUNT); - for (let i = 1; i < count; ++i) { - titleText += "\n" + files[i].name; - } - if (files.length == TRUNCATED_FILE_COUNT + 1) { - titleText += "\n" + files[TRUNCATED_FILE_COUNT].name; - } else if (files.length > TRUNCATED_FILE_COUNT + 1) { - const l10n = new Localization( - ["toolkit/global/htmlForm.ftl"], - true - ); - const andXMoreStr = l10n.formatValueSync( - "input-file-and-more-files", - { fileCount: files.length - TRUNCATED_FILE_COUNT } - ); - titleText += "\n" + andXMoreStr; - } - } - } catch (e) {} - } + if ( + (defView.HTMLAnchorElement.isInstance(tipElement) || + defView.HTMLAreaElement.isInstance(tipElement) || + defView.HTMLLinkElement.isInstance(tipElement) || + defView.SVGAElement.isInstance(tipElement)) && + tipElement.href + ) { + XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title"); + } - // Check texts against null so that title="" can be used to undefine a - // title on a child element. - let usedTipElement = null; - while ( - tipElement && - titleText == null && - XLinkTitleText == null && - SVGTitleText == null && - XULtooltiptextText == null - ) { - if (tipElement.nodeType == defView.Node.ELEMENT_NODE) { - if (tipElement.namespaceURI == XUL_NS) { - XULtooltiptextText = tipElement.hasAttribute("tooltiptext") - ? tipElement.getAttribute("tooltiptext") - : null; - } else if (!defView.SVGElement.isInstance(tipElement)) { - titleText = tipElement.getAttribute("title"); - } + // If the element is invalid per HTML5 Forms specifications and has no title, + // show the constraint validation error message. + if ( + titleText == null && + (defView.HTMLInputElement.isInstance(tipElement) || + defView.HTMLTextAreaElement.isInstance(tipElement) || + defView.HTMLSelectElement.isInstance(tipElement) || + defView.HTMLButtonElement.isInstance(tipElement)) && + !tipElement.form?.noValidate + ) { + // If the element is barred from constraint validation or valid, + // the validation message will be the empty string. + titleText = tipElement.validationMessage || null; + } - if ( - (defView.HTMLAnchorElement.isInstance(tipElement) || - defView.HTMLAreaElement.isInstance(tipElement) || - defView.HTMLLinkElement.isInstance(tipElement) || - defView.SVGAElement.isInstance(tipElement)) && - tipElement.href - ) { - XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title"); - } - if ( - lookingForSVGTitle && - (!defView.SVGElement.isInstance(tipElement) || - tipElement.parentNode.nodeType == defView.Node.DOCUMENT_NODE) - ) { - lookingForSVGTitle = false; - } - if (lookingForSVGTitle) { - for (let childNode of tipElement.childNodes) { - if (defView.SVGTitleElement.isInstance(childNode)) { - SVGTitleText = childNode.textContent; - break; - } + // If the element is an without a title, we should show + // the current file selection. + if ( + titleText == null && + defView.HTMLInputElement.isInstance(tipElement) && + tipElement.type == "file" + ) { + try { + titleText = getFileInputTitleText(tipElement); + } catch (ex) {} + } + + if ( + lookingForSVGTitle && + tipElement.parentNode.nodeType != defView.Node.DOCUMENT_NODE + ) { + for (let childNode of tipElement.childNodes) { + if (defView.SVGTitleElement.isInstance(childNode)) { + titleText = childNode.textContent; + break; } } - - usedTipElement = tipElement; } - tipElement = tipElement.flattenedTreeParentNode; + // Check texts against null so that title="" can be used to undefine a + // title on a child element. + if (titleText != null || XLinkTitleText != null) { + break; + } } - return [titleText, XLinkTitleText, SVGTitleText, XULtooltiptextText].some( - function (t) { - if (t && /\S/.test(t)) { - // Make CRLF and CR render one line break each. - textOut.value = t.replace(/\r\n?/g, "\n"); - - if (usedTipElement) { - direction = defView - .getComputedStyle(usedTipElement) - .getPropertyValue("direction"); - } + return [titleText, XLinkTitleText].some(function (t) { + if (t && /\S/.test(t)) { + // Make CRLF and CR render one line break each. + textOut.value = t.replace(/\r\n?/g, "\n"); - directionOut.value = direction; - return true; + if (tipElement) { + direction = defView + .getComputedStyle(tipElement) + .getPropertyValue("direction"); } - return false; + directionOut.value = direction; + return true; } - ); + + return false; + }); }, classID: Components.ID("{f376627f-0bbc-47b8-887e-fc92574cc91f}"), -- cgit v1.2.3