Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions test/integration/viewer_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,72 @@ describe("PDF viewer", () => {
});
});

describe("Save/download disabled when supportsDownloading is false", () => {
let pages;

beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".textLayer .endOfContent",
null,
null,
{ supportsDownloading: false }
);
});

afterEach(async () => {
await closePages(pages);
});

it("must hide the download buttons and skip save/download", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.waitForSelector("#downloadButton", { hidden: true });
await waitAndClick(page, "#secondaryToolbarToggleButton");
await page.waitForSelector("#secondaryDownload", { hidden: true });

const triggered = await page.evaluate(async () => {
const app = window.PDFViewerApplication;
const calls = [];
const saveDocument = app.pdfDocument.saveDocument.bind(
app.pdfDocument
);
app.pdfDocument.saveDocument = (...args) => {
calls.push("saveDocument");
return saveDocument(...args);
};

// Each bail-out path dispatches a TESTING-only "downloadskipped"
// event, so we can deterministically wait for all four attempts to
// run to completion.
let skipped = 0;
const allSkipped = new Promise(resolve => {
app.eventBus.on("downloadskipped", function listener() {
if (++skipped === 4) {
app.eventBus.off("downloadskipped", listener);
resolve();
}
});
});

await app.download();
await app.save();
await app.downloadOrSave();
app.eventBus.dispatch("download", { source: null });
await allSkipped;

return { calls, skipped, downloadManager: app.downloadManager };
});
expect(triggered.downloadManager)
.withContext(`In ${browserName}`)
.toBeNull();
expect(triggered.calls).withContext(`In ${browserName}`).toEqual([]);
expect(triggered.skipped).withContext(`In ${browserName}`).toBe(4);
})
);
});
});

describe("Pinch-zoom", () => {
let pages;

Expand Down
36 changes: 35 additions & 1 deletion web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ const PDFViewerApplication = {
maxCanvasPixels: x => parseInt(x, 10),
spreadModeOnLoad: x => parseInt(x, 10),
supportsCaretBrowsingMode: x => x === "true",
supportsDownloading: x => x === "true",
viewerCssTheme: x => parseInt(x, 10),
forcePageColors: x => x === "true",
pageColorsBackground: x => x,
Expand Down Expand Up @@ -434,7 +435,16 @@ const PDFViewerApplication = {
ignoreDestinationZoom: AppOptions.get("ignoreDestinationZoom"),
}));

const downloadManager = (this.downloadManager = new DownloadManager());
const supportsDownloading = AppOptions.get("supportsDownloading");
const downloadManager = (this.downloadManager = supportsDownloading
? new DownloadManager()
: null);
if (appConfig.secondaryToolbar?.downloadButton) {
appConfig.secondaryToolbar.downloadButton.hidden = !supportsDownloading;
}
if (appConfig.toolbar?.download) {
appConfig.toolbar.download.hidden = !supportsDownloading;
}

const findController = (this.findController = new PDFFindController({
linkService,
Expand Down Expand Up @@ -1311,6 +1321,13 @@ const PDFViewerApplication = {
},

async download() {
if (!this.downloadManager) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
this.eventBus.dispatch("downloadskipped", { source: this });
}
return;
}

let data;
try {
data = await (this.pdfDocument
Expand All @@ -1323,6 +1340,13 @@ const PDFViewerApplication = {
},

async save() {
if (!this.downloadManager) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
this.eventBus.dispatch("downloadskipped", { source: this });
}
return;
}

if (this._saveInProgress) {
return;
}
Expand Down Expand Up @@ -1354,6 +1378,13 @@ const PDFViewerApplication = {
},

async downloadOrSave() {
if (!this.downloadManager) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
this.eventBus.dispatch("downloadskipped", { source: this });
}
return;
}

// In the Firefox case, this method MUST always trigger a download.
// When the user is closing a modified and unsaved document, we display a
// prompt asking for saving or not. In case they save, we must wait for
Expand Down Expand Up @@ -2442,6 +2473,9 @@ const PDFViewerApplication = {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) {
return;
}
if (!this.downloadManager) {
return;
}
if (!this.pdfDocument) {
return;
}
Expand Down
5 changes: 5 additions & 0 deletions web/app_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ const defaultOptions = {
value: true,
kind: OptionKind.BROWSER,
},
supportsDownloading: {
/** @type {boolean} */
value: true,
kind: OptionKind.BROWSER,
},
supportsIntegratedFind: {
/** @type {boolean} */
value: false,
Expand Down
2 changes: 1 addition & 1 deletion web/pdf_attachment_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class PDFAttachmentViewer extends BaseTreeViewer {
: fallbackContent;

if (content) {
this.downloadManager.openOrDownloadData(content, filename);
this.downloadManager?.openOrDownloadData(content, filename);
}
};

Expand Down
5 changes: 4 additions & 1 deletion web/pdf_outline_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ class PDFOutlineViewer extends BaseTreeViewer {
const content = await linkService.getAttachmentContent(attachmentId);

if (content) {
this.downloadManager.openOrDownloadData(content, attachment.filename);
this.downloadManager?.openOrDownloadData(
content,
attachment.filename
);
}
};

Expand Down
Loading