Strictly manage lifetime of PDFPrintService
Make sure that the print service is stopped as soon as possible when aborted, and that it is not possible for a (slow) promise to accidentally wipe the state of a print job that was started later.
This commit is contained in:
parent
1c3fb175dd
commit
1c869906c8
@ -39,6 +39,7 @@
|
|||||||
var scratchCanvas = null;
|
var scratchCanvas = null;
|
||||||
|
|
||||||
function renderPage(pdfDocument, pageNumber, size, wrapper) {
|
function renderPage(pdfDocument, pageNumber, size, wrapper) {
|
||||||
|
var activeServiceOnEntry = activeService;
|
||||||
if (!scratchCanvas) {
|
if (!scratchCanvas) {
|
||||||
scratchCanvas = document.createElement('canvas');
|
scratchCanvas = document.createElement('canvas');
|
||||||
}
|
}
|
||||||
@ -69,9 +70,7 @@
|
|||||||
};
|
};
|
||||||
return pdfPage.render(renderContext).promise;
|
return pdfPage.render(renderContext).promise;
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
if (!activeService) {
|
activeServiceOnEntry.throwIfInactive();
|
||||||
return Promise.reject(new Error('cancelled'));
|
|
||||||
}
|
|
||||||
if (('toBlob' in scratchCanvas) &&
|
if (('toBlob' in scratchCanvas) &&
|
||||||
!pdfjsLib.PDFJS.disableCreateObjectURL) {
|
!pdfjsLib.PDFJS.disableCreateObjectURL) {
|
||||||
scratchCanvas.toBlob(function (blob) {
|
scratchCanvas.toBlob(function (blob) {
|
||||||
@ -98,6 +97,8 @@
|
|||||||
|
|
||||||
PDFPrintService.prototype = {
|
PDFPrintService.prototype = {
|
||||||
layout: function () {
|
layout: function () {
|
||||||
|
this.throwIfInactive();
|
||||||
|
|
||||||
var pdfDocument = this.pdfDocument;
|
var pdfDocument = this.pdfDocument;
|
||||||
var printContainer = this.printContainer;
|
var printContainer = this.printContainer;
|
||||||
var body = document.querySelector('body');
|
var body = document.querySelector('body');
|
||||||
@ -139,15 +140,18 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
|
if (activeService !== this) {
|
||||||
|
// |activeService| cannot be replaced without calling destroy() first,
|
||||||
|
// so if it differs then an external consumer has a stale reference to
|
||||||
|
// us.
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.printContainer.textContent = '';
|
this.printContainer.textContent = '';
|
||||||
this.wrappers = null;
|
this.wrappers = null;
|
||||||
if (this.pageStyleSheet && this.pageStyleSheet.parentNode) {
|
if (this.pageStyleSheet && this.pageStyleSheet.parentNode) {
|
||||||
this.pageStyleSheet.parentNode.removeChild(this.pageStyleSheet);
|
this.pageStyleSheet.parentNode.removeChild(this.pageStyleSheet);
|
||||||
this.pageStyleSheet = null;
|
this.pageStyleSheet = null;
|
||||||
}
|
}
|
||||||
if (activeService !== this) {
|
|
||||||
return; // no need to clean up shared resources
|
|
||||||
}
|
|
||||||
activeService = null;
|
activeService = null;
|
||||||
if (scratchCanvas) {
|
if (scratchCanvas) {
|
||||||
scratchCanvas.width = scratchCanvas.height = 0;
|
scratchCanvas.width = scratchCanvas.height = 0;
|
||||||
@ -164,10 +168,7 @@
|
|||||||
renderPages: function () {
|
renderPages: function () {
|
||||||
var pageCount = this.pagesOverview.length;
|
var pageCount = this.pagesOverview.length;
|
||||||
var renderNextPage = function (resolve, reject) {
|
var renderNextPage = function (resolve, reject) {
|
||||||
if (activeService !== this) {
|
this.throwIfInactive();
|
||||||
reject(new Error('cancelled'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (++this.currentPage >= pageCount) {
|
if (++this.currentPage >= pageCount) {
|
||||||
renderProgress(pageCount, pageCount);
|
renderProgress(pageCount, pageCount);
|
||||||
resolve();
|
resolve();
|
||||||
@ -181,6 +182,16 @@
|
|||||||
}.bind(this);
|
}.bind(this);
|
||||||
return new Promise(renderNextPage);
|
return new Promise(renderNextPage);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get active() {
|
||||||
|
return this === activeService;
|
||||||
|
},
|
||||||
|
|
||||||
|
throwIfInactive: function () {
|
||||||
|
if (!this.active) {
|
||||||
|
throw new Error('This print request was cancelled or completed.');
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -200,7 +211,22 @@
|
|||||||
if (!activeService) {
|
if (!activeService) {
|
||||||
console.error('Expected print service to be initialized.');
|
console.error('Expected print service to be initialized.');
|
||||||
}
|
}
|
||||||
activeService.renderPages().then(startPrint, abort);
|
var activeServiceOnEntry = activeService;
|
||||||
|
activeService.renderPages().then(function () {
|
||||||
|
activeServiceOnEntry.throwIfInactive();
|
||||||
|
return startPrint(activeServiceOnEntry);
|
||||||
|
}).catch(function () {
|
||||||
|
// Ignore any error messages.
|
||||||
|
}).then(function () {
|
||||||
|
// aborts acts on the "active" print request, so we need to check
|
||||||
|
// whether the print request (activeServiceOnEntry) is still active.
|
||||||
|
// Without the check, an unrelated print request (created after aborting
|
||||||
|
// this print request while the pages were being generated) would be
|
||||||
|
// aborted.
|
||||||
|
if (activeServiceOnEntry.active) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -210,17 +236,21 @@
|
|||||||
window.dispatchEvent(event);
|
window.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startPrint() {
|
function startPrint(activeServiceOnEntry) {
|
||||||
// Push window.print in the macrotask queue to avoid being affected by
|
return new Promise(function (resolve) {
|
||||||
// the deprecation of running print() code in a microtask, see
|
// Push window.print in the macrotask queue to avoid being affected by
|
||||||
// https://github.com/mozilla/pdf.js/issues/7547.
|
// the deprecation of running print() code in a microtask, see
|
||||||
setTimeout(function() {
|
// https://github.com/mozilla/pdf.js/issues/7547.
|
||||||
if (!activeService) {
|
setTimeout(function () {
|
||||||
return; // Print task cancelled by user.
|
if (!activeServiceOnEntry.active) {
|
||||||
}
|
resolve();
|
||||||
print.call(window);
|
return;
|
||||||
setTimeout(abort, 20); // Tidy-up
|
}
|
||||||
}, 0);
|
print.call(window);
|
||||||
|
// Delay promise resolution in case print() was not synchronous.
|
||||||
|
setTimeout(resolve, 20); // Tidy-up.
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function abort() {
|
function abort() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user