Merge pull request #21261 from calixteman/bug1960363

Add a Content-Security-Policy to pdf.js' viewer.html (bug 1960363)
This commit is contained in:
calixteman 2026-05-18 18:24:38 +02:00 committed by GitHub
commit abb8e31408
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 174 additions and 14 deletions

View File

@ -1214,6 +1214,10 @@ function discardCommentsCSS() {
}
function preprocessHTML(source, defines) {
defines = {
...defines,
TESTING: defines.TESTING ?? process.env.TESTING === "true",
};
const outName = getTempFile("~preprocess", ".html");
preprocess(source, outName, defines);
const out = fs.readFileSync(outName).toString();

View File

@ -26,8 +26,11 @@ import {
waitForPageChanging,
waitForPageRendered,
} from "./test_utils.mjs";
import path from "path";
import { PNG } from "pngjs";
const __dirname = import.meta.dirname;
describe("PDF viewer", () => {
describe("Zoom origin", () => {
let pages;
@ -1900,5 +1903,108 @@ describe("PDF viewer", () => {
);
});
});
describe("@page size stylesheet under CSP", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"basicapi.pdf",
".textLayer .endOfContent",
null,
{
earlySetup: () => {
// Capture state while window.print() runs — the print service's
// destroy() removes the @page stylesheet right after, on the
// afterprint event.
window._pageRuleApplied = null;
window.print = () => {
window._pageRuleApplied = [
...document.querySelectorAll("style"),
].some(
s =>
s.sheet?.cssRules.length > 0 &&
[...s.sheet.cssRules].some(r => r.cssText.includes("@page"))
);
};
},
appSetup: app => {
app._testPrintResolver = Promise.withResolvers();
},
eventBusSetup: eventBus => {
eventBus.on(
"afterprint",
() => {
window.PDFViewerApplication._testPrintResolver.resolve();
},
{ once: true }
);
},
}
);
});
afterEach(async () => {
await closePages(pages);
});
// The print service injects an inline
// <style>@page { size: WxH pt }</style> to match the PDF's page
// dimensions. If the CSP `style-src-elem` directive blocks inline
// <style> elements, the element is created but its content is never
// parsed — `sheet.cssRules` stays empty and the @page rule has no
// effect. See web/viewer.html.
it("must apply the injected @page rule (no CSP block)", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await waitAndClick(page, "#printButton");
await awaitPromise(
await page.evaluateHandle(() => [
window.PDFViewerApplication._testPrintResolver.promise,
])
);
const hasPageRule = await page.evaluate(
() => window._pageRuleApplied
);
expect(hasPageRule)
.withContext(
`In ${browserName}: injected @page stylesheet was parsed`
)
.toBeTrue();
})
);
});
});
});
describe("Open a new PDF via the file input", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".textLayer .endOfContent");
});
afterEach(async () => {
await closePages(pages);
});
// The "Open" input wraps the chosen file in a blob URL,
// which the worker then fetches. `connect-src` in the production CSP must
// therefore allow `blob:` — see web/viewer.html.
it("must load a PDF picked through the file input (blob URL)", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const fileInput = await page.$("#fileInput");
await fileInput.uploadFile(
path.join(__dirname, "../pdfs/basicapi.pdf")
);
await page.waitForFunction(
() => window.PDFViewerApplication.pdfDocument?.numPages === 3
);
})
);
});
});
});

View File

@ -26,6 +26,20 @@ See https://github.com/adobe-type-tools/cmap-resources
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<title>PDF.js viewer</title>
<!--
The print service injects an inline <style>@page { size: }</style>
at print time (web/pdf_print_service.js, web/firefox_print_service.js)
to match the PDF's page dimensions. Since the size varies per PDF the
content can't be pre-hashed, so style-src-elem allows 'unsafe-inline'.
Inline style="…" attributes stay blocked via style-src (no fallback).
-->
<!--#if MOZCENTRAL-->
<!--<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src resource: 'wasm-unsafe-eval'; worker-src resource:; style-src resource:; style-src-elem resource: 'unsafe-inline'; img-src resource: blob: data:; font-src resource:; connect-src resource:; base-uri 'none'; form-action 'none';"
/>-->
<!--#endif-->
<!--#if MOZCENTRAL-->
<!--#include viewer-snippet-firefox-extension.html-->
<!--#endif-->

View File

@ -4,19 +4,7 @@
users with recognizing which checkbox they have to click when they
visit chrome://extensions.
-->
<p
id="chrome-pdfjs-logo-bg"
style="
display: block;
padding-left: 60px;
min-height: 48px;
background-size: 48px;
background-repeat: no-repeat;
font-size: 14px;
line-height: 1.8em;
word-break: break-all;
"
>
<p id="chrome-pdfjs-logo-bg">
Click on "<span id="chrome-file-access-label">Allow access to file URLs</span>" at
<a id="chrome-link-to-extensions-page">chrome://extensions</a>
<br />

View File

@ -713,6 +713,25 @@ dialog :link {
margin-top: 10px;
}
/*#if !MOZCENTRAL*/
#printServiceDialog {
min-width: 200px;
}
/*#endif*/
/*#if CHROME*/
#chrome-pdfjs-logo-bg {
display: block;
padding-left: 60px;
min-height: 48px;
background-size: 48px;
background-repeat: no-repeat;
font-size: 14px;
line-height: 1.8em;
word-break: break-all;
}
/*#endif*/
.grab-to-pan-grab {
cursor: grab !important;
}

View File

@ -29,6 +29,35 @@ See https://github.com/adobe-type-tools/cmap-resources
<!--#endif-->
<title>PDF.js viewer</title>
<!--
The print service injects an inline <style>@page { size: }</style>
at print time (web/pdf_print_service.js, web/firefox_print_service.js)
to match the PDF's page dimensions. Since the size varies per PDF the
content can't be pre-hashed, so style-src-elem allows 'unsafe-inline'.
Inline style="…" attributes stay blocked via style-src (no fallback).
-->
<!--#if MOZCENTRAL-->
<!--<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src resource: 'wasm-unsafe-eval'; worker-src resource:; style-src resource:; style-src-elem resource: 'unsafe-inline'; img-src resource: blob: data:; font-src resource:; connect-src resource:; base-uri 'none'; form-action 'none';"
/>-->
<!--#elif TESTING-->
<!--<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-eval'; worker-src 'self' blob:; style-src 'self'; style-src-elem 'self' 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self' data:; connect-src * blob: data:; base-uri 'self'; form-action 'none';"
/>-->
<!--#elif CHROME-->
<!--<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self' blob:; style-src 'self'; style-src-elem 'self' 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self' data:; connect-src * blob: data:; base-uri 'self'; form-action 'none';"
/>-->
<!--#else-->
<!--<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self' blob:; style-src 'self'; style-src-elem 'self' 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self' data:; connect-src * blob: data:; base-uri 'none'; form-action 'none';"
/>-->
<!--#endif-->
<!--#if MOZCENTRAL-->
<!--#include viewer-snippet-firefox-extension.html-->
<!--#elif CHROME-->
@ -1237,7 +1266,7 @@ See https://github.com/adobe-type-tools/cmap-resources
</dialog>
<!--#if !MOZCENTRAL-->
<dialog id="printServiceDialog" style="min-width: 200px">
<dialog id="printServiceDialog">
<div class="row">
<span data-l10n-id="pdfjs-print-progress-message"></span>
</div>