diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index cba633a0e..fd9c67023 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -1013,7 +1013,7 @@ class WidgetAnnotationElement extends AnnotationElement { return (isWin && event.ctrlKey) || (isMac && event.metaKey); } - _setEventListener(element, baseName, eventName, valueGetter) { + _setEventListener(element, elementData, baseName, eventName, valueGetter) { if (baseName.includes("mouse")) { // Mouse events element.addEventListener(baseName, event => { @@ -1029,8 +1029,24 @@ class WidgetAnnotationElement extends AnnotationElement { }); }); } else { - // Non mouse event + // Non-mouse events element.addEventListener(baseName, event => { + if (baseName === "blur") { + if (!elementData.focused || !event.relatedTarget) { + return; + } + elementData.focused = false; + } else if (baseName === "focus") { + if (elementData.focused) { + return; + } + elementData.focused = true; + } + + if (!valueGetter) { + return; + } + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { source: this, detail: { @@ -1043,10 +1059,25 @@ class WidgetAnnotationElement extends AnnotationElement { } } - _setEventListeners(element, names, getter) { + _setEventListeners(element, elementData, names, getter) { for (const [baseName, eventName] of names) { if (eventName === "Action" || this.data.actions?.[eventName]) { - this._setEventListener(element, baseName, eventName, getter); + if (eventName === "Focus" || eventName === "Blur") { + elementData ||= { focused: false }; + } + this._setEventListener( + element, + elementData, + baseName, + eventName, + getter + ); + if (eventName === "Focus" && !this.data.actions?.Blur) { + // Ensure that elementData will have the correct value. + this._setEventListener(element, elementData, "blur", "Blur", null); + } else if (eventName === "Blur" && !this.data.actions?.Focus) { + this._setEventListener(element, elementData, "focus", "Focus", null); + } } } } @@ -1178,6 +1209,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { formattedValue: fieldFormattedValues, lastCommittedValue: null, commitKey: 1, + focused: false, }; if (this.data.multiLine) { @@ -1238,12 +1270,16 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { if (this.enableScripting && this.hasJSActions) { element.addEventListener("focus", event => { + if (elementData.focused) { + return; + } const { target } = event; if (elementData.userValue) { target.value = elementData.userValue; } elementData.lastCommittedValue = target.value; elementData.commitKey = 1; + elementData.focused = true; }); element.addEventListener("updatefromsandbox", jsEvent => { @@ -1349,9 +1385,10 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { const _blurListener = blurListener; blurListener = null; element.addEventListener("blur", event => { - if (!event.relatedTarget) { + if (!elementData.focused || !event.relatedTarget) { return; } + elementData.focused = false; const { value } = event.target; elementData.userValue = value; if (elementData.lastCommittedValue !== value) { @@ -1431,6 +1468,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { this._setEventListeners( element, + elementData, [ ["focus", "Focus"], ["blur", "Blur"], @@ -1540,6 +1578,7 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { this._setEventListeners( element, + null, [ ["change", "Validate"], ["change", "Action"], @@ -1630,6 +1669,7 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { this._setEventListeners( element, + null, [ ["change", "Validate"], ["change", "Action"], @@ -1894,6 +1934,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { this._setEventListeners( selectElement, + null, [ ["focus", "Focus"], ["blur", "Blur"], diff --git a/test/integration/scripting_spec.js b/test/integration/scripting_spec.js index cfaef62e2..7680ec9de 100644 --- a/test/integration/scripting_spec.js +++ b/test/integration/scripting_spec.js @@ -2154,4 +2154,50 @@ describe("Interaction", () => { ); }); }); + + describe("Textfields and focus", () => { + let pages; + let otherPages; + + beforeAll(async () => { + otherPages = await Promise.all( + global.integrationSessions.map(async session => + session.browser.newPage() + ) + ); + pages = await loadAndWait("evaljs.pdf", getSelector("55R")); + }); + + afterAll(async () => { + await closePages(pages); + await Promise.all(otherPages.map(page => page.close())); + }); + + it("must check that focus/blur callbacks aren't called", async () => { + await Promise.all( + pages.map(async ([browserName, page], i) => { + await page.waitForFunction( + "window.PDFViewerApplication.scriptingReady === true" + ); + + await page.click(getSelector("55R")); + await page.type(getSelector("55R"), "Hello", { delay: 10 }); + await page.click(getSelector("56R")); + await page.waitForTimeout(10); + + await page.click(getSelector("55R")); + await page.type(getSelector("55R"), " World", { delay: 10 }); + await page.waitForTimeout(10); + + await otherPages[i].bringToFront(); + await otherPages[i].waitForTimeout(100); + await page.bringToFront(); + await page.waitForTimeout(100); + + const text = await page.$eval(getSelector("55R"), el => el.value); + expect(text).withContext(`In ${browserName}`).toEqual("Hello World"); + }) + ); + }); + }); });