From 0385131a9aad762eef78f9568fb1f96bfce43d56 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 18 Nov 2013 11:17:26 -0800 Subject: [PATCH] Leave initial request open until the viewer is ready to switch to range requests. --- .../firefox/components/PdfStreamConverter.js | 41 +++++++++---------- src/core/chunked_stream.js | 31 ++++++++++++++ src/core/pdf_manager.js | 3 +- src/display/api.js | 3 ++ web/viewer.js | 3 +- 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index db84e8254..293e7ca30 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -439,9 +439,12 @@ var RangedChromeActions = (function RangedChromeActionsClosure() { * This is for range requests */ function RangedChromeActions( - domWindow, contentDispositionFilename, originalRequest) { + domWindow, contentDispositionFilename, originalRequest, + dataListener) { ChromeActions.call(this, domWindow, contentDispositionFilename); + this.dataListener = dataListener; + this.originalRequest = originalRequest; this.pdfUrl = originalRequest.URI.spec; this.contentLength = originalRequest.contentLength; @@ -487,11 +490,15 @@ var RangedChromeActions = (function RangedChromeActionsClosure() { proto.constructor = RangedChromeActions; proto.initPassiveLoading = function RangedChromeActions_initPassiveLoading() { + this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); + this.originalRequest = null; this.domWindow.postMessage({ pdfjsLoadAction: 'supportsRangedLoading', pdfUrl: this.pdfUrl, - length: this.contentLength + length: this.contentLength, + data: this.dataListener.getData() }, '*'); + this.dataListener = null; return true; }; @@ -692,8 +699,8 @@ PdfStreamConverter.prototype = { * 1. asyncConvertData stores the listener * 2. onStartRequest creates a new channel, streams the viewer * 3. If range requests are supported: - * 3.1. Suspends and cancels the request so we can issue range - * requests instead. + * 3.1. Leave the request open until the viewer is ready to switch to + * range requests. * * If range rquests are not supported: * 3.1. Read the stream as it's loaded in onDataAvailable to send @@ -779,18 +786,12 @@ PdfStreamConverter.prototype = { PdfJsTelemetry.onViewerIsUsed(); PdfJsTelemetry.onDocumentSize(aRequest.contentLength); - if (!rangeRequest) { - // Creating storage for PDF data - var contentLength = aRequest.contentLength; - this.dataListener = new PdfDataListener(contentLength); - this.binaryStream = Cc['@mozilla.org/binaryinputstream;1'] - .createInstance(Ci.nsIBinaryInputStream); - } else { - // Suspend the request so we're not consuming any of the stream, - // but we can't cancel the request yet. Otherwise, the original - // listener will think we do not want to go the new PDF url - aRequest.suspend(); - } + + // Creating storage for PDF data + var contentLength = aRequest.contentLength; + this.dataListener = new PdfDataListener(contentLength); + this.binaryStream = Cc['@mozilla.org/binaryinputstream;1'] + .createInstance(Ci.nsIBinaryInputStream); // Create a new channel that is viewer loaded as a resource. var ioService = Services.io; @@ -816,12 +817,8 @@ PdfStreamConverter.prototype = { var domWindow = getDOMWindow(channel); var actions; if (rangeRequest) { - // We are going to be issuing range requests, so cancel the - // original request - aRequest.resume(); - aRequest.cancel(Cr.NS_BINDING_ABORTED); - actions = new RangedChromeActions(domWindow, - contentDispositionFilename, aRequest); + actions = new RangedChromeActions( + domWindow, contentDispositionFilename, aRequest, dataListener); } else { actions = new StandardChromeActions( domWindow, contentDispositionFilename, dataListener); diff --git a/src/core/chunked_stream.js b/src/core/chunked_stream.js index 0eb814092..2c323c9ac 100644 --- a/src/core/chunked_stream.js +++ b/src/core/chunked_stream.js @@ -30,6 +30,7 @@ var ChunkedStream = (function ChunkedStreamClosure() { this.numChunksLoaded = 0; this.numChunks = Math.ceil(length / chunkSize); this.manager = manager; + this.initialDataLength = 0; } // required methods for a stream. if a particular stream does not @@ -77,11 +78,26 @@ var ChunkedStream = (function ChunkedStreamClosure() { } }, + onReceiveInitialData: function (data) { + this.bytes.set(data); + this.initialDataLength = data.length; + var endChunk = this.end === data.length ? + this.numChunks : Math.floor(data.length / this.chunkSize); + for (var i = 0; i < endChunk; i++) { + this.loadedChunks[i] = true; + ++this.numChunksLoaded; + } + }, + ensureRange: function ChunkedStream_ensureRange(begin, end) { if (begin >= end) { return; } + if (end <= this.initialDataLength) { + return; + } + var chunkSize = this.chunkSize; var beginChunk = Math.floor(begin / chunkSize); var endChunk = Math.floor((end - 1) / chunkSize) + 1; @@ -243,10 +259,25 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { this.callbacksByRequest = {}; this.loadedStream = new Promise(); + if (args.initialData) { + this.setInitialData(args.initialData); + } } ChunkedStreamManager.prototype = { + setInitialData: function ChunkedStreamManager_setInitialData(data) { + this.stream.onReceiveInitialData(data); + if (this.stream.allChunksLoaded()) { + this.loadedStream.resolve(this.stream); + } else if (this.msgHandler) { + this.msgHandler.send('DocProgress', { + loaded: data.length, + total: this.length + }); + } + }, + onLoadedStream: function ChunkedStreamManager_getLoadedStream() { return this.loadedStream; }, diff --git a/src/core/pdf_manager.js b/src/core/pdf_manager.js index a6641ae4c..468a3a62f 100644 --- a/src/core/pdf_manager.js +++ b/src/core/pdf_manager.js @@ -139,7 +139,8 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { msgHandler: msgHandler, httpHeaders: args.httpHeaders, chunkedViewerLoading: args.chunkedViewerLoading, - disableAutoFetch: args.disableAutoFetch + disableAutoFetch: args.disableAutoFetch, + initialData: args.initialData }; this.streamManager = new ChunkedStreamManager(args.length, CHUNK_SIZE, args.url, params); diff --git a/src/display/api.js b/src/display/api.js index 28e0a8480..c66f67f17 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -98,6 +98,9 @@ PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug; * - data - A typed array with PDF data. * - httpHeaders - Basic authentication headers. * - password - For decrypting password-protected PDFs. + * - initialData - A typed array with the first portion or all of the pdf data. + * Used by the extension since some data is already loaded + * before the switch to range requests. * * @param {object} pdfDataRangeTransport is optional. It is used if you want * to manually serve range requests for data in the PDF. See viewer.js for diff --git a/web/viewer.js b/web/viewer.js index 267da1b89..e156bc84d 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -445,7 +445,8 @@ var PDFView = { switch (args.pdfjsLoadAction) { case 'supportsRangedLoading': PDFView.open(args.pdfUrl, 0, undefined, pdfDataRangeTransport, { - length: args.length + length: args.length, + initialData: args.data }); break; case 'range':