Skip to content

Commit

Permalink
Strictly manage lifetime of PDFPrintService
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Rob--W committed Oct 30, 2016
1 parent 1c3fb17 commit 1c86990
Showing 1 changed file with 52 additions and 22 deletions.
74 changes: 52 additions & 22 deletions web/pdf_print_service.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
var scratchCanvas = null;

function renderPage(pdfDocument, pageNumber, size, wrapper) {
var activeServiceOnEntry = activeService;
if (!scratchCanvas) {
scratchCanvas = document.createElement('canvas');
}
Expand Down Expand Up @@ -69,9 +70,7 @@
};
return pdfPage.render(renderContext).promise;
}).then(function() {
if (!activeService) {
return Promise.reject(new Error('cancelled'));
}
activeServiceOnEntry.throwIfInactive();
if (('toBlob' in scratchCanvas) &&
!pdfjsLib.PDFJS.disableCreateObjectURL) {
scratchCanvas.toBlob(function (blob) {
Expand All @@ -98,6 +97,8 @@

PDFPrintService.prototype = {
layout: function () {
this.throwIfInactive();

var pdfDocument = this.pdfDocument;
var printContainer = this.printContainer;
var body = document.querySelector('body');
Expand Down Expand Up @@ -139,15 +140,18 @@
},

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.wrappers = null;
if (this.pageStyleSheet && this.pageStyleSheet.parentNode) {
this.pageStyleSheet.parentNode.removeChild(this.pageStyleSheet);
this.pageStyleSheet = null;
}
if (activeService !== this) {
return; // no need to clean up shared resources
}
activeService = null;
if (scratchCanvas) {
scratchCanvas.width = scratchCanvas.height = 0;
Expand All @@ -164,10 +168,7 @@
renderPages: function () {
var pageCount = this.pagesOverview.length;
var renderNextPage = function (resolve, reject) {
if (activeService !== this) {
reject(new Error('cancelled'));
return;
}
this.throwIfInactive();
if (++this.currentPage >= pageCount) {
renderProgress(pageCount, pageCount);
resolve();
Expand All @@ -181,6 +182,16 @@
}.bind(this);
return new Promise(renderNextPage);
},

get active() {
return this === activeService;
},

throwIfInactive: function () {
if (!this.active) {
throw new Error('This print request was cancelled or completed.');
}
},
};


Expand All @@ -200,7 +211,22 @@
if (!activeService) {
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();
}
});
}
};

Expand All @@ -210,17 +236,21 @@
window.dispatchEvent(event);
}

function startPrint() {
// Push window.print in the macrotask queue to avoid being affected by
// the deprecation of running print() code in a microtask, see
// https://github.com/mozilla/pdf.js/issues/7547.
setTimeout(function() {
if (!activeService) {
return; // Print task cancelled by user.
}
print.call(window);
setTimeout(abort, 20); // Tidy-up
}, 0);
function startPrint(activeServiceOnEntry) {
return new Promise(function (resolve) {
// Push window.print in the macrotask queue to avoid being affected by
// the deprecation of running print() code in a microtask, see
// https://github.com/mozilla/pdf.js/issues/7547.
setTimeout(function () {
if (!activeServiceOnEntry.active) {
resolve();
return;
}
print.call(window);
// Delay promise resolution in case print() was not synchronous.
setTimeout(resolve, 20); // Tidy-up.
}, 0);
});
}

function abort() {
Expand Down

0 comments on commit 1c86990

Please sign in to comment.