From e3fbe2908aa823a47950a631163e26132b7f117a Mon Sep 17 00:00:00 2001
From: Calixte Denizet <calixte.denizet@gmail.com>
Date: Mon, 2 Oct 2023 20:30:27 +0200
Subject: [PATCH] [Editor] Use the alt text to descibe the canvas used to
 display the image

---
 src/display/editor/editor.js          | 13 ++++++++++
 src/display/editor/stamp.js           | 13 ++++++++++
 test/integration/stamp_editor_spec.js | 36 ++++++++++++++++++++++++---
 3 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js
index a0dc2477d..ea15eb434 100644
--- a/src/display/editor/editor.js
+++ b/src/display/editor/editor.js
@@ -927,6 +927,9 @@ class AnnotationEditor {
     if (!tooltip.parentNode) {
       button.append(tooltip);
     }
+
+    const element = this.getImageForAltText();
+    element?.setAttribute("aria-describedby", tooltip.id);
   }
 
   #toggleAltTextButton(enabled = false) {
@@ -960,6 +963,9 @@ class AnnotationEditor {
     };
   }
 
+  /**
+   * Set the alt text data.
+   */
   set altTextData({ altText, decorative }) {
     if (this.#altText === altText && this.#altTextDecorative === decorative) {
       return;
@@ -1369,6 +1375,13 @@ class AnnotationEditor {
    */
   enterInEditMode() {}
 
+  /**
+   * @returns {HTMLElement | null} the element requiring an alt text.
+   */
+  getImageForAltText() {
+    return null;
+  }
+
   /**
    * Get the div which really contains the displayed content.
    * @returns {HTMLDivElement | undefined}
diff --git a/src/display/editor/stamp.js b/src/display/editor/stamp.js
index 9974527f9..c99734639 100644
--- a/src/display/editor/stamp.js
+++ b/src/display/editor/stamp.js
@@ -32,6 +32,8 @@ class StampEditor extends AnnotationEditor {
 
   #bitmapFile = null;
 
+  #bitmapFileName = "";
+
   #canvas = null;
 
   #observer = null;
@@ -104,6 +106,9 @@ class StampEditor extends AnnotationEditor {
       this.#bitmapId = data.id;
       this.#isSvg = data.isSvg;
     }
+    if (data.file) {
+      this.#bitmapFileName = data.file.name;
+    }
     this.#createCanvas();
   }
 
@@ -332,6 +337,9 @@ class StampEditor extends AnnotationEditor {
       },
     });
     this.addAltTextButton();
+    if (this.#bitmapFileName) {
+      canvas.setAttribute("aria-label", this.#bitmapFileName);
+    }
   }
 
   /**
@@ -438,6 +446,11 @@ class StampEditor extends AnnotationEditor {
     );
   }
 
+  /** @inheritdoc */
+  getImageForAltText() {
+    return this.#canvas;
+  }
+
   #serializeBitmap(toUrl) {
     if (toUrl) {
       if (this.#isSvg) {
diff --git a/test/integration/stamp_editor_spec.js b/test/integration/stamp_editor_spec.js
index 5d6ef19f4..9b79b1863 100644
--- a/test/integration/stamp_editor_spec.js
+++ b/test/integration/stamp_editor_spec.js
@@ -249,9 +249,34 @@ describe("Stamp Editor", () => {
             ]);
           }, data);
 
-          await page.keyboard.down("Control");
-          await page.keyboard.press("v");
-          await page.keyboard.up("Control");
+          let hasPasteEvent = false;
+          while (!hasPasteEvent) {
+            // We retry to paste if nothing has been pasted before 500ms.
+            const promise = Promise.race([
+              page.evaluate(
+                () =>
+                  new Promise(resolve => {
+                    document.addEventListener(
+                      "paste",
+                      e => resolve(e.clipboardData.items.length !== 0),
+                      {
+                        once: true,
+                      }
+                    );
+                  })
+              ),
+              page.evaluate(
+                () =>
+                  new Promise(resolve => {
+                    setTimeout(() => resolve(false), 500);
+                  })
+              ),
+            ]);
+            await page.keyboard.down("Control");
+            await page.keyboard.press("v");
+            await page.keyboard.up("Control");
+            hasPasteEvent = await promise;
+          }
 
           await waitForImage(page, getEditorSelector(0));
 
@@ -277,6 +302,11 @@ describe("Stamp Editor", () => {
           const saveButtonSelector = "#altTextDialog #altTextSave";
           await page.click(saveButtonSelector);
 
+          // Check that the canvas has an aria-describedby attribute.
+          await page.waitForSelector(
+            `${getEditorSelector(0)} canvas[aria-describedby]`
+          );
+
           // Wait for the alt-text button to have the correct icon.
           await page.waitForSelector(`${buttonSelector}:not([hidden]).done`);