Merge pull request #21666 from calixteman/bug2054348

Fix the selection rendering when a page has been destroyed and rendered again (bug 2054348)
This commit is contained in:
Tim van der Meij 2026-07-30 21:12:47 +02:00 committed by GitHub
commit a80897dc9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 103 additions and 2 deletions

View File

@ -641,7 +641,8 @@ class DrawLayer {
textLayerData.selectionDiv = div;
}
if (!div.parentNode && drawLayer.#parent) {
if (drawLayer.#parent && div.parentNode !== drawLayer.#parent) {
// The div can still be in a canvas wrapper which has been removed.
drawLayer.#parent.append(div);
this.#selections.add(div);
}

View File

@ -20,9 +20,11 @@
import {
closePages,
closeSinglePage,
firstPageOnTop,
getSpanRectFromText,
kbSelectAll,
loadAndWait,
scrollIntoView,
waitForEvent,
} from "./test_utils.mjs";
import { MathClamp } from "../../src/shared/math_clamp.js";
@ -1148,6 +1150,94 @@ describe("Text layer", () => {
);
});
});
describe("when the page has been destroyed and rendered again", () => {
const selectionSelector = ".canvasWrapper .selection svg path[d]";
const timeout = 5000;
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
`.page[data-page-number = "1"] .endOfContent`,
undefined,
undefined,
{ annotationEditorMode: -1 }
);
});
afterEach(async () => {
await closePages(pages);
});
async function selectSomeText(page) {
const [positionStart, positionEnd] = await Promise.all([
getSpanRectFromText(
page,
1,
"(frequently executed) bytecode sequences, records"
).then(middlePosition),
getSpanRectFromText(
page,
1,
"them, and compiles them to fast native code. We call such a se-"
).then(belowEndPosition),
]);
await page.mouse.move(positionStart.x, positionStart.y);
await page.mouse.down();
await moveInSteps(page, positionStart, positionEnd, 20);
await page.mouse.up();
}
it("must draw the selection", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await selectSomeText(page);
await page.waitForSelector(selectionSelector, { timeout });
await page.evaluate(() => {
document.getSelection().removeAllRanges();
});
await page.waitForSelector(selectionSelector, {
hidden: true,
timeout,
});
// Scroll down, page by page, until the first page view is
// evicted from the buffer: its canvas wrapper is then removed.
const pagesCount = await page.evaluate(
() => window.PDFViewerApplication.pagesCount
);
let isDestroyed = false;
for (let i = 2; i <= pagesCount && !isDestroyed; i++) {
const selector = `.page[data-page-number = "${i}"]`;
await scrollIntoView(page, selector);
await page.waitForSelector(
`${selector} .canvasWrapper canvas`,
{ timeout: 0 }
);
isDestroyed = !(await page.$(
`.page[data-page-number = "1"] .canvasWrapper`
));
}
expect(isDestroyed)
.withContext(`In ${browserName}, first page destroyed`)
.toBeTrue();
await firstPageOnTop(page);
await page.waitForSelector(
`.page[data-page-number = "1"] .canvasWrapper canvas`,
{ timeout: 0 }
);
await selectSomeText(page);
await page.waitForSelector(selectionSelector, { timeout });
})
);
});
});
});
describe("using selection carets", () => {

View File

@ -370,6 +370,7 @@ const PDFViewerApplication = {
// Set some specific preferences for tests.
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
Object.assign(opts, {
annotationEditorMode: x => parseInt(x, 10),
capCanvasAreaFactor: x => parseInt(x, 10),
docBaseUrl: x => x,
enableAltText: x => x === "true",

View File

@ -936,9 +936,14 @@ class PDFPageView extends BasePDFPageView {
if (this.structTreeLayer && !this.textLayer) {
this.structTreeLayer = null;
}
// The annotation editor layer and the draw layer keep references on the
// text layer (the latter uses its div in order to render the selection),
// hence they must be recreated too.
if (
this.annotationEditorLayer &&
(!keepAnnotationEditorLayer || !this.annotationEditorLayer.div)
(!keepAnnotationEditorLayer ||
!this.annotationEditorLayer.div ||
!this.textLayer)
) {
if (this.drawLayer) {
this.drawLayer.cancel();
@ -947,6 +952,10 @@ class PDFPageView extends BasePDFPageView {
this.annotationEditorLayer.cancel();
this.annotationEditorLayer = null;
}
if (this.drawLayer && !this.textLayer) {
this.drawLayer.cancel();
this.drawLayer = null;
}
if (this.xfaLayer && (!keepXfaLayer || !this.xfaLayer.div)) {
this.xfaLayer.cancel();
this.xfaLayer = null;