From 4cc3afe094f1028a57fede32166eb511c926afcb Mon Sep 17 00:00:00 2001 From: Pdf Bot Date: Fri, 23 Oct 2015 18:55:08 +0100 Subject: [PATCH] PDF.js version 1.1.551 - See mozilla/pdf.js@2a5616c2aa2388e65669f5f99481b35e7422bc5e --- bower.json | 2 +- build/pdf.combined.js | 441 +++++++++++++++++++++++++++++++----------- build/pdf.js | 142 +++++++++++--- build/pdf.worker.js | 308 ++++++++++++++++++++--------- package.json | 2 +- web/pdf_viewer.js | 4 +- 6 files changed, 667 insertions(+), 232 deletions(-) diff --git a/bower.json b/bower.json index a881d899e..1999c21b2 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.1.546", + "version": "1.1.551", "main": [ "build/pdf.js", "build/pdf.worker.js" diff --git a/build/pdf.combined.js b/build/pdf.combined.js index bdc9ec7f6..9fc00ec9c 100644 --- a/build/pdf.combined.js +++ b/build/pdf.combined.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.1.546'; -PDFJS.build = 'b3a9a0d'; +PDFJS.version = '1.1.551'; +PDFJS.build = '2a5616c'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -225,6 +225,11 @@ function warn(msg) { } } +// Deprecated API function -- treated as warnings. +function deprecated(details) { + warn('Deprecated API usage: ' + details); +} + // Fatal errors that should trigger the fallback UI and halt execution by // throwing an exception. function error(msg) { @@ -1869,12 +1874,19 @@ PDFJS.getDocument = function getDocument(src, var task = new PDFDocumentLoadingTask(); // Support of the obsolete arguments (for compatibility with API v1.0) + if (arguments.length > 1) { + deprecated('getDocument is called with pdfDataRangeTransport, ' + + 'passwordCallback or progressCallback argument'); + } if (pdfDataRangeTransport) { if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) { // Not a PDFDataRangeTransport instance, trying to add missing properties. pdfDataRangeTransport = Object.create(pdfDataRangeTransport); pdfDataRangeTransport.length = src.length; pdfDataRangeTransport.initialData = src.initialData; + if (!pdfDataRangeTransport.abort) { + pdfDataRangeTransport.abort = function () {}; + } } src = Object.create(src); src.range = pdfDataRangeTransport; @@ -1934,6 +1946,7 @@ PDFJS.getDocument = function getDocument(src, workerInitializedCapability.promise.then(function transportInitialized() { transport.fetchDocument(task, params); }); + task._transport = transport; return task; }; @@ -1946,6 +1959,7 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { /** @constructs PDFDocumentLoadingTask */ function PDFDocumentLoadingTask() { this._capability = createPromiseCapability(); + this._transport = null; /** * Callback to request a password if wrong or no password was provided. @@ -1971,7 +1985,14 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { return this._capability.promise; }, - // TODO add cancel or abort method + /** + * Aborts all network requests and destroys worker. + * @return {Promise} A promise that is resolved after destruction activity + * is completed. + */ + destroy: function () { + return this._transport.destroy(); + }, /** * Registers callbacks to indicate the document loading completion. @@ -2058,6 +2079,9 @@ var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() { requestDataRange: function PDFDataRangeTransport_requestDataRange(begin, end) { throw new Error('Abstract method PDFDataRangeTransport.requestDataRange'); + }, + + abort: function PDFDataRangeTransport_abort() { } }; return PDFDataRangeTransport; @@ -2071,9 +2095,10 @@ PDFJS.PDFDataRangeTransport = PDFDataRangeTransport; * @class */ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { - function PDFDocumentProxy(pdfInfo, transport) { + function PDFDocumentProxy(pdfInfo, transport, loadingTask) { this.pdfInfo = pdfInfo; this.transport = transport; + this.loadingTask = loadingTask; } PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ { /** @@ -2196,7 +2221,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * Destroys current document instance and terminates worker. */ destroy: function PDFDocumentProxy_destroy() { - this.transport.destroy(); + return this.transport.destroy(); } }; return PDFDocumentProxy; @@ -2273,8 +2298,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() { this.commonObjs = transport.commonObjs; this.objs = new PDFObjects(); this.cleanupAfterRender = false; - this.pendingDestroy = false; + this.pendingCleanup = false; this.intentStates = {}; + this.destroyed = false; } PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ { /** @@ -2338,7 +2364,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { // If there was a pending destroy cancel it so no cleanup happens during // this call to render. - this.pendingDestroy = false; + this.pendingCleanup = false; var renderingIntent = (params.intent === 'print' ? 'print' : 'display'); @@ -2379,13 +2405,14 @@ var PDFPageProxy = (function PDFPageProxyClosure() { // Obsolete parameter support if (params.continueCallback) { + deprecated('render is used with continueCallback parameter'); renderTask.onContinue = params.continueCallback; } var self = this; intentState.displayReadyCapability.promise.then( function pageDisplayReadyPromise(transparency) { - if (self.pendingDestroy) { + if (self.pendingCleanup) { complete(); return; } @@ -2405,9 +2432,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() { } if (self.cleanupAfterRender) { - self.pendingDestroy = true; + self.pendingCleanup = true; } - self._tryDestroy(); + self._tryCleanup(); if (error) { internalRenderTask.capability.reject(error); @@ -2468,20 +2495,52 @@ var PDFPageProxy = (function PDFPageProxyClosure() { pageIndex: this.pageNumber - 1 }); }, + + /** + * Destroys page object. + */ + _destroy: function PDFPageProxy_destroy() { + this.destroyed = true; + this.transport.pageCache[this.pageIndex] = null; + + var waitOn = []; + Object.keys(this.intentStates).forEach(function(intent) { + var intentState = this.intentStates[intent]; + intentState.renderTasks.forEach(function(renderTask) { + var renderCompleted = renderTask.capability.promise. + catch(function () {}); // ignoring failures + waitOn.push(renderCompleted); + renderTask.cancel(); + }); + }, this); + this.objs.clear(); + this.annotationsPromise = null; + this.pendingCleanup = false; + return Promise.all(waitOn); + }, + /** - * Destroys resources allocated by the page. + * Cleans up resources allocated by the page. (deprecated) */ - destroy: function PDFPageProxy_destroy() { - this.pendingDestroy = true; - this._tryDestroy(); + destroy: function() { + deprecated('page destroy method, use cleanup() instead'); + this.cleanup(); + }, + + /** + * Cleans up resources allocated by the page. + */ + cleanup: function PDFPageProxy_cleanup() { + this.pendingCleanup = true; + this._tryCleanup(); }, /** * For internal use only. Attempts to clean up if rendering is in a state * where that's possible. * @ignore */ - _tryDestroy: function PDFPageProxy__destroy() { - if (!this.pendingDestroy || + _tryCleanup: function PDFPageProxy_tryCleanup() { + if (!this.pendingCleanup || Object.keys(this.intentStates).some(function(intent) { var intentState = this.intentStates[intent]; return (intentState.renderTasks.length !== 0 || @@ -2495,7 +2554,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { }, this); this.objs.clear(); this.annotationsPromise = null; - this.pendingDestroy = false; + this.pendingCleanup = false; }, /** * For internal use only. @@ -2533,7 +2592,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { if (operatorListChunk.lastChunk) { intentState.receivingOperatorList = false; - this._tryDestroy(); + this._tryCleanup(); } } }; @@ -2551,6 +2610,8 @@ var WorkerTransport = (function WorkerTransportClosure() { this.commonObjs = new PDFObjects(); this.loadingTask = null; + this.destroyed = false; + this.destroyCapability = null; this.pageCache = []; this.pagePromises = []; @@ -2567,15 +2628,40 @@ var WorkerTransport = (function WorkerTransportClosure() { } WorkerTransport.prototype = { destroy: function WorkerTransport_destroy() { + if (this.destroyCapability) { + return this.destroyCapability.promise; + } + + this.destroyed = true; + this.destroyCapability = createPromiseCapability(); + + var waitOn = []; + // We need to wait for all renderings to be completed, e.g. + // timeout/rAF can take a long time. + this.pageCache.forEach(function (page) { + if (page) { + waitOn.push(page._destroy()); + } + }); this.pageCache = []; this.pagePromises = []; var self = this; - this.messageHandler.sendWithPromise('Terminate', null).then(function () { + // We also need to wait for the worker to finish its long running tasks. + var terminated = this.messageHandler.sendWithPromise('Terminate', null); + waitOn.push(terminated); + Promise.all(waitOn).then(function () { FontLoader.clear(); if (self.worker) { self.worker.terminate(); } - }); + if (self.pdfDataRangeTransport) { + self.pdfDataRangeTransport.abort(); + self.pdfDataRangeTransport = null; + } + self.messageHandler = null; + self.destroyCapability.resolve(); + }, this.destroyCapability.reject); + return this.destroyCapability.promise; }, setupFakeWorker: function WorkerTransport_setupFakeWorker() { @@ -2647,9 +2733,10 @@ var WorkerTransport = (function WorkerTransportClosure() { messageHandler.on('GetDoc', function transportDoc(data) { var pdfInfo = data.pdfInfo; this.numPages = data.pdfInfo.numPages; - var pdfDocument = new PDFDocumentProxy(pdfInfo, this); + var loadingTask = this.loadingTask; + var pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask); this.pdfDocument = pdfDocument; - this.loadingTask._capability.resolve(pdfDocument); + loadingTask._capability.resolve(pdfDocument); }, this); messageHandler.on('NeedPassword', @@ -2850,6 +2937,12 @@ var WorkerTransport = (function WorkerTransportClosure() { }, fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) { + if (this.destroyed) { + loadingTask._capability.reject(new Error('Loading aborted')); + this.destroyCapability.resolve(); + return; + } + this.loadingTask = loadingTask; source.disableAutoFetch = PDFJS.disableAutoFetch; @@ -2888,6 +2981,9 @@ var WorkerTransport = (function WorkerTransportClosure() { var promise = this.messageHandler.sendWithPromise('GetPage', { pageIndex: pageIndex }).then(function (pageInfo) { + if (this.destroyed) { + throw new Error('Transport destroyed'); + } var page = new PDFPageProxy(pageIndex, pageInfo, this); this.pageCache[pageIndex] = page; return page; @@ -2945,7 +3041,7 @@ var WorkerTransport = (function WorkerTransportClosure() { for (var i = 0, ii = this.pageCache.length; i < ii; i++) { var page = this.pageCache[i]; if (page) { - page.destroy(); + page.cleanup(); } } this.commonObjs.clear(); @@ -8633,7 +8729,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { this.chunksNeededByRequest = {}; this.requestsByChunk = {}; - this.callbacksByRequest = {}; + this.promisesByRequest = {}; this.progressiveDataLength = 0; this._loadedStreamCapability = createPromiseCapability(); @@ -8652,12 +8748,11 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { // contiguous ranges to load in as few requests as possible requestAllChunks: function ChunkedStreamManager_requestAllChunks() { var missingChunks = this.stream.getMissingChunks(); - this.requestChunks(missingChunks); + this._requestChunks(missingChunks); return this._loadedStreamCapability.promise; }, - requestChunks: function ChunkedStreamManager_requestChunks(chunks, - callback) { + _requestChunks: function ChunkedStreamManager_requestChunks(chunks) { var requestId = this.currRequestId++; var chunksNeeded; @@ -8670,13 +8765,11 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } if (isEmptyObj(chunksNeeded)) { - if (callback) { - callback(); - } - return; + return Promise.resolve(); } - this.callbacksByRequest[requestId] = callback; + var capability = createPromiseCapability(); + this.promisesByRequest[requestId] = capability; var chunksToRequest = []; for (var chunk in chunksNeeded) { @@ -8689,7 +8782,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } if (!chunksToRequest.length) { - return; + return capability.promise; } var groupedChunksToRequest = this.groupChunks(chunksToRequest); @@ -8700,6 +8793,8 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length); this.sendRequest(begin, end); } + + return capability.promise; }, getStream: function ChunkedStreamManager_getStream() { @@ -8707,8 +8802,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { }, // Loads any chunks in the requested range that are not yet loaded - requestRange: function ChunkedStreamManager_requestRange( - begin, end, callback) { + requestRange: function ChunkedStreamManager_requestRange(begin, end) { end = Math.min(end, this.length); @@ -8720,11 +8814,10 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { chunks.push(chunk); } - this.requestChunks(chunks, callback); + return this._requestChunks(chunks); }, - requestRanges: function ChunkedStreamManager_requestRanges(ranges, - callback) { + requestRanges: function ChunkedStreamManager_requestRanges(ranges) { ranges = ranges || []; var chunksToRequest = []; @@ -8739,7 +8832,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } chunksToRequest.sort(function(a, b) { return a - b; }); - this.requestChunks(chunksToRequest, callback); + return this._requestChunks(chunksToRequest); }, // Groups a sorted array of chunks into as few contiguous larger @@ -8838,17 +8931,15 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { nextEmptyChunk = this.stream.nextEmptyChunk(endChunk); } if (isInt(nextEmptyChunk)) { - this.requestChunks([nextEmptyChunk]); + this._requestChunks([nextEmptyChunk]); } } for (i = 0; i < loadedRequests.length; ++i) { requestId = loadedRequests[i]; - var callback = this.callbacksByRequest[requestId]; - delete this.callbacksByRequest[requestId]; - if (callback) { - callback(); - } + var capability = this.promisesByRequest[requestId]; + delete this.promisesByRequest[requestId]; + capability.resolve(); } this.msgHandler.send('DocProgress', { @@ -8869,6 +8960,16 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { getEndChunk: function ChunkedStreamManager_getEndChunk(end) { var chunk = Math.floor((end - 1) / this.chunkSize) + 1; return chunk; + }, + + abort: function ChunkedStreamManager_abort() { + if (this.networkManager) { + this.networkManager.abortAllRequests(); + } + for(var requestId in this.promisesByRequest) { + var capability = this.promisesByRequest[requestId]; + capability.reject(new Error('Request was aborted')); + } } }; @@ -9040,7 +9141,8 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { reject(e); return; } - pdfManager.streamManager.requestRange(e.begin, e.end, ensureHelper); + pdfManager.streamManager.requestRange(e.begin, e.end). + then(ensureHelper, reject); } } @@ -9050,11 +9152,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { NetworkPdfManager.prototype.requestRange = function NetworkPdfManager_requestRange(begin, end) { - return new Promise(function (resolve) { - this.streamManager.requestRange(begin, end, function() { - resolve(); - }); - }.bind(this)); + return this.streamManager.requestRange(begin, end); }; NetworkPdfManager.prototype.requestLoadedStream = @@ -9074,7 +9172,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { NetworkPdfManager.prototype.terminate = function NetworkPdfManager_terminate() { - this.streamManager.networkManager.abortAllRequests(); + this.streamManager.abort(); }; return NetworkPdfManager; @@ -9219,7 +9317,7 @@ var Page = (function PageClosure() { }.bind(this)); }, - getOperatorList: function Page_getOperatorList(handler, intent) { + getOperatorList: function Page_getOperatorList(handler, task, intent) { var self = this; var pdfManager = this.pdfManager; @@ -9252,8 +9350,8 @@ var Page = (function PageClosure() { pageIndex: self.pageIndex, intent: intent }); - return partialEvaluator.getOperatorList(contentStream, self.resources, - opList).then(function () { + return partialEvaluator.getOperatorList(contentStream, task, + self.resources, opList).then(function () { return opList; }); }); @@ -9270,7 +9368,7 @@ var Page = (function PageClosure() { } var annotationsReadyPromise = Annotation.appendToOperatorList( - annotations, pageOpList, pdfManager, partialEvaluator, intent); + annotations, pageOpList, pdfManager, partialEvaluator, task, intent); return annotationsReadyPromise.then(function () { pageOpList.flush(true); return pageOpList; @@ -9278,7 +9376,7 @@ var Page = (function PageClosure() { }); }, - extractTextContent: function Page_extractTextContent() { + extractTextContent: function Page_extractTextContent(task) { var handler = { on: function nullHandlerOn() {}, send: function nullHandlerSend() {} @@ -9307,6 +9405,7 @@ var Page = (function PageClosure() { self.fontCache); return partialEvaluator.getTextContent(contentStream, + task, self.resources); }); }, @@ -10959,9 +11058,9 @@ var XRef = (function XRefClosure() { resolve(xref.fetch(ref, suppressEncryption)); } catch (e) { if (e instanceof MissingDataException) { - streamManager.requestRange(e.begin, e.end, function () { + streamManager.requestRange(e.begin, e.end).then(function () { tryFetch(resolve, reject); - }); + }, reject); return; } reject(e); @@ -11237,6 +11336,7 @@ var ObjectLoader = (function() { this.keys = keys; this.xref = xref; this.refSet = null; + this.capability = null; } ObjectLoader.prototype = { @@ -11257,11 +11357,11 @@ var ObjectLoader = (function() { nodesToVisit.push(this.obj[keys[i]]); } - this.walk(nodesToVisit); + this._walk(nodesToVisit); return this.capability.promise; }, - walk: function ObjectLoader_walk(nodesToVisit) { + _walk: function ObjectLoader_walk(nodesToVisit) { var nodesToRevisit = []; var pendingRequests = []; // DFS walk of the object graph. @@ -11308,7 +11408,7 @@ var ObjectLoader = (function() { } if (pendingRequests.length) { - this.xref.stream.manager.requestRanges(pendingRequests, + this.xref.stream.manager.requestRanges(pendingRequests).then( function pendingRequestCallback() { nodesToVisit = nodesToRevisit; for (var i = 0; i < nodesToRevisit.length; i++) { @@ -11319,8 +11419,8 @@ var ObjectLoader = (function() { this.refSet.remove(node); } } - this.walk(nodesToVisit); - }.bind(this)); + this._walk(nodesToVisit); + }.bind(this), this.capability.reject); return; } // Everything is loaded. @@ -11694,7 +11794,7 @@ var Annotation = (function AnnotationClosure() { }.bind(this)); }, - getOperatorList: function Annotation_getOperatorList(evaluator) { + getOperatorList: function Annotation_getOperatorList(evaluator, task) { if (!this.appearance) { return Promise.resolve(new OperatorList()); @@ -11721,7 +11821,8 @@ var Annotation = (function AnnotationClosure() { return resourcesPromise.then(function(resources) { var opList = new OperatorList(); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); - return evaluator.getOperatorList(self.appearance, resources, opList). + return evaluator.getOperatorList(self.appearance, task, + resources, opList). then(function () { opList.addOp(OPS.endAnnotation, []); self.appearance.reset(); @@ -11732,7 +11833,7 @@ var Annotation = (function AnnotationClosure() { }; Annotation.appendToOperatorList = function Annotation_appendToOperatorList( - annotations, opList, pdfManager, partialEvaluator, intent) { + annotations, opList, pdfManager, partialEvaluator, task, intent) { function reject(e) { annotationsReadyCapability.reject(e); @@ -11745,7 +11846,7 @@ var Annotation = (function AnnotationClosure() { if (intent === 'display' && annotations[i].isViewable() || intent === 'print' && annotations[i].isPrintable()) { annotationPromises.push( - annotations[i].getOperatorList(partialEvaluator)); + annotations[i].getOperatorList(partialEvaluator, task)); } } Promise.all(annotationPromises).then(function(datas) { @@ -11977,9 +12078,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { } Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) { + getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator, + task) { if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator); + return Annotation.prototype.getOperatorList.call(this, evaluator, task); } var opList = new OperatorList(); @@ -11992,7 +12094,8 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { } var stream = new Stream(stringToBytes(data.defaultAppearance)); - return evaluator.getOperatorList(stream, this.fieldResources, opList). + return evaluator.getOperatorList(stream, task, + this.fieldResources, opList). then(function () { return opList; }); @@ -17380,6 +17483,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { buildFormXObject: function PartialEvaluator_buildFormXObject(resources, xobj, smask, operatorList, + task, initialState) { var matrix = xobj.dict.getArray('Matrix'); var bbox = xobj.dict.getArray('BBox'); @@ -17412,7 +17516,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]); - return this.getOperatorList(xobj, + return this.getOperatorList(xobj, task, (xobj.dict.get('Resources') || resources), operatorList, initialState). then(function () { operatorList.addOp(OPS.paintFormXObjectEnd, []); @@ -17524,7 +17628,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, handleSMask: function PartialEvaluator_handleSmask(smask, resources, - operatorList, + operatorList, task, stateManager) { var smaskContent = smask.get('G'); var smaskOptions = { @@ -17532,13 +17636,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { backdrop: smask.get('BC') }; return this.buildFormXObject(resources, smaskContent, smaskOptions, - operatorList, stateManager.state.clone()); + operatorList, task, stateManager.state.clone()); }, handleTilingType: function PartialEvaluator_handleTilingType(fn, args, resources, pattern, patternDict, - operatorList) { + operatorList, task) { // Create an IR of the pattern code. var tilingOpList = new OperatorList(); // Merge the available resources, to prevent issues when the patternDict @@ -17546,8 +17650,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var resourcesArray = [patternDict.get('Resources'), resources]; var patternResources = Dict.merge(this.xref, resourcesArray); - return this.getOperatorList(pattern, patternResources, tilingOpList).then( - function () { + return this.getOperatorList(pattern, task, patternResources, + tilingOpList).then(function () { // Add the dependencies to the parent operator list so they are // resolved before sub operator list is executed synchronously. operatorList.addDependencies(tilingOpList.dependencies); @@ -17560,7 +17664,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { handleSetFont: function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef, - operatorList, state) { + operatorList, task, state) { // TODO(mack): Not needed? var fontName; if (fontArgs) { @@ -17574,8 +17678,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (!translated.font.isType3Font) { return translated; } - return translated.loadType3Data(self, resources, operatorList).then( - function () { + return translated.loadType3Data(self, resources, operatorList, task). + then(function () { return translated; }); }).then(function (translated) { @@ -17622,8 +17726,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, setGState: function PartialEvaluator_setGState(resources, gState, - operatorList, xref, - stateManager) { + operatorList, task, + xref, stateManager) { // This array holds the converted/processed state data. var gStateObj = []; var gStateMap = gState.map; @@ -17647,8 +17751,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { break; case 'Font': promise = promise.then(function () { - return self.handleSetFont(resources, null, value[0], - operatorList, stateManager.state). + return self.handleSetFont(resources, null, value[0], operatorList, + task, stateManager.state). then(function (loadedName) { operatorList.addDependency(loadedName); gStateObj.push([key, [loadedName, value[1]]]); @@ -17667,7 +17771,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (isDict(dict)) { promise = promise.then(function () { return self.handleSMask(dict, resources, operatorList, - stateManager); + task, stateManager); }); gStateObj.push([key, true]); } else { @@ -17848,7 +17952,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, handleColorN: function PartialEvaluator_handleColorN(operatorList, fn, args, - cs, patterns, resources, xref) { + cs, patterns, resources, task, xref) { // compile tiling patterns var patternName = args[args.length - 1]; // SCN/scn applies patterns along with normal colors @@ -17861,7 +17965,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (typeNum === TILING_PATTERN) { var color = cs.base ? cs.base.getRgb(args, 0) : null; return this.handleTilingType(fn, color, resources, pattern, - dict, operatorList); + dict, operatorList, task); } else if (typeNum === SHADING_PATTERN) { var shading = dict.get('Shading'); var matrix = dict.get('Matrix'); @@ -17878,6 +17982,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, getOperatorList: function PartialEvaluator_getOperatorList(stream, + task, resources, operatorList, initialState) { @@ -17896,6 +18001,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var timeSlotManager = new TimeSlotManager(); return new Promise(function next(resolve, reject) { + task.ensureNotTerminated(); timeSlotManager.reset(); var stop, operation = {}, i, ii, cs; while (!(stop = timeSlotManager.check())) { @@ -17938,7 +18044,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (type.name === 'Form') { stateManager.save(); return self.buildFormXObject(resources, xobj, null, - operatorList, + operatorList, task, stateManager.state.clone()). then(function () { stateManager.restore(); @@ -17962,8 +18068,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { case OPS.setFont: var fontSize = args[1]; // eagerly collect all fonts - return self.handleSetFont(resources, args, null, - operatorList, stateManager.state). + return self.handleSetFont(resources, args, null, operatorList, + task, stateManager.state). then(function (loadedName) { operatorList.addDependency(loadedName); operatorList.addOp(OPS.setFont, [loadedName, fontSize]); @@ -18069,7 +18175,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { cs = stateManager.state.fillColorSpace; if (cs.name === 'Pattern') { return self.handleColorN(operatorList, OPS.setFillColorN, - args, cs, patterns, resources, xref).then(function() { + args, cs, patterns, resources, task, xref).then(function() { next(resolve, reject); }, reject); } @@ -18080,7 +18186,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { cs = stateManager.state.strokeColorSpace; if (cs.name === 'Pattern') { return self.handleColorN(operatorList, OPS.setStrokeColorN, - args, cs, patterns, resources, xref).then(function() { + args, cs, patterns, resources, task, xref).then(function() { next(resolve, reject); }, reject); } @@ -18114,8 +18220,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } var gState = extGState.get(dictName.name); - return self.setGState(resources, gState, operatorList, xref, - stateManager).then(function() { + return self.setGState(resources, gState, operatorList, task, + xref, stateManager).then(function() { next(resolve, reject); }, reject); case OPS.moveTo: @@ -18153,7 +18259,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (stop) { deferred.then(function () { next(resolve, reject); - }); + }, reject); return; } // Some PDFs don't close all restores inside object/form. @@ -18165,7 +18271,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }); }, - getTextContent: function PartialEvaluator_getTextContent(stream, resources, + getTextContent: function PartialEvaluator_getTextContent(stream, task, + resources, stateManager) { stateManager = (stateManager || new StateManager(new TextState())); @@ -18343,6 +18450,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var timeSlotManager = new TimeSlotManager(); return new Promise(function next(resolve, reject) { + task.ensureNotTerminated(); timeSlotManager.reset(); var stop, operation = {}, args = []; while (!(stop = timeSlotManager.check())) { @@ -18498,7 +18606,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { stateManager.transform(matrix); } - return self.getTextContent(xobj, + return self.getTextContent(xobj, task, xobj.dict.get('Resources') || resources, stateManager). then(function (formTextContent) { Util.appendToArray(bidiTexts, formTextContent.items); @@ -18538,7 +18646,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (stop) { deferred.then(function () { next(resolve, reject); - }); + }, reject); return; } resolve(textContent); @@ -19103,7 +19211,7 @@ var TranslatedFont = (function TranslatedFontClosure() { ]); this.sent = true; }, - loadType3Data: function (evaluator, resources, parentOperatorList) { + loadType3Data: function (evaluator, resources, parentOperatorList, task) { assert(this.font.isType3Font); if (this.type3Loaded) { @@ -19120,7 +19228,7 @@ var TranslatedFont = (function TranslatedFontClosure() { loadCharProcsPromise = loadCharProcsPromise.then(function (key) { var glyphStream = charProcs[key]; var operatorList = new OperatorList(); - return evaluator.getOperatorList(glyphStream, fontResources, + return evaluator.getOperatorList(glyphStream, task, fontResources, operatorList).then(function () { charProcOperatorList[key] = operatorList.getIR(); @@ -40683,9 +40791,58 @@ var NullStream = (function NullStreamClosure() { })(); +var WorkerTask = (function WorkerTaskClosure() { + function WorkerTask(name) { + this.name = name; + this.terminated = false; + this._capability = createPromiseCapability(); + } + + WorkerTask.prototype = { + get finished() { + return this._capability.promise; + }, + + finish: function () { + this._capability.resolve(); + }, + + terminate: function () { + this.terminated = true; + }, + + ensureNotTerminated: function () { + if (this.terminated) { + throw new Error('Worker task was terminated'); + } + } + }; + + return WorkerTask; +})(); + var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { setup: function wphSetup(handler) { var pdfManager; + var terminated = false; + var cancelXHRs = null; + var WorkerTasks = []; + + function ensureNotTerminated() { + if (terminated) { + throw new Error('Worker was terminated'); + } + } + + function startWorkerTask(task) { + WorkerTasks.push(task); + } + + function finishWorkerTask(task) { + task.finish(); + var i = WorkerTasks.indexOf(task); + WorkerTasks.splice(i, 1); + } function loadDocument(recoveryMode) { var loadDocumentCapability = createPromiseCapability(); @@ -40722,13 +40879,14 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { function getPdfManager(data) { var pdfManagerCapability = createPromiseCapability(); + var pdfManager; var source = data.source; var disableRange = data.disableRange; if (source.data) { try { pdfManager = new LocalPdfManager(source.data, source.password); - pdfManagerCapability.resolve(); + pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); } @@ -40736,7 +40894,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } else if (source.chunkedViewerLoading) { try { pdfManager = new NetworkPdfManager(source, handler); - pdfManagerCapability.resolve(); + pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); } @@ -40797,6 +40955,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } catch (ex) { pdfManagerCapability.reject(ex); } + cancelXHRs = null; }, onProgressiveData: source.disableStream ? null : @@ -40837,10 +40996,11 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { // the data is array, instantiating directly from it try { pdfManager = new LocalPdfManager(pdfFile, source.password); - pdfManagerCapability.resolve(); + pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); } + cancelXHRs = null; }, onError: function onError(status) { @@ -40855,6 +41015,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { ') while retrieving PDF "' + source.url + '".', status); handler.send('UnexpectedResponse', exception); } + cancelXHRs = null; }, onProgress: function onProgress(evt) { @@ -40865,6 +41026,10 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } }); + cancelXHRs = function () { + networkManager.abortRequest(fullRequestXhrId); + }; + return pdfManagerCapability.promise; } @@ -40897,8 +41062,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); handler.on('GetDocRequest', function wphSetupDoc(data) { - var onSuccess = function(doc) { + ensureNotTerminated(); handler.send('GetDoc', { pdfInfo: doc }); }; @@ -40921,6 +41086,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } }; + ensureNotTerminated(); + PDFJS.maxImageSize = data.maxImageSize === undefined ? -1 : data.maxImageSize; PDFJS.disableFontFace = data.disableFontFace; @@ -40930,13 +41097,26 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { null : data.cMapUrl; PDFJS.cMapPacked = data.cMapPacked === true; - getPdfManager(data).then(function () { + getPdfManager(data).then(function (newPdfManager) { + if (terminated) { + // We were in a process of setting up the manager, but it got + // terminated in the middle. + newPdfManager.terminate(); + throw new Error('Worker was terminated'); + } + + pdfManager = newPdfManager; + handler.send('PDFManagerReady', null); pdfManager.onLoadedStream().then(function(stream) { handler.send('DataLoaded', { length: stream.bytes.byteLength }); }); }).then(function pdfManagerReady() { + ensureNotTerminated(); + loadDocument(false).then(onSuccess, function loadFailure(ex) { + ensureNotTerminated(); + // Try again with recoveryMode == true if (!(ex instanceof XRefParseException)) { if (ex instanceof PasswordException) { @@ -40951,6 +41131,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { pdfManager.requestLoadedStream(); pdfManager.onLoadedStream().then(function() { + ensureNotTerminated(); + loadDocument(true).then(onSuccess, onFailure); }); }, onFailure); @@ -41041,17 +41223,25 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); handler.on('RenderPageRequest', function wphSetupRenderPage(data) { - pdfManager.getPage(data.pageIndex).then(function(page) { + var pageIndex = data.pageIndex; + pdfManager.getPage(pageIndex).then(function(page) { + var task = new WorkerTask('RenderPageRequest: page ' + pageIndex); + startWorkerTask(task); - var pageNum = data.pageIndex + 1; + var pageNum = pageIndex + 1; var start = Date.now(); // Pre compile the pdf page and fetch the fonts/images. - page.getOperatorList(handler, data.intent).then(function(operatorList) { + page.getOperatorList(handler, task, data.intent).then( + function(operatorList) { + finishWorkerTask(task); info('page=' + pageNum + ' - getOperatorList: time=' + (Date.now() - start) + 'ms, len=' + operatorList.fnArray.length); - }, function(e) { + finishWorkerTask(task); + if (task.terminated) { + return; // ignoring errors from the terminated thread + } var minimumStackMessage = 'worker.js: while trying to getPage() and getOperatorList()'; @@ -41086,13 +41276,23 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }, this); handler.on('GetTextContent', function wphExtractText(data) { - return pdfManager.getPage(data.pageIndex).then(function(page) { - var pageNum = data.pageIndex + 1; + var pageIndex = data.pageIndex; + return pdfManager.getPage(pageIndex).then(function(page) { + var task = new WorkerTask('GetTextContent: page ' + pageIndex); + startWorkerTask(task); + var pageNum = pageIndex + 1; var start = Date.now(); - return page.extractTextContent().then(function(textContent) { + return page.extractTextContent(task).then(function(textContent) { + finishWorkerTask(task); info('text indexing: page=' + pageNum + ' - time=' + (Date.now() - start) + 'ms'); return textContent; + }, function (reason) { + finishWorkerTask(task); + if (task.terminated) { + return; // ignoring errors from the terminated thread + } + throw reason; }); }); }); @@ -41102,7 +41302,22 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); handler.on('Terminate', function wphTerminate(data) { - pdfManager.terminate(); + terminated = true; + if (pdfManager) { + pdfManager.terminate(); + pdfManager = null; + } + if (cancelXHRs) { + cancelXHRs(); + } + + var waitOn = []; + WorkerTasks.forEach(function (task) { + waitOn.push(task.finished); + task.terminate(); + }); + + return Promise.all(waitOn).then(function () {}); }); } }; diff --git a/build/pdf.js b/build/pdf.js index 6eef6cd9e..3822e0c28 100644 --- a/build/pdf.js +++ b/build/pdf.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.1.546'; -PDFJS.build = 'b3a9a0d'; +PDFJS.version = '1.1.551'; +PDFJS.build = '2a5616c'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -225,6 +225,11 @@ function warn(msg) { } } +// Deprecated API function -- treated as warnings. +function deprecated(details) { + warn('Deprecated API usage: ' + details); +} + // Fatal errors that should trigger the fallback UI and halt execution by // throwing an exception. function error(msg) { @@ -1878,12 +1883,19 @@ PDFJS.getDocument = function getDocument(src, var task = new PDFDocumentLoadingTask(); // Support of the obsolete arguments (for compatibility with API v1.0) + if (arguments.length > 1) { + deprecated('getDocument is called with pdfDataRangeTransport, ' + + 'passwordCallback or progressCallback argument'); + } if (pdfDataRangeTransport) { if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) { // Not a PDFDataRangeTransport instance, trying to add missing properties. pdfDataRangeTransport = Object.create(pdfDataRangeTransport); pdfDataRangeTransport.length = src.length; pdfDataRangeTransport.initialData = src.initialData; + if (!pdfDataRangeTransport.abort) { + pdfDataRangeTransport.abort = function () {}; + } } src = Object.create(src); src.range = pdfDataRangeTransport; @@ -1943,6 +1955,7 @@ PDFJS.getDocument = function getDocument(src, workerInitializedCapability.promise.then(function transportInitialized() { transport.fetchDocument(task, params); }); + task._transport = transport; return task; }; @@ -1955,6 +1968,7 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { /** @constructs PDFDocumentLoadingTask */ function PDFDocumentLoadingTask() { this._capability = createPromiseCapability(); + this._transport = null; /** * Callback to request a password if wrong or no password was provided. @@ -1980,7 +1994,14 @@ var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { return this._capability.promise; }, - // TODO add cancel or abort method + /** + * Aborts all network requests and destroys worker. + * @return {Promise} A promise that is resolved after destruction activity + * is completed. + */ + destroy: function () { + return this._transport.destroy(); + }, /** * Registers callbacks to indicate the document loading completion. @@ -2067,6 +2088,9 @@ var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() { requestDataRange: function PDFDataRangeTransport_requestDataRange(begin, end) { throw new Error('Abstract method PDFDataRangeTransport.requestDataRange'); + }, + + abort: function PDFDataRangeTransport_abort() { } }; return PDFDataRangeTransport; @@ -2080,9 +2104,10 @@ PDFJS.PDFDataRangeTransport = PDFDataRangeTransport; * @class */ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { - function PDFDocumentProxy(pdfInfo, transport) { + function PDFDocumentProxy(pdfInfo, transport, loadingTask) { this.pdfInfo = pdfInfo; this.transport = transport; + this.loadingTask = loadingTask; } PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ { /** @@ -2205,7 +2230,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * Destroys current document instance and terminates worker. */ destroy: function PDFDocumentProxy_destroy() { - this.transport.destroy(); + return this.transport.destroy(); } }; return PDFDocumentProxy; @@ -2282,8 +2307,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() { this.commonObjs = transport.commonObjs; this.objs = new PDFObjects(); this.cleanupAfterRender = false; - this.pendingDestroy = false; + this.pendingCleanup = false; this.intentStates = {}; + this.destroyed = false; } PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ { /** @@ -2347,7 +2373,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { // If there was a pending destroy cancel it so no cleanup happens during // this call to render. - this.pendingDestroy = false; + this.pendingCleanup = false; var renderingIntent = (params.intent === 'print' ? 'print' : 'display'); @@ -2388,13 +2414,14 @@ var PDFPageProxy = (function PDFPageProxyClosure() { // Obsolete parameter support if (params.continueCallback) { + deprecated('render is used with continueCallback parameter'); renderTask.onContinue = params.continueCallback; } var self = this; intentState.displayReadyCapability.promise.then( function pageDisplayReadyPromise(transparency) { - if (self.pendingDestroy) { + if (self.pendingCleanup) { complete(); return; } @@ -2414,9 +2441,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() { } if (self.cleanupAfterRender) { - self.pendingDestroy = true; + self.pendingCleanup = true; } - self._tryDestroy(); + self._tryCleanup(); if (error) { internalRenderTask.capability.reject(error); @@ -2477,20 +2504,52 @@ var PDFPageProxy = (function PDFPageProxyClosure() { pageIndex: this.pageNumber - 1 }); }, + + /** + * Destroys page object. + */ + _destroy: function PDFPageProxy_destroy() { + this.destroyed = true; + this.transport.pageCache[this.pageIndex] = null; + + var waitOn = []; + Object.keys(this.intentStates).forEach(function(intent) { + var intentState = this.intentStates[intent]; + intentState.renderTasks.forEach(function(renderTask) { + var renderCompleted = renderTask.capability.promise. + catch(function () {}); // ignoring failures + waitOn.push(renderCompleted); + renderTask.cancel(); + }); + }, this); + this.objs.clear(); + this.annotationsPromise = null; + this.pendingCleanup = false; + return Promise.all(waitOn); + }, + /** - * Destroys resources allocated by the page. + * Cleans up resources allocated by the page. (deprecated) */ - destroy: function PDFPageProxy_destroy() { - this.pendingDestroy = true; - this._tryDestroy(); + destroy: function() { + deprecated('page destroy method, use cleanup() instead'); + this.cleanup(); + }, + + /** + * Cleans up resources allocated by the page. + */ + cleanup: function PDFPageProxy_cleanup() { + this.pendingCleanup = true; + this._tryCleanup(); }, /** * For internal use only. Attempts to clean up if rendering is in a state * where that's possible. * @ignore */ - _tryDestroy: function PDFPageProxy__destroy() { - if (!this.pendingDestroy || + _tryCleanup: function PDFPageProxy_tryCleanup() { + if (!this.pendingCleanup || Object.keys(this.intentStates).some(function(intent) { var intentState = this.intentStates[intent]; return (intentState.renderTasks.length !== 0 || @@ -2504,7 +2563,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { }, this); this.objs.clear(); this.annotationsPromise = null; - this.pendingDestroy = false; + this.pendingCleanup = false; }, /** * For internal use only. @@ -2542,7 +2601,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { if (operatorListChunk.lastChunk) { intentState.receivingOperatorList = false; - this._tryDestroy(); + this._tryCleanup(); } } }; @@ -2560,6 +2619,8 @@ var WorkerTransport = (function WorkerTransportClosure() { this.commonObjs = new PDFObjects(); this.loadingTask = null; + this.destroyed = false; + this.destroyCapability = null; this.pageCache = []; this.pagePromises = []; @@ -2618,15 +2679,40 @@ var WorkerTransport = (function WorkerTransportClosure() { } WorkerTransport.prototype = { destroy: function WorkerTransport_destroy() { + if (this.destroyCapability) { + return this.destroyCapability.promise; + } + + this.destroyed = true; + this.destroyCapability = createPromiseCapability(); + + var waitOn = []; + // We need to wait for all renderings to be completed, e.g. + // timeout/rAF can take a long time. + this.pageCache.forEach(function (page) { + if (page) { + waitOn.push(page._destroy()); + } + }); this.pageCache = []; this.pagePromises = []; var self = this; - this.messageHandler.sendWithPromise('Terminate', null).then(function () { + // We also need to wait for the worker to finish its long running tasks. + var terminated = this.messageHandler.sendWithPromise('Terminate', null); + waitOn.push(terminated); + Promise.all(waitOn).then(function () { FontLoader.clear(); if (self.worker) { self.worker.terminate(); } - }); + if (self.pdfDataRangeTransport) { + self.pdfDataRangeTransport.abort(); + self.pdfDataRangeTransport = null; + } + self.messageHandler = null; + self.destroyCapability.resolve(); + }, this.destroyCapability.reject); + return this.destroyCapability.promise; }, setupFakeWorker: function WorkerTransport_setupFakeWorker() { @@ -2700,9 +2786,10 @@ var WorkerTransport = (function WorkerTransportClosure() { messageHandler.on('GetDoc', function transportDoc(data) { var pdfInfo = data.pdfInfo; this.numPages = data.pdfInfo.numPages; - var pdfDocument = new PDFDocumentProxy(pdfInfo, this); + var loadingTask = this.loadingTask; + var pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask); this.pdfDocument = pdfDocument; - this.loadingTask._capability.resolve(pdfDocument); + loadingTask._capability.resolve(pdfDocument); }, this); messageHandler.on('NeedPassword', @@ -2903,6 +2990,12 @@ var WorkerTransport = (function WorkerTransportClosure() { }, fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) { + if (this.destroyed) { + loadingTask._capability.reject(new Error('Loading aborted')); + this.destroyCapability.resolve(); + return; + } + this.loadingTask = loadingTask; source.disableAutoFetch = PDFJS.disableAutoFetch; @@ -2941,6 +3034,9 @@ var WorkerTransport = (function WorkerTransportClosure() { var promise = this.messageHandler.sendWithPromise('GetPage', { pageIndex: pageIndex }).then(function (pageInfo) { + if (this.destroyed) { + throw new Error('Transport destroyed'); + } var page = new PDFPageProxy(pageIndex, pageInfo, this); this.pageCache[pageIndex] = page; return page; @@ -2998,7 +3094,7 @@ var WorkerTransport = (function WorkerTransportClosure() { for (var i = 0, ii = this.pageCache.length; i < ii; i++) { var page = this.pageCache[i]; if (page) { - page.destroy(); + page.cleanup(); } } this.commonObjs.clear(); diff --git a/build/pdf.worker.js b/build/pdf.worker.js index b90af4750..c0a087e8c 100644 --- a/build/pdf.worker.js +++ b/build/pdf.worker.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.1.546'; -PDFJS.build = 'b3a9a0d'; +PDFJS.version = '1.1.551'; +PDFJS.build = '2a5616c'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -225,6 +225,11 @@ function warn(msg) { } } +// Deprecated API function -- treated as warnings. +function deprecated(details) { + warn('Deprecated API usage: ' + details); +} + // Fatal errors that should trigger the fallback UI and halt execution by // throwing an exception. function error(msg) { @@ -2172,7 +2177,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { this.chunksNeededByRequest = {}; this.requestsByChunk = {}; - this.callbacksByRequest = {}; + this.promisesByRequest = {}; this.progressiveDataLength = 0; this._loadedStreamCapability = createPromiseCapability(); @@ -2191,12 +2196,11 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { // contiguous ranges to load in as few requests as possible requestAllChunks: function ChunkedStreamManager_requestAllChunks() { var missingChunks = this.stream.getMissingChunks(); - this.requestChunks(missingChunks); + this._requestChunks(missingChunks); return this._loadedStreamCapability.promise; }, - requestChunks: function ChunkedStreamManager_requestChunks(chunks, - callback) { + _requestChunks: function ChunkedStreamManager_requestChunks(chunks) { var requestId = this.currRequestId++; var chunksNeeded; @@ -2209,13 +2213,11 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } if (isEmptyObj(chunksNeeded)) { - if (callback) { - callback(); - } - return; + return Promise.resolve(); } - this.callbacksByRequest[requestId] = callback; + var capability = createPromiseCapability(); + this.promisesByRequest[requestId] = capability; var chunksToRequest = []; for (var chunk in chunksNeeded) { @@ -2228,7 +2230,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } if (!chunksToRequest.length) { - return; + return capability.promise; } var groupedChunksToRequest = this.groupChunks(chunksToRequest); @@ -2239,6 +2241,8 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length); this.sendRequest(begin, end); } + + return capability.promise; }, getStream: function ChunkedStreamManager_getStream() { @@ -2246,8 +2250,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { }, // Loads any chunks in the requested range that are not yet loaded - requestRange: function ChunkedStreamManager_requestRange( - begin, end, callback) { + requestRange: function ChunkedStreamManager_requestRange(begin, end) { end = Math.min(end, this.length); @@ -2259,11 +2262,10 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { chunks.push(chunk); } - this.requestChunks(chunks, callback); + return this._requestChunks(chunks); }, - requestRanges: function ChunkedStreamManager_requestRanges(ranges, - callback) { + requestRanges: function ChunkedStreamManager_requestRanges(ranges) { ranges = ranges || []; var chunksToRequest = []; @@ -2278,7 +2280,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } chunksToRequest.sort(function(a, b) { return a - b; }); - this.requestChunks(chunksToRequest, callback); + return this._requestChunks(chunksToRequest); }, // Groups a sorted array of chunks into as few contiguous larger @@ -2377,17 +2379,15 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { nextEmptyChunk = this.stream.nextEmptyChunk(endChunk); } if (isInt(nextEmptyChunk)) { - this.requestChunks([nextEmptyChunk]); + this._requestChunks([nextEmptyChunk]); } } for (i = 0; i < loadedRequests.length; ++i) { requestId = loadedRequests[i]; - var callback = this.callbacksByRequest[requestId]; - delete this.callbacksByRequest[requestId]; - if (callback) { - callback(); - } + var capability = this.promisesByRequest[requestId]; + delete this.promisesByRequest[requestId]; + capability.resolve(); } this.msgHandler.send('DocProgress', { @@ -2408,6 +2408,16 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { getEndChunk: function ChunkedStreamManager_getEndChunk(end) { var chunk = Math.floor((end - 1) / this.chunkSize) + 1; return chunk; + }, + + abort: function ChunkedStreamManager_abort() { + if (this.networkManager) { + this.networkManager.abortAllRequests(); + } + for(var requestId in this.promisesByRequest) { + var capability = this.promisesByRequest[requestId]; + capability.reject(new Error('Request was aborted')); + } } }; @@ -2579,7 +2589,8 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { reject(e); return; } - pdfManager.streamManager.requestRange(e.begin, e.end, ensureHelper); + pdfManager.streamManager.requestRange(e.begin, e.end). + then(ensureHelper, reject); } } @@ -2589,11 +2600,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { NetworkPdfManager.prototype.requestRange = function NetworkPdfManager_requestRange(begin, end) { - return new Promise(function (resolve) { - this.streamManager.requestRange(begin, end, function() { - resolve(); - }); - }.bind(this)); + return this.streamManager.requestRange(begin, end); }; NetworkPdfManager.prototype.requestLoadedStream = @@ -2613,7 +2620,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { NetworkPdfManager.prototype.terminate = function NetworkPdfManager_terminate() { - this.streamManager.networkManager.abortAllRequests(); + this.streamManager.abort(); }; return NetworkPdfManager; @@ -2758,7 +2765,7 @@ var Page = (function PageClosure() { }.bind(this)); }, - getOperatorList: function Page_getOperatorList(handler, intent) { + getOperatorList: function Page_getOperatorList(handler, task, intent) { var self = this; var pdfManager = this.pdfManager; @@ -2791,8 +2798,8 @@ var Page = (function PageClosure() { pageIndex: self.pageIndex, intent: intent }); - return partialEvaluator.getOperatorList(contentStream, self.resources, - opList).then(function () { + return partialEvaluator.getOperatorList(contentStream, task, + self.resources, opList).then(function () { return opList; }); }); @@ -2809,7 +2816,7 @@ var Page = (function PageClosure() { } var annotationsReadyPromise = Annotation.appendToOperatorList( - annotations, pageOpList, pdfManager, partialEvaluator, intent); + annotations, pageOpList, pdfManager, partialEvaluator, task, intent); return annotationsReadyPromise.then(function () { pageOpList.flush(true); return pageOpList; @@ -2817,7 +2824,7 @@ var Page = (function PageClosure() { }); }, - extractTextContent: function Page_extractTextContent() { + extractTextContent: function Page_extractTextContent(task) { var handler = { on: function nullHandlerOn() {}, send: function nullHandlerSend() {} @@ -2846,6 +2853,7 @@ var Page = (function PageClosure() { self.fontCache); return partialEvaluator.getTextContent(contentStream, + task, self.resources); }); }, @@ -4498,9 +4506,9 @@ var XRef = (function XRefClosure() { resolve(xref.fetch(ref, suppressEncryption)); } catch (e) { if (e instanceof MissingDataException) { - streamManager.requestRange(e.begin, e.end, function () { + streamManager.requestRange(e.begin, e.end).then(function () { tryFetch(resolve, reject); - }); + }, reject); return; } reject(e); @@ -4776,6 +4784,7 @@ var ObjectLoader = (function() { this.keys = keys; this.xref = xref; this.refSet = null; + this.capability = null; } ObjectLoader.prototype = { @@ -4796,11 +4805,11 @@ var ObjectLoader = (function() { nodesToVisit.push(this.obj[keys[i]]); } - this.walk(nodesToVisit); + this._walk(nodesToVisit); return this.capability.promise; }, - walk: function ObjectLoader_walk(nodesToVisit) { + _walk: function ObjectLoader_walk(nodesToVisit) { var nodesToRevisit = []; var pendingRequests = []; // DFS walk of the object graph. @@ -4847,7 +4856,7 @@ var ObjectLoader = (function() { } if (pendingRequests.length) { - this.xref.stream.manager.requestRanges(pendingRequests, + this.xref.stream.manager.requestRanges(pendingRequests).then( function pendingRequestCallback() { nodesToVisit = nodesToRevisit; for (var i = 0; i < nodesToRevisit.length; i++) { @@ -4858,8 +4867,8 @@ var ObjectLoader = (function() { this.refSet.remove(node); } } - this.walk(nodesToVisit); - }.bind(this)); + this._walk(nodesToVisit); + }.bind(this), this.capability.reject); return; } // Everything is loaded. @@ -5233,7 +5242,7 @@ var Annotation = (function AnnotationClosure() { }.bind(this)); }, - getOperatorList: function Annotation_getOperatorList(evaluator) { + getOperatorList: function Annotation_getOperatorList(evaluator, task) { if (!this.appearance) { return Promise.resolve(new OperatorList()); @@ -5260,7 +5269,8 @@ var Annotation = (function AnnotationClosure() { return resourcesPromise.then(function(resources) { var opList = new OperatorList(); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); - return evaluator.getOperatorList(self.appearance, resources, opList). + return evaluator.getOperatorList(self.appearance, task, + resources, opList). then(function () { opList.addOp(OPS.endAnnotation, []); self.appearance.reset(); @@ -5271,7 +5281,7 @@ var Annotation = (function AnnotationClosure() { }; Annotation.appendToOperatorList = function Annotation_appendToOperatorList( - annotations, opList, pdfManager, partialEvaluator, intent) { + annotations, opList, pdfManager, partialEvaluator, task, intent) { function reject(e) { annotationsReadyCapability.reject(e); @@ -5284,7 +5294,7 @@ var Annotation = (function AnnotationClosure() { if (intent === 'display' && annotations[i].isViewable() || intent === 'print' && annotations[i].isPrintable()) { annotationPromises.push( - annotations[i].getOperatorList(partialEvaluator)); + annotations[i].getOperatorList(partialEvaluator, task)); } } Promise.all(annotationPromises).then(function(datas) { @@ -5516,9 +5526,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { } Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { - getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) { + getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator, + task) { if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator); + return Annotation.prototype.getOperatorList.call(this, evaluator, task); } var opList = new OperatorList(); @@ -5531,7 +5542,8 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { } var stream = new Stream(stringToBytes(data.defaultAppearance)); - return evaluator.getOperatorList(stream, this.fieldResources, opList). + return evaluator.getOperatorList(stream, task, + this.fieldResources, opList). then(function () { return opList; }); @@ -10919,6 +10931,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { buildFormXObject: function PartialEvaluator_buildFormXObject(resources, xobj, smask, operatorList, + task, initialState) { var matrix = xobj.dict.getArray('Matrix'); var bbox = xobj.dict.getArray('BBox'); @@ -10951,7 +10964,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]); - return this.getOperatorList(xobj, + return this.getOperatorList(xobj, task, (xobj.dict.get('Resources') || resources), operatorList, initialState). then(function () { operatorList.addOp(OPS.paintFormXObjectEnd, []); @@ -11063,7 +11076,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, handleSMask: function PartialEvaluator_handleSmask(smask, resources, - operatorList, + operatorList, task, stateManager) { var smaskContent = smask.get('G'); var smaskOptions = { @@ -11071,13 +11084,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { backdrop: smask.get('BC') }; return this.buildFormXObject(resources, smaskContent, smaskOptions, - operatorList, stateManager.state.clone()); + operatorList, task, stateManager.state.clone()); }, handleTilingType: function PartialEvaluator_handleTilingType(fn, args, resources, pattern, patternDict, - operatorList) { + operatorList, task) { // Create an IR of the pattern code. var tilingOpList = new OperatorList(); // Merge the available resources, to prevent issues when the patternDict @@ -11085,8 +11098,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var resourcesArray = [patternDict.get('Resources'), resources]; var patternResources = Dict.merge(this.xref, resourcesArray); - return this.getOperatorList(pattern, patternResources, tilingOpList).then( - function () { + return this.getOperatorList(pattern, task, patternResources, + tilingOpList).then(function () { // Add the dependencies to the parent operator list so they are // resolved before sub operator list is executed synchronously. operatorList.addDependencies(tilingOpList.dependencies); @@ -11099,7 +11112,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { handleSetFont: function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef, - operatorList, state) { + operatorList, task, state) { // TODO(mack): Not needed? var fontName; if (fontArgs) { @@ -11113,8 +11126,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (!translated.font.isType3Font) { return translated; } - return translated.loadType3Data(self, resources, operatorList).then( - function () { + return translated.loadType3Data(self, resources, operatorList, task). + then(function () { return translated; }); }).then(function (translated) { @@ -11161,8 +11174,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, setGState: function PartialEvaluator_setGState(resources, gState, - operatorList, xref, - stateManager) { + operatorList, task, + xref, stateManager) { // This array holds the converted/processed state data. var gStateObj = []; var gStateMap = gState.map; @@ -11186,8 +11199,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { break; case 'Font': promise = promise.then(function () { - return self.handleSetFont(resources, null, value[0], - operatorList, stateManager.state). + return self.handleSetFont(resources, null, value[0], operatorList, + task, stateManager.state). then(function (loadedName) { operatorList.addDependency(loadedName); gStateObj.push([key, [loadedName, value[1]]]); @@ -11206,7 +11219,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (isDict(dict)) { promise = promise.then(function () { return self.handleSMask(dict, resources, operatorList, - stateManager); + task, stateManager); }); gStateObj.push([key, true]); } else { @@ -11387,7 +11400,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, handleColorN: function PartialEvaluator_handleColorN(operatorList, fn, args, - cs, patterns, resources, xref) { + cs, patterns, resources, task, xref) { // compile tiling patterns var patternName = args[args.length - 1]; // SCN/scn applies patterns along with normal colors @@ -11400,7 +11413,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (typeNum === TILING_PATTERN) { var color = cs.base ? cs.base.getRgb(args, 0) : null; return this.handleTilingType(fn, color, resources, pattern, - dict, operatorList); + dict, operatorList, task); } else if (typeNum === SHADING_PATTERN) { var shading = dict.get('Shading'); var matrix = dict.get('Matrix'); @@ -11417,6 +11430,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, getOperatorList: function PartialEvaluator_getOperatorList(stream, + task, resources, operatorList, initialState) { @@ -11435,6 +11449,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var timeSlotManager = new TimeSlotManager(); return new Promise(function next(resolve, reject) { + task.ensureNotTerminated(); timeSlotManager.reset(); var stop, operation = {}, i, ii, cs; while (!(stop = timeSlotManager.check())) { @@ -11477,7 +11492,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (type.name === 'Form') { stateManager.save(); return self.buildFormXObject(resources, xobj, null, - operatorList, + operatorList, task, stateManager.state.clone()). then(function () { stateManager.restore(); @@ -11501,8 +11516,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { case OPS.setFont: var fontSize = args[1]; // eagerly collect all fonts - return self.handleSetFont(resources, args, null, - operatorList, stateManager.state). + return self.handleSetFont(resources, args, null, operatorList, + task, stateManager.state). then(function (loadedName) { operatorList.addDependency(loadedName); operatorList.addOp(OPS.setFont, [loadedName, fontSize]); @@ -11608,7 +11623,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { cs = stateManager.state.fillColorSpace; if (cs.name === 'Pattern') { return self.handleColorN(operatorList, OPS.setFillColorN, - args, cs, patterns, resources, xref).then(function() { + args, cs, patterns, resources, task, xref).then(function() { next(resolve, reject); }, reject); } @@ -11619,7 +11634,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { cs = stateManager.state.strokeColorSpace; if (cs.name === 'Pattern') { return self.handleColorN(operatorList, OPS.setStrokeColorN, - args, cs, patterns, resources, xref).then(function() { + args, cs, patterns, resources, task, xref).then(function() { next(resolve, reject); }, reject); } @@ -11653,8 +11668,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } var gState = extGState.get(dictName.name); - return self.setGState(resources, gState, operatorList, xref, - stateManager).then(function() { + return self.setGState(resources, gState, operatorList, task, + xref, stateManager).then(function() { next(resolve, reject); }, reject); case OPS.moveTo: @@ -11692,7 +11707,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (stop) { deferred.then(function () { next(resolve, reject); - }); + }, reject); return; } // Some PDFs don't close all restores inside object/form. @@ -11704,7 +11719,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }); }, - getTextContent: function PartialEvaluator_getTextContent(stream, resources, + getTextContent: function PartialEvaluator_getTextContent(stream, task, + resources, stateManager) { stateManager = (stateManager || new StateManager(new TextState())); @@ -11882,6 +11898,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var timeSlotManager = new TimeSlotManager(); return new Promise(function next(resolve, reject) { + task.ensureNotTerminated(); timeSlotManager.reset(); var stop, operation = {}, args = []; while (!(stop = timeSlotManager.check())) { @@ -12037,7 +12054,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { stateManager.transform(matrix); } - return self.getTextContent(xobj, + return self.getTextContent(xobj, task, xobj.dict.get('Resources') || resources, stateManager). then(function (formTextContent) { Util.appendToArray(bidiTexts, formTextContent.items); @@ -12077,7 +12094,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (stop) { deferred.then(function () { next(resolve, reject); - }); + }, reject); return; } resolve(textContent); @@ -12642,7 +12659,7 @@ var TranslatedFont = (function TranslatedFontClosure() { ]); this.sent = true; }, - loadType3Data: function (evaluator, resources, parentOperatorList) { + loadType3Data: function (evaluator, resources, parentOperatorList, task) { assert(this.font.isType3Font); if (this.type3Loaded) { @@ -12659,7 +12676,7 @@ var TranslatedFont = (function TranslatedFontClosure() { loadCharProcsPromise = loadCharProcsPromise.then(function (key) { var glyphStream = charProcs[key]; var operatorList = new OperatorList(); - return evaluator.getOperatorList(glyphStream, fontResources, + return evaluator.getOperatorList(glyphStream, task, fontResources, operatorList).then(function () { charProcOperatorList[key] = operatorList.getIR(); @@ -34222,9 +34239,58 @@ var NullStream = (function NullStreamClosure() { })(); +var WorkerTask = (function WorkerTaskClosure() { + function WorkerTask(name) { + this.name = name; + this.terminated = false; + this._capability = createPromiseCapability(); + } + + WorkerTask.prototype = { + get finished() { + return this._capability.promise; + }, + + finish: function () { + this._capability.resolve(); + }, + + terminate: function () { + this.terminated = true; + }, + + ensureNotTerminated: function () { + if (this.terminated) { + throw new Error('Worker task was terminated'); + } + } + }; + + return WorkerTask; +})(); + var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { setup: function wphSetup(handler) { var pdfManager; + var terminated = false; + var cancelXHRs = null; + var WorkerTasks = []; + + function ensureNotTerminated() { + if (terminated) { + throw new Error('Worker was terminated'); + } + } + + function startWorkerTask(task) { + WorkerTasks.push(task); + } + + function finishWorkerTask(task) { + task.finish(); + var i = WorkerTasks.indexOf(task); + WorkerTasks.splice(i, 1); + } function loadDocument(recoveryMode) { var loadDocumentCapability = createPromiseCapability(); @@ -34261,13 +34327,14 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { function getPdfManager(data) { var pdfManagerCapability = createPromiseCapability(); + var pdfManager; var source = data.source; var disableRange = data.disableRange; if (source.data) { try { pdfManager = new LocalPdfManager(source.data, source.password); - pdfManagerCapability.resolve(); + pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); } @@ -34275,7 +34342,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } else if (source.chunkedViewerLoading) { try { pdfManager = new NetworkPdfManager(source, handler); - pdfManagerCapability.resolve(); + pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); } @@ -34336,6 +34403,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } catch (ex) { pdfManagerCapability.reject(ex); } + cancelXHRs = null; }, onProgressiveData: source.disableStream ? null : @@ -34376,10 +34444,11 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { // the data is array, instantiating directly from it try { pdfManager = new LocalPdfManager(pdfFile, source.password); - pdfManagerCapability.resolve(); + pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); } + cancelXHRs = null; }, onError: function onError(status) { @@ -34394,6 +34463,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { ') while retrieving PDF "' + source.url + '".', status); handler.send('UnexpectedResponse', exception); } + cancelXHRs = null; }, onProgress: function onProgress(evt) { @@ -34404,6 +34474,10 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } }); + cancelXHRs = function () { + networkManager.abortRequest(fullRequestXhrId); + }; + return pdfManagerCapability.promise; } @@ -34436,8 +34510,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); handler.on('GetDocRequest', function wphSetupDoc(data) { - var onSuccess = function(doc) { + ensureNotTerminated(); handler.send('GetDoc', { pdfInfo: doc }); }; @@ -34460,6 +34534,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } }; + ensureNotTerminated(); + PDFJS.maxImageSize = data.maxImageSize === undefined ? -1 : data.maxImageSize; PDFJS.disableFontFace = data.disableFontFace; @@ -34469,13 +34545,26 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { null : data.cMapUrl; PDFJS.cMapPacked = data.cMapPacked === true; - getPdfManager(data).then(function () { + getPdfManager(data).then(function (newPdfManager) { + if (terminated) { + // We were in a process of setting up the manager, but it got + // terminated in the middle. + newPdfManager.terminate(); + throw new Error('Worker was terminated'); + } + + pdfManager = newPdfManager; + handler.send('PDFManagerReady', null); pdfManager.onLoadedStream().then(function(stream) { handler.send('DataLoaded', { length: stream.bytes.byteLength }); }); }).then(function pdfManagerReady() { + ensureNotTerminated(); + loadDocument(false).then(onSuccess, function loadFailure(ex) { + ensureNotTerminated(); + // Try again with recoveryMode == true if (!(ex instanceof XRefParseException)) { if (ex instanceof PasswordException) { @@ -34490,6 +34579,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { pdfManager.requestLoadedStream(); pdfManager.onLoadedStream().then(function() { + ensureNotTerminated(); + loadDocument(true).then(onSuccess, onFailure); }); }, onFailure); @@ -34580,17 +34671,25 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); handler.on('RenderPageRequest', function wphSetupRenderPage(data) { - pdfManager.getPage(data.pageIndex).then(function(page) { + var pageIndex = data.pageIndex; + pdfManager.getPage(pageIndex).then(function(page) { + var task = new WorkerTask('RenderPageRequest: page ' + pageIndex); + startWorkerTask(task); - var pageNum = data.pageIndex + 1; + var pageNum = pageIndex + 1; var start = Date.now(); // Pre compile the pdf page and fetch the fonts/images. - page.getOperatorList(handler, data.intent).then(function(operatorList) { + page.getOperatorList(handler, task, data.intent).then( + function(operatorList) { + finishWorkerTask(task); info('page=' + pageNum + ' - getOperatorList: time=' + (Date.now() - start) + 'ms, len=' + operatorList.fnArray.length); - }, function(e) { + finishWorkerTask(task); + if (task.terminated) { + return; // ignoring errors from the terminated thread + } var minimumStackMessage = 'worker.js: while trying to getPage() and getOperatorList()'; @@ -34625,13 +34724,23 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }, this); handler.on('GetTextContent', function wphExtractText(data) { - return pdfManager.getPage(data.pageIndex).then(function(page) { - var pageNum = data.pageIndex + 1; + var pageIndex = data.pageIndex; + return pdfManager.getPage(pageIndex).then(function(page) { + var task = new WorkerTask('GetTextContent: page ' + pageIndex); + startWorkerTask(task); + var pageNum = pageIndex + 1; var start = Date.now(); - return page.extractTextContent().then(function(textContent) { + return page.extractTextContent(task).then(function(textContent) { + finishWorkerTask(task); info('text indexing: page=' + pageNum + ' - time=' + (Date.now() - start) + 'ms'); return textContent; + }, function (reason) { + finishWorkerTask(task); + if (task.terminated) { + return; // ignoring errors from the terminated thread + } + throw reason; }); }); }); @@ -34641,7 +34750,22 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); handler.on('Terminate', function wphTerminate(data) { - pdfManager.terminate(); + terminated = true; + if (pdfManager) { + pdfManager.terminate(); + pdfManager = null; + } + if (cancelXHRs) { + cancelXHRs(); + } + + var waitOn = []; + WorkerTasks.forEach(function (task) { + waitOn.push(task.finished); + task.terminate(); + }); + + return Promise.all(waitOn).then(function () {}); }); } }; diff --git a/package.json b/package.json index 37d61c946..cb3aff6b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.1.546", + "version": "1.1.551", "description": "Generic build of Mozilla's PDF.js library.", "keywords": [ "Mozilla", diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 632ff83c3..f259c044c 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -972,7 +972,7 @@ var PDFPageView = (function PDFPageViewClosure() { this.zoomLayer = null; this.reset(); if (this.pdfPage) { - this.pdfPage.destroy(); + this.pdfPage.cleanup(); } }, @@ -1317,9 +1317,9 @@ var PDFPageView = (function PDFPageViewClosure() { canvasContext: ctx, viewport: this.viewport, // intent: 'default', // === 'display' - continueCallback: renderContinueCallback }; var renderTask = this.renderTask = this.pdfPage.render(renderContext); + renderTask.onContinue = renderContinueCallback; this.renderTask.promise.then( function pdfPageRenderCallback() {