Add support for dismissing comment popups with click outside

This solves [bug 1989406](https://bugzilla.mozilla.org/show_bug.cgi?id=1989406).
(“The user should be able to dismiss the in-content message displayed by clicking somewhere else in the PDF”)
There’s a good gif there that shows the problematic behavior.

In the thread, there are also mentions of 2 similar but slightly separate problems:

* clicking on another highlight should also dismiss
* the mention that hitting the escape key does not dismiss

I found the last point, the escape key, to work already (first test case here).
But this PR solves the main bug (second test case) and the adjacent one
(third test case).
It works by using the existing `unselectAll` handling.
This commit is contained in:
Titus Wormer 2026-03-02 15:29:16 +01:00
parent d859d78bb5
commit 8b4f9048cf
No known key found for this signature in database
GPG Key ID: E6E581152ED04E2E
3 changed files with 92 additions and 0 deletions

View File

@ -901,6 +901,7 @@ class AnnotationEditorLayer {
const currentMode = this.#uiManager.getMode();
if (
currentMode === AnnotationEditorType.STAMP ||
currentMode === AnnotationEditorType.POPUP ||
currentMode === AnnotationEditorType.SIGNATURE
) {
this.#uiManager.unselectAll();

View File

@ -2393,6 +2393,7 @@ class AnnotationEditorUIManager {
ed.unselect();
}
}
this.#commentManager?.destroyPopup();
this.#selectedEditors.clear();
this.#selectedEditors.add(editor);
@ -2583,6 +2584,8 @@ class AnnotationEditorUIManager {
return;
}
this.#commentManager?.destroyPopup();
if (!this.hasSelection) {
return;
}

View File

@ -21,6 +21,7 @@ import {
dragAndDrop,
getEditorSelector,
getRect,
getSpanRectFromText,
highlightSpan,
kbModifierDown,
kbModifierUp,
@ -1176,4 +1177,91 @@ describe("Comment", () => {
);
});
});
describe("Must close comment popups (bug 1989406)", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
"page-fit",
null,
{ enableComment: true }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must close a comment popup on escape", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await highlightSpan(page, 1, "Abstract");
await editComment(page, getEditorSelector(0), "hi");
const rect = await getSpanRectFromText(page, 1, "Introduction");
// Unfocus.
await page.mouse.click(rect.x, rect.y);
await waitAndClick(page, ".annotationCommentButton");
await page.waitForSelector("#commentPopup", { visible: true });
await page.keyboard.press("Escape");
await page.waitForSelector("#commentPopup", { hidden: true });
})
);
});
it("must close a comment popup on click outside", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await highlightSpan(page, 1, "Abstract");
await editComment(page, getEditorSelector(0), "hi");
const rect = await getSpanRectFromText(page, 1, "Introduction");
// Unfocus.
await page.mouse.click(rect.x, rect.y);
await waitAndClick(page, ".annotationCommentButton");
await page.waitForSelector("#commentPopup", { visible: true });
// Click outside the popup.
await page.mouse.click(rect.x, rect.y);
await page.waitForSelector("#commentPopup", { hidden: true });
})
);
});
it("must close a comment popup on click on other highlight", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await highlightSpan(page, 1, "Abstract");
await editComment(page, getEditorSelector(0), "hello");
await highlightSpan(page, 1, "Introduction");
await editComment(page, getEditorSelector(1), "world");
// Open "Abstract" comment popup.
await waitAndClick(page, ".annotationCommentButton");
await page.waitForSelector("#commentPopup", { visible: true });
// Click on "Introduction" highlight.
await waitAndClick(page, getEditorSelector(1));
await page.waitForSelector("#commentPopup", { hidden: true });
})
);
});
});
});