Support file save triggered from the Firefox integrated version.

Related to https://bugzilla.mozilla.org/show_bug.cgi?id=1659753

This allows Firefox trigger a "save" event from ctrl/cmd+s or the "Save
Page As" context menu, which in turn lets pdf.js generate a new PDF if
there is form data to save.

I also now use `sourceEventType` on downloads so Firefox can determine if
it should launch the "open with" dialog or "save as" dialog.
This commit is contained in:
Brendan Dahl 2020-08-19 12:19:59 -07:00
parent 10f61b8c96
commit 8023175103
4 changed files with 38 additions and 12 deletions

View File

@ -2541,7 +2541,7 @@ class WorkerTransport {
numPages: this._numPages, numPages: this._numPages,
annotationStorage: annotationStorage:
(annotationStorage && annotationStorage.getAll()) || null, (annotationStorage && annotationStorage.getAll()) || null,
filename: this._fullReader.filename, filename: this._fullReader ? this._fullReader.filename : null,
}) })
.finally(() => { .finally(() => {
annotationStorage.resetModified(); annotationStorage.resetModified();

View File

@ -874,7 +874,7 @@ const PDFViewerApplication = {
); );
}, },
download() { download({ sourceEventType = "download" }) {
function downloadByUrl() { function downloadByUrl() {
downloadManager.downloadUrl(url, filename); downloadManager.downloadUrl(url, filename);
} }
@ -902,12 +902,12 @@ const PDFViewerApplication = {
.getData() .getData()
.then(function (data) { .then(function (data) {
const blob = new Blob([data], { type: "application/pdf" }); const blob = new Blob([data], { type: "application/pdf" });
downloadManager.download(blob, url, filename); downloadManager.download(blob, url, filename, sourceEventType);
}) })
.catch(downloadByUrl); // Error occurred, try downloading with the URL. .catch(downloadByUrl); // Error occurred, try downloading with the URL.
}, },
save() { save({ sourceEventType = "download" }) {
if (this._saveInProgress) { if (this._saveInProgress) {
return; return;
} }
@ -927,7 +927,7 @@ const PDFViewerApplication = {
// When the PDF document isn't ready, or the PDF file is still downloading, // When the PDF document isn't ready, or the PDF file is still downloading,
// simply download using the URL. // simply download using the URL.
if (!this.pdfDocument || !this.downloadComplete) { if (!this.pdfDocument || !this.downloadComplete) {
this.download(); this.download({ sourceEventType });
return; return;
} }
@ -936,10 +936,10 @@ const PDFViewerApplication = {
.saveDocument(this.pdfDocument.annotationStorage) .saveDocument(this.pdfDocument.annotationStorage)
.then(data => { .then(data => {
const blob = new Blob([data], { type: "application/pdf" }); const blob = new Blob([data], { type: "application/pdf" });
downloadManager.download(blob, url, filename); downloadManager.download(blob, url, filename, sourceEventType);
}) })
.catch(() => { .catch(() => {
this.download(); this.download({ sourceEventType });
}) })
.finally(() => { .finally(() => {
this._saveInProgress = false; this._saveInProgress = false;
@ -1722,6 +1722,7 @@ const PDFViewerApplication = {
eventBus._on("presentationmode", webViewerPresentationMode); eventBus._on("presentationmode", webViewerPresentationMode);
eventBus._on("print", webViewerPrint); eventBus._on("print", webViewerPrint);
eventBus._on("download", webViewerDownload); eventBus._on("download", webViewerDownload);
eventBus._on("save", webViewerSave);
eventBus._on("firstpage", webViewerFirstPage); eventBus._on("firstpage", webViewerFirstPage);
eventBus._on("lastpage", webViewerLastPage); eventBus._on("lastpage", webViewerLastPage);
eventBus._on("nextpage", webViewerNextPage); eventBus._on("nextpage", webViewerNextPage);
@ -1800,6 +1801,7 @@ const PDFViewerApplication = {
eventBus._off("presentationmode", webViewerPresentationMode); eventBus._off("presentationmode", webViewerPresentationMode);
eventBus._off("print", webViewerPrint); eventBus._off("print", webViewerPrint);
eventBus._off("download", webViewerDownload); eventBus._off("download", webViewerDownload);
eventBus._off("save", webViewerSave);
eventBus._off("firstpage", webViewerFirstPage); eventBus._off("firstpage", webViewerFirstPage);
eventBus._off("lastpage", webViewerLastPage); eventBus._off("lastpage", webViewerLastPage);
eventBus._off("nextpage", webViewerNextPage); eventBus._off("nextpage", webViewerNextPage);
@ -2334,16 +2336,22 @@ function webViewerPresentationMode() {
function webViewerPrint() { function webViewerPrint() {
window.print(); window.print();
} }
function webViewerDownload() { function webViewerDownloadOrSave(sourceEventType) {
if ( if (
PDFViewerApplication.pdfDocument && PDFViewerApplication.pdfDocument &&
PDFViewerApplication.pdfDocument.annotationStorage.size > 0 PDFViewerApplication.pdfDocument.annotationStorage.size > 0
) { ) {
PDFViewerApplication.save(); PDFViewerApplication.save({ sourceEventType });
} else { } else {
PDFViewerApplication.download(); PDFViewerApplication.download({ sourceEventType });
} }
} }
function webViewerDownload() {
webViewerDownloadOrSave("download");
}
function webViewerSave() {
webViewerDownloadOrSave("save");
}
function webViewerFirstPage() { function webViewerFirstPage() {
if (PDFViewerApplication.pdfDocument) { if (PDFViewerApplication.pdfDocument) {
PDFViewerApplication.page = 1; PDFViewerApplication.page = 1;

View File

@ -64,7 +64,13 @@ class DownloadManager {
download(blobUrl, filename); download(blobUrl, filename);
} }
download(blob, url, filename) { /**
* @param sourceEventType {string} Used to signal what triggered the download.
* The version of PDF.js integrated with Firefox uses this to to determine
* which dialog to show. "save" triggers "save as" and "download" triggers
* the "open with" dialog.
*/
download(blob, url, filename, sourceEventType = "download") {
if (navigator.msSaveBlob) { if (navigator.msSaveBlob) {
// IE10 / IE11 // IE10 / IE11
if (!navigator.msSaveBlob(blob, filename)) { if (!navigator.msSaveBlob(blob, filename)) {

View File

@ -115,7 +115,7 @@ class DownloadManager {
); );
} }
download(blob, url, filename) { download(blob, url, filename, sourceEventType = "download") {
const blobUrl = URL.createObjectURL(blob); const blobUrl = URL.createObjectURL(blob);
const onResponse = err => { const onResponse = err => {
if (err && this.onerror) { if (err && this.onerror) {
@ -130,6 +130,7 @@ class DownloadManager {
blobUrl, blobUrl,
originalUrl: url, originalUrl: url,
filename, filename,
sourceEventType,
}, },
onResponse onResponse
); );
@ -231,6 +232,17 @@ class MozL10n {
} }
})(); })();
(function listenSaveEvent() {
const handleEvent = function ({ type, detail }) {
if (!PDFViewerApplication.initialized) {
return;
}
PDFViewerApplication.eventBus.dispatch(type, { source: window });
};
window.addEventListener("save", handleEvent);
})();
class FirefoxComDataRangeTransport extends PDFDataRangeTransport { class FirefoxComDataRangeTransport extends PDFDataRangeTransport {
requestDataRange(begin, end) { requestDataRange(begin, end) {
FirefoxCom.request("requestDataRange", { begin, end }); FirefoxCom.request("requestDataRange", { begin, end });