Browse Source

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.
Rob Wu 9 years ago
parent
commit
1c869906c8
  1. 74
      web/pdf_print_service.js

74
web/pdf_print_service.js

@ -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…
Cancel
Save