Commit the current editing session before saving (bug 2047516)

When saving or downloading a document, an annotation that was still
being edited (e.g. an in-progress ink drawing) wasn't committed first,
so its content was missing from the saved file.
This commit is contained in:
Calixte Denizet 2026-07-13 18:57:09 +02:00
parent 5e3272c929
commit 125f692589
3 changed files with 62 additions and 2 deletions

View File

@ -1039,7 +1039,7 @@ class AnnotationEditorUIManager {
}, },
{ capture: true, signal } { capture: true, signal }
); );
window.addEventListener("beforeunload", this.#beforeUnload.bind(this), { window.addEventListener("beforeunload", this.endCurrentEditing.bind(this), {
capture: true, capture: true,
signal, signal,
}); });
@ -1462,7 +1462,7 @@ class AnnotationEditorUIManager {
this.highlightSelection(methodOfCreation, /* comment */ true); this.highlightSelection(methodOfCreation, /* comment */ true);
} }
#beforeUnload(e) { endCurrentEditing() {
this.commitOrRemove(); this.commitOrRemove();
this.currentLayer?.endDrawingSession(/* isAborted = */ false); this.currentLayer?.endDrawingSession(/* isAborted = */ false);
} }

View File

@ -18,6 +18,7 @@ import {
clearEditors, clearEditors,
closePages, closePages,
countStorageEntries, countStorageEntries,
createPromise,
dragAndDrop, dragAndDrop,
getAnnotationSelector, getAnnotationSelector,
getEditors, getEditors,
@ -26,6 +27,7 @@ import {
getSerialized, getSerialized,
isCanvasMonochrome, isCanvasMonochrome,
kbRedo, kbRedo,
kbSave,
kbUndo, kbUndo,
loadAndWait, loadAndWait,
moveEditor, moveEditor,
@ -41,6 +43,7 @@ import {
waitForStorageEntries, waitForStorageEntries,
waitForTimeout, waitForTimeout,
} from "./test_utils.mjs"; } from "./test_utils.mjs";
import { AnnotationEditorType } from "../../src/shared/util.js";
const selectAll = selectEditors.bind(null, "ink"); const selectAll = selectEditors.bind(null, "ink");
@ -1348,3 +1351,59 @@ describe("Ink must committed when leaving the tab", () => {
); );
}); });
}); });
describe("Ink must be committed when the document is saved", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that an in-progress drawing is committed and saved", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToInk(page);
const rect = await getRect(page, ".annotationEditorLayer");
const x = rect.x + 20;
const y = rect.y + 20;
await drawLine(page, x, y, x + 50, y + 50);
// The drawing session is still open, hence nothing has been committed
// to the annotation storage yet.
const count = await countStorageEntries(page);
expect(count).withContext(`In ${browserName}`).toEqual(0);
// `saveDocument` is only called when the annotation storage isn't
// empty. If the in-progress drawing weren't committed first, then
// `downloadOrSave` would take the "download" path and `saveDocument`
// would never run.
const saveDocumentCalled = await createPromise(page, resolve => {
window.PDFViewerApplication.pdfDocument.saveDocument = () =>
resolve();
});
// Save the document with the keyboard shortcut (Ctrl/Cmd+S).
await kbSave(page);
// The in-progress drawing must have been committed to the storage...
await waitForStorageEntries(page, 1);
// ...as an ink annotation...
const serialized = await getSerialized(page);
expect(serialized.length).withContext(`In ${browserName}`).toEqual(1);
expect(serialized[0].annotationType)
.withContext(`In ${browserName}`)
.toEqual(AnnotationEditorType.INK);
// ...and the document must have been saved (and not merely downloaded).
await awaitPromise(saveDocumentCalled);
})
);
});
});

View File

@ -1388,6 +1388,7 @@ const PDFViewerApplication = {
} }
return; return;
} }
this.pdfViewer._layerProperties.annotationEditorUIManager?.endCurrentEditing();
// In the Firefox case, this method MUST always trigger a download. // In the Firefox case, this method MUST always trigger a download.
// When the user is closing a modified and unsaved document, we display a // When the user is closing a modified and unsaved document, we display a