Skip to content

Commit c541d24

Browse files
Merge pull request #21407 from calixteman/fix_hidden_updated_field
Fix form fields with their own canvas updated on non-rendered pages
2 parents 29ad297 + ecd43bc commit c541d24

5 files changed

Lines changed: 119 additions & 17 deletions

File tree

src/display/annotation_layer.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,15 +1321,6 @@ class WidgetAnnotationElement extends AnnotationElement {
13211321
return this.container;
13221322
}
13231323

1324-
showElementAndHideCanvas(element) {
1325-
if (this.data.hasOwnCanvas) {
1326-
if (element.previousSibling?.nodeName === "CANVAS") {
1327-
element.previousSibling.hidden = true;
1328-
}
1329-
element.hidden = false;
1330-
}
1331-
}
1332-
13331324
_getKeyModifier(event) {
13341325
return FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey;
13351326
}
@@ -1547,7 +1538,14 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
15471538
}
15481539
}
15491540
if (this.data.hasOwnCanvas) {
1550-
element.hidden = true;
1541+
// The rendered appearance (a canvas) is shown instead of this element.
1542+
this.container.classList.add("hasOwnCanvas");
1543+
if (storage.has(id)) {
1544+
// Once the field is modified, the `sandboxModified` class hides the
1545+
// (now outdated) canvas and shows this element instead.
1546+
// The field can already have been modified.
1547+
this.container.classList.add("sandboxModified");
1548+
}
15511549
}
15521550
GetElementsByNameSet.add(element);
15531551
this.contentElement = element;
@@ -1637,7 +1635,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
16371635
});
16381636

16391637
element.addEventListener("updatefromsandbox", jsEvent => {
1640-
this.showElementAndHideCanvas(jsEvent.target);
1638+
this.container.classList.add("sandboxModified");
16411639
const actions = {
16421640
value(event) {
16431641
elementData.userValue = event.detail.value ?? "";

test/integration/scripting_spec.mjs

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,31 +1886,38 @@ describe("Interaction", () => {
18861886
const selector = getAnnotationSelector("9R");
18871887
const hasVisibleCanvas = await page.$eval(
18881888
`${selector} > canvas`,
1889-
elem => elem && !elem.hasAttribute("hidden")
1889+
elem => getComputedStyle(elem).display !== "none"
18901890
);
18911891
expect(hasVisibleCanvas)
18921892
.withContext(`In ${browserName}`)
18931893
.toEqual(true);
18941894

1895-
const hasHiddenInput = await page.$eval(`${selector} > input`, elem =>
1896-
elem?.hasAttribute("hidden")
1895+
const hasHiddenInput = await page.$eval(
1896+
`${selector} > input`,
1897+
elem => getComputedStyle(elem).display === "none"
18971898
);
18981899
expect(hasHiddenInput).withContext(`In ${browserName}`).toEqual(true);
18991900

19001901
await page.click(getSelector("12R"));
1901-
await page.waitForSelector(`${selector} > canvas[hidden]`);
1902+
await page.waitForFunction(
1903+
sel =>
1904+
getComputedStyle(document.querySelector(`${sel} > canvas`))
1905+
.display === "none",
1906+
{},
1907+
selector
1908+
);
19021909

19031910
const hasHiddenCanvas = await page.$eval(
19041911
`${selector} > canvas`,
1905-
elem => elem?.hasAttribute("hidden")
1912+
elem => getComputedStyle(elem).display === "none"
19061913
);
19071914
expect(hasHiddenCanvas)
19081915
.withContext(`In ${browserName}`)
19091916
.toEqual(true);
19101917

19111918
const hasVisibleInput = await page.$eval(
19121919
`${selector} > input`,
1913-
elem => elem && !elem.hasAttribute("hidden")
1920+
elem => getComputedStyle(elem).display !== "none"
19141921
);
19151922
expect(hasVisibleInput)
19161923
.withContext(`In ${browserName}`)
@@ -2696,4 +2703,89 @@ describe("Interaction", () => {
26962703
);
26972704
});
26982705
});
2706+
2707+
describe("in text_field_own_canvas_calc.pdf", () => {
2708+
let pages;
2709+
2710+
beforeEach(async () => {
2711+
pages = await loadAndWait(
2712+
"text_field_own_canvas_calc.pdf",
2713+
getSelector("7R"),
2714+
"page-fit"
2715+
);
2716+
});
2717+
2718+
afterEach(async () => {
2719+
await closePages(pages);
2720+
});
2721+
2722+
it("must show the field instead of its canvas when it was calculated while its page wasn't rendered", async () => {
2723+
await Promise.all(
2724+
pages.map(async ([browserName, page]) => {
2725+
// The read-only "Mirror" field (8R) is on page 3, which hasn't been
2726+
// rendered yet.
2727+
expect(await page.$(getSelector("8R")))
2728+
.withContext(`In ${browserName}`)
2729+
.toBeNull();
2730+
2731+
// Modifying the "Source" field (7R) on page 1 mirrors its value into
2732+
// the read-only field on page 3 through a Calculate action.
2733+
await page.type(getSelector("7R"), "Hello PDF.js");
2734+
await page.keyboard.press("Enter");
2735+
await waitForEntryInStorage(
2736+
page,
2737+
"8R",
2738+
{ value: "Hello PDF.js" },
2739+
(stored, expected) =>
2740+
!!stored &&
2741+
JSON.parse(stored).value === JSON.parse(expected).value
2742+
);
2743+
2744+
// The value has been mirrored into the storage while page 3, and
2745+
// hence its annotation layer, hasn't been rendered yet.
2746+
const page3AnnotationCount = await page.evaluate(() => {
2747+
const layer = document.querySelector(
2748+
'.page[data-page-number="3"] .annotationLayer'
2749+
);
2750+
return layer ? layer.childElementCount : 0;
2751+
});
2752+
expect(page3AnnotationCount)
2753+
.withContext(`In ${browserName}`)
2754+
.toEqual(0);
2755+
2756+
// Render page 3.
2757+
await scrollIntoView(page, '.page[data-page-number="3"]');
2758+
const inputPage3Selector = getSelector("8R");
2759+
await page.waitForSelector(
2760+
`.sandboxModified:has(${inputPage3Selector})`,
2761+
{ visible: true }
2762+
);
2763+
2764+
// The field must show its (calculated) value and the now-outdated
2765+
// canvas must be hidden.
2766+
const { value, isFieldVisible, isCanvasHidden } = await page.evaluate(
2767+
sel => {
2768+
const input = document.querySelector(sel);
2769+
const canvas = input
2770+
.closest("section")
2771+
.querySelector("canvas.annotationContent");
2772+
return {
2773+
value: input.value,
2774+
isFieldVisible: getComputedStyle(input).display !== "none",
2775+
isCanvasHidden:
2776+
!!canvas && getComputedStyle(canvas).display === "none",
2777+
};
2778+
},
2779+
inputPage3Selector
2780+
);
2781+
2782+
expect(value)
2783+
.withContext(`In ${browserName}`)
2784+
.toEqual("Hello PDF.js");
2785+
expect(isFieldVisible).withContext(`In ${browserName}`).toBe(true);
2786+
expect(isCanvasHidden).withContext(`In ${browserName}`).toBe(true);
2787+
})
2788+
);
2789+
});
2790+
});
26992791
});

test/pdfs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,3 +931,4 @@
931931
!issue21346.pdf
932932
!cidfont_cmap_overflow.pdf
933933
!jbig2_file_header.pdf
934+
!text_field_own_canvas_calc.pdf
1.75 KB
Binary file not shown.

web/annotation_layer_builder.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,17 @@
138138
}
139139
}
140140

141+
/* A field rendered to its own canvas shows that canvas and keeps its editable
142+
element hidden. Once the field is modified the canvas is outdated, so it's
143+
hidden and the element is shown instead. */
144+
.hasOwnCanvas:not(.sandboxModified) :is(input, textarea) {
145+
display: none;
146+
}
147+
148+
.hasOwnCanvas.sandboxModified canvas.annotationContent {
149+
display: none;
150+
}
151+
141152
.textLayer.selecting ~ & section {
142153
pointer-events: none;
143154
}

0 commit comments

Comments
 (0)