diff --git a/test/integration/viewer_spec.mjs b/test/integration/viewer_spec.mjs index b3b299d74..2a319017e 100644 --- a/test/integration/viewer_spec.mjs +++ b/test/integration/viewer_spec.mjs @@ -1359,6 +1359,36 @@ describe("PDF viewer", () => { }); }); + describe("File param with a relative URL and a query string (issue 20137)", () => { + let pages; + + beforeEach(async () => { + // The `file` parameter is `/test/pdfs/basicapi.pdf?token=%2Ffoo`. + pages = await loadAndWait( + "basicapi.pdf%3Ftoken%3D%252Ffoo", + ".textLayer .endOfContent" + ); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must not re-encode the file param", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const pdfUrl = await page.evaluate( + () => window.PDFViewerApplication.url + ); + + expect(pdfUrl) + .withContext(`In ${browserName}`) + .toBe("/test/pdfs/basicapi.pdf?token=%2Ffoo"); + }) + ); + }); + }); + describe("Keyboard scrolling on startup (bug 843653)", () => { let pages; diff --git a/web/app.js b/web/app.js index 12af58ebe..77e2a3f85 100644 --- a/web/app.js +++ b/web/app.js @@ -888,11 +888,13 @@ const PDFViewerApplication = { const queryString = document.location.search.substring(1); const params = parseQueryString(queryString); file = params.get("file") ?? AppOptions.get("defaultUrl"); - try { - file = new URL(file).href; - } catch { - file = encodeURIComponent(file).replaceAll("%2F", "/"); - } + // Note that `parseQueryString` has already percent-decoded the parameter, + // hence it's used as-is below: re-encoding it would break URLs with e.g. + // a query string or a percent-encoded path (issue 20137). + // In a relative URL a "#" is assumed to be part of the filename, rather + // than a fragment separator, since the viewer takes its own hash + // parameters from the *viewer* URL (issue 19990). + file = URL.parse(file)?.href ?? file.replaceAll("#", "%23"); validateFileURL(file); } else if (PDFJSDev.test("MOZCENTRAL")) { file = window.location.href;