From a7273c8efeea8ea0e97fa11cbfbe2c28de1ad0f6 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 27 Mar 2019 17:54:05 +0100 Subject: [PATCH] Avoid dispatching range requests to fetch PDF data that's already loaded with streaming (PR 10675 follow-up) *Please note:* This patch purposely ignores `src/display/network.js`, since its support for progressive reading depends on the non-standard `moz-chunked-arraybuffer` responseType which is currently in the process of being removed. --- src/display/api.js | 15 +++++++++++++++ src/display/fetch_stream.js | 7 +++++++ src/display/node_stream.js | 19 +++++++++++++------ src/display/transport_stream.js | 16 ++++++++++++++-- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/display/api.js b/src/display/api.js index 9c0a9ba0e..b27191ad5 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -1814,6 +1814,21 @@ class WorkerTransport { const rangeReader = this._networkStream.getRangeReader(data.begin, data.end); + // When streaming is enabled, it's possible that the data requested here + // has already been fetched via the `_fullRequestReader` implementation. + // However, given that the PDF data is loaded asynchronously on the + // main-thread and then sent via `postMessage` to the worker-thread, + // it may not have been available during parsing (hence the attempt to + // use range requests here). + // + // To avoid wasting time and resources here, we'll thus *not* dispatch + // range requests if the data was already loaded but has not been sent to + // the worker-thread yet (which will happen via the `_fullRequestReader`). + if (!rangeReader) { + sink.close(); + return; + } + sink.onPull = () => { rangeReader.read().then(function({ value, done, }) { if (done) { diff --git a/src/display/fetch_stream.js b/src/display/fetch_stream.js index 20ca3281f..7dcd3e537 100644 --- a/src/display/fetch_stream.js +++ b/src/display/fetch_stream.js @@ -42,6 +42,10 @@ class PDFFetchStream { this._rangeRequestReaders = []; } + get _progressiveDataLength() { + return (this._fullRequestReader ? this._fullRequestReader._loaded : 0); + } + getFullReader() { assert(!this._fullRequestReader); this._fullRequestReader = new PDFFetchStreamReader(this); @@ -49,6 +53,9 @@ class PDFFetchStream { } getRangeReader(begin, end) { + if (end <= this._progressiveDataLength) { + return null; + } let reader = new PDFFetchStreamRangeReader(this, begin, end); this._rangeRequestReaders.push(reader); return reader; diff --git a/src/display/node_stream.js b/src/display/node_stream.js index 5b744f867..a38b02bbd 100644 --- a/src/display/node_stream.js +++ b/src/display/node_stream.js @@ -54,19 +54,26 @@ class PDFNodeStream { this.isFsUrl = this.url.protocol === 'file:'; this.httpHeaders = (this.isHttp && source.httpHeaders) || {}; - this._fullRequest = null; + this._fullRequestReader = null; this._rangeRequestReaders = []; } + get _progressiveDataLength() { + return (this._fullRequestReader ? this._fullRequestReader._loaded : 0); + } + getFullReader() { - assert(!this._fullRequest); - this._fullRequest = this.isFsUrl ? + assert(!this._fullRequestReader); + this._fullRequestReader = this.isFsUrl ? new PDFNodeStreamFsFullReader(this) : new PDFNodeStreamFullReader(this); - return this._fullRequest; + return this._fullRequestReader; } getRangeReader(start, end) { + if (end <= this._progressiveDataLength) { + return null; + } let rangeReader = this.isFsUrl ? new PDFNodeStreamFsRangeReader(this, start, end) : new PDFNodeStreamRangeReader(this, start, end); @@ -75,8 +82,8 @@ class PDFNodeStream { } cancelAllRequests(reason) { - if (this._fullRequest) { - this._fullRequest.cancel(reason); + if (this._fullRequestReader) { + this._fullRequestReader.cancel(reason); } let readers = this._rangeRequestReaders.slice(0); diff --git a/src/display/transport_stream.js b/src/display/transport_stream.js index fe02746b7..2c055e51e 100644 --- a/src/display/transport_stream.js +++ b/src/display/transport_stream.js @@ -70,6 +70,10 @@ var PDFDataTransportStream = (function PDFDataTransportStreamClosure() { } }, + get _progressiveDataLength() { + return (this._fullRequestReader ? this._fullRequestReader._loaded : 0); + }, + _onProgress: function PDFDataTransportStream_onDataProgress(evt) { if (this._rangeReaders.length > 0) { // Reporting to first range reader. @@ -96,6 +100,9 @@ var PDFDataTransportStream = (function PDFDataTransportStreamClosure() { }, getRangeReader: function PDFDataTransportStream_getRangeReader(begin, end) { + if (end <= this._progressiveDataLength) { + return null; + } var reader = new PDFDataTransportStreamRangeReader(this, begin, end); this._pdfDataRangeTransport.requestDataRange(begin, end); this._rangeReaders.push(reader); @@ -121,6 +128,10 @@ var PDFDataTransportStream = (function PDFDataTransportStreamClosure() { this._done = false; this._filename = null; this._queuedChunks = queuedChunks || []; + this._loaded = 0; + for (const chunk of this._queuedChunks) { + this._loaded += chunk.byteLength; + } this._requests = []; this._headersReady = Promise.resolve(); stream._fullRequestReader = this; @@ -135,9 +146,10 @@ var PDFDataTransportStream = (function PDFDataTransportStreamClosure() { if (this._requests.length > 0) { var requestCapability = this._requests.shift(); requestCapability.resolve({ value: chunk, done: false, }); - return; + } else { + this._queuedChunks.push(chunk); } - this._queuedChunks.push(chunk); + this._loaded += chunk.byteLength; }, get headersReady() {