summaryrefslogtreecommitdiffstats
path: root/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs')
-rw-r--r--toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs172
1 files changed, 172 insertions, 0 deletions
diff --git a/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs b/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs
new file mode 100644
index 0000000000..0ddd594cb6
--- /dev/null
+++ b/toolkit/components/tooltiptext/TooltipTextProvider.sys.mjs
@@ -0,0 +1,172 @@
+/* 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/. */
+
+export function TooltipTextProvider() {}
+
+TooltipTextProvider.prototype = {
+ getNodeText(tipElement, textOut, directionOut) {
+ // Don't show the tooltip if the tooltip node is a document or browser.
+ // Caller should ensure the node is in (composed) document.
+ if (
+ !tipElement ||
+ !tipElement.ownerDocument ||
+ tipElement.localName == "browser"
+ ) {
+ return false;
+ }
+
+ var defView = tipElement.ownerGlobal;
+ // XXX Work around bug 350679:
+ // "Tooltips can be fired in documents with no view".
+ if (!defView) {
+ return false;
+ }
+
+ const XLinkNS = "http://www.w3.org/1999/xlink";
+ const XUL_NS =
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ 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;
+ }
+
+ // If the element is an <input type='file'> 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) {}
+ }
+
+ // 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 (
+ (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;
+ }
+ }
+ }
+
+ usedTipElement = tipElement;
+ }
+
+ tipElement = tipElement.flattenedTreeParentNode;
+ }
+
+ 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");
+ }
+
+ directionOut.value = direction;
+ return true;
+ }
+
+ return false;
+ }
+ );
+ },
+
+ classID: Components.ID("{f376627f-0bbc-47b8-887e-fc92574cc91f}"),
+ QueryInterface: ChromeUtils.generateQI(["nsITooltipTextProvider"]),
+};