From 9babc144a7c492f5535d0f49c0b4b0692b2079e7 Mon Sep 17 00:00:00 2001 From: Aditi Date: Mon, 4 Aug 2025 15:29:52 +0530 Subject: [PATCH] Make horizontal padding relative to device width The fixed -400px horizontal offset used by scrollIntoView led to horizontal scroll only moving part-way right on narrow screens. The highlights near the right-edge remained party or completely off screen. This centres the highlighted match on any viewport width while clamping the left margin to 20-400px. On very narrow screens the scrollbar now moves all the way to the right instead of stopping midway. --- test/integration/find_spec.mjs | 28 +++++++++++++++++ test/integration/viewer_spec.mjs | 53 ++++++++++++++++++++++++++++++++ web/pdf_find_controller.js | 4 +-- web/ui_utils.js | 12 +++++++- 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/test/integration/find_spec.mjs b/test/integration/find_spec.mjs index cfcd13635..91ea589cc 100644 --- a/test/integration/find_spec.mjs +++ b/test/integration/find_spec.mjs @@ -149,4 +149,32 @@ describe("find bar", () => { ); }); }); + + describe("scrolls to the search result text for smaller viewports", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".textLayer", 100); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must scroll to the search result text", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + // Set a smaller viewport to simulate a mobile device + await page.setViewport({ width: 350, height: 600 }); + await page.click("#viewFindButton"); + await page.waitForSelector("#findInput", { visible: true }); + await page.type("#findInput", "productivity"); + + const highlight = await page.waitForSelector(".textLayer .highlight"); + + expect(await highlight.isIntersectingViewport()).toBeTrue(); + }) + ); + }); + }); }); diff --git a/test/integration/viewer_spec.mjs b/test/integration/viewer_spec.mjs index e0c6937bb..5ca008e4c 100644 --- a/test/integration/viewer_spec.mjs +++ b/test/integration/viewer_spec.mjs @@ -1355,4 +1355,57 @@ describe("PDF viewer", () => { ); }); }); + + describe("Pinch-zoom", () => { + let pages; + beforeEach(async () => { + pages = await loadAndWait( + "tracemonkey.pdf", + `.page[data-page-number = "1"] .endOfContent` + ); + }); + it("keeps the content under the pinch centre fixed on the screen", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + if (browserName === "firefox") { + // Firefox does not support touch events on devices + // with no touch screen. + return; + } + + const rect = await getSpanRectFromText(page, 1, "type-stable"); + const originX = rect.x + rect.width / 2; + const originY = rect.y + rect.height / 2; + const rendered = await createPromise(page, resolve => { + const cb = e => { + if (e.pageNumber === 1) { + window.PDFViewerApplication.eventBus.off( + "textlayerrendered", + cb + ); + resolve(); + } + }; + window.PDFViewerApplication.eventBus.on("textlayerrendered", cb); + }); + const client = await page.target().createCDPSession(); + await client.send("Input.synthesizePinchGesture", { + x: originX, + y: originY, + scaleFactor: 3, + gestureSourceType: "touch", + }); + await awaitPromise(rendered); + const spanHandle = await page.evaluateHandle(() => + Array.from( + document.querySelectorAll( + '.page[data-page-number="1"] .textLayer span' + ) + ).find(span => span.textContent.includes("type-stable")) + ); + expect(await spanHandle.isIntersectingViewport()).toBeTrue(); + }) + ); + }); + }); }); diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index 4d0eb3a95..19abf3fca 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -29,7 +29,6 @@ const FindState = { const FIND_TIMEOUT = 250; // ms const MATCH_SCROLL_OFFSET_TOP = -50; // px -const MATCH_SCROLL_OFFSET_LEFT = -400; // px const CHARACTERS_TO_NORMALIZE = { "\u2010": "-", // Hyphen @@ -573,10 +572,9 @@ class PDFFindController { return; } this._scrollMatches = false; // Ensure that scrolling only happens once. - const spot = { top: MATCH_SCROLL_OFFSET_TOP, - left: selectedLeft + MATCH_SCROLL_OFFSET_LEFT, + left: selectedLeft, }; scrollIntoView(element, spot, /* scrollMatches = */ true); } diff --git a/web/ui_utils.js b/web/ui_utils.js index 99c4ff58a..ac4bcf17b 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -120,7 +120,17 @@ function scrollIntoView(element, spot, scrollMatches = false) { offsetY += spot.top; } if (spot.left !== undefined) { - offsetX += spot.left; + if (scrollMatches) { + const elementWidth = element.getBoundingClientRect().width; + const padding = MathClamp( + (parent.clientWidth - elementWidth) / 2, + 20, + 400 + ); + offsetX += spot.left - padding; + } else { + offsetX += spot.left; + } parent.scrollLeft = offsetX; } }