Merge pull request #5263 from yurydelendik/stream
Implement streaming using moz-chunk-arraybuffer
This commit is contained in:
commit
9c56c6f9f6
@ -50,6 +50,12 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"disableStream": {
|
||||||
|
"title": "Disable streaming for requests",
|
||||||
|
"description": "Whether to disable streaming for requests (not recommended).",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"disableAutoFetch": {
|
"disableAutoFetch": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
@ -178,6 +178,7 @@ function makeContentReadable(obj, window) {
|
|||||||
function PdfDataListener(length) {
|
function PdfDataListener(length) {
|
||||||
this.length = length; // less than 0, if length is unknown
|
this.length = length; // less than 0, if length is unknown
|
||||||
this.data = new Uint8Array(length >= 0 ? length : 0x10000);
|
this.data = new Uint8Array(length >= 0 ? length : 0x10000);
|
||||||
|
this.position = 0;
|
||||||
this.loaded = 0;
|
this.loaded = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +201,11 @@ PdfDataListener.prototype = {
|
|||||||
this.loaded = willBeLoaded;
|
this.loaded = willBeLoaded;
|
||||||
this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0));
|
this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0));
|
||||||
},
|
},
|
||||||
|
readData: function PdfDataListener_readData() {
|
||||||
|
var data = this.data.subarray(this.position, this.loaded);
|
||||||
|
this.position = this.loaded;
|
||||||
|
return data;
|
||||||
|
},
|
||||||
getData: function PdfDataListener_getData() {
|
getData: function PdfDataListener_getData() {
|
||||||
var data = this.data;
|
var data = this.data;
|
||||||
if (this.loaded != data.length)
|
if (this.loaded != data.length)
|
||||||
@ -523,11 +529,13 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
|
|||||||
*/
|
*/
|
||||||
function RangedChromeActions(
|
function RangedChromeActions(
|
||||||
domWindow, contentDispositionFilename, originalRequest,
|
domWindow, contentDispositionFilename, originalRequest,
|
||||||
dataListener) {
|
rangeEnabled, streamingEnabled, dataListener) {
|
||||||
|
|
||||||
ChromeActions.call(this, domWindow, contentDispositionFilename);
|
ChromeActions.call(this, domWindow, contentDispositionFilename);
|
||||||
this.dataListener = dataListener;
|
this.dataListener = dataListener;
|
||||||
this.originalRequest = originalRequest;
|
this.originalRequest = originalRequest;
|
||||||
|
this.rangeEnabled = rangeEnabled;
|
||||||
|
this.streamingEnabled = streamingEnabled;
|
||||||
|
|
||||||
this.pdfUrl = originalRequest.URI.spec;
|
this.pdfUrl = originalRequest.URI.spec;
|
||||||
this.contentLength = originalRequest.contentLength;
|
this.contentLength = originalRequest.contentLength;
|
||||||
@ -585,20 +593,46 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
|
|||||||
proto.constructor = RangedChromeActions;
|
proto.constructor = RangedChromeActions;
|
||||||
|
|
||||||
proto.initPassiveLoading = function RangedChromeActions_initPassiveLoading() {
|
proto.initPassiveLoading = function RangedChromeActions_initPassiveLoading() {
|
||||||
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
|
var self = this;
|
||||||
this.originalRequest = null;
|
var data;
|
||||||
|
if (!this.streamingEnabled) {
|
||||||
|
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||||
|
this.originalRequest = null;
|
||||||
|
data = this.dataListener.getData();
|
||||||
|
this.dataListener = null;
|
||||||
|
} else {
|
||||||
|
data = this.dataListener.readData();
|
||||||
|
|
||||||
|
this.dataListener.onprogress = function (loaded, total) {
|
||||||
|
self.domWindow.postMessage({
|
||||||
|
pdfjsLoadAction: 'progressiveRead',
|
||||||
|
loaded: loaded,
|
||||||
|
total: total,
|
||||||
|
chunk: self.dataListener.readData()
|
||||||
|
}, '*');
|
||||||
|
};
|
||||||
|
this.dataListener.oncomplete = function () {
|
||||||
|
delete self.dataListener;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.domWindow.postMessage({
|
this.domWindow.postMessage({
|
||||||
pdfjsLoadAction: 'supportsRangedLoading',
|
pdfjsLoadAction: 'supportsRangedLoading',
|
||||||
|
rangeEnabled: this.rangeEnabled,
|
||||||
|
streamingEnabled: this.streamingEnabled,
|
||||||
pdfUrl: this.pdfUrl,
|
pdfUrl: this.pdfUrl,
|
||||||
length: this.contentLength,
|
length: this.contentLength,
|
||||||
data: this.dataListener.getData()
|
data: data
|
||||||
}, '*');
|
}, '*');
|
||||||
this.dataListener = null;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
proto.requestDataRange = function RangedChromeActions_requestDataRange(args) {
|
proto.requestDataRange = function RangedChromeActions_requestDataRange(args) {
|
||||||
|
if (!this.rangeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var begin = args.begin;
|
var begin = args.begin;
|
||||||
var end = args.end;
|
var end = args.end;
|
||||||
var domWindow = this.domWindow;
|
var domWindow = this.domWindow;
|
||||||
@ -840,7 +874,8 @@ PdfStreamConverter.prototype = {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
var rangeRequest = false;
|
var rangeRequest = false;
|
||||||
if (isHttpRequest) {
|
var hash = aRequest.URI.ref;
|
||||||
|
if (isHttpRequest && !getBoolPref(PREF_PREFIX + '.disableRange', false)) {
|
||||||
var contentEncoding = 'identity';
|
var contentEncoding = 'identity';
|
||||||
try {
|
try {
|
||||||
contentEncoding = aRequest.getResponseHeader('Content-Encoding');
|
contentEncoding = aRequest.getResponseHeader('Content-Encoding');
|
||||||
@ -851,12 +886,13 @@ PdfStreamConverter.prototype = {
|
|||||||
acceptRanges = aRequest.getResponseHeader('Accept-Ranges');
|
acceptRanges = aRequest.getResponseHeader('Accept-Ranges');
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
var hash = aRequest.URI.ref;
|
|
||||||
rangeRequest = contentEncoding === 'identity' &&
|
rangeRequest = contentEncoding === 'identity' &&
|
||||||
acceptRanges === 'bytes' &&
|
acceptRanges === 'bytes' &&
|
||||||
aRequest.contentLength >= 0 &&
|
aRequest.contentLength >= 0 &&
|
||||||
hash.indexOf('disableRange=true') < 0;
|
hash.toLowerCase().indexOf('disablerange=true') < 0;
|
||||||
}
|
}
|
||||||
|
var streamRequest = !getBoolPref(PREF_PREFIX + '.disableStream', false) &&
|
||||||
|
hash.toLowerCase().indexOf('disablestream=true') < 0;
|
||||||
|
|
||||||
aRequest.QueryInterface(Ci.nsIChannel);
|
aRequest.QueryInterface(Ci.nsIChannel);
|
||||||
|
|
||||||
@ -914,12 +950,13 @@ PdfStreamConverter.prototype = {
|
|||||||
// may have changed during a redirect.
|
// may have changed during a redirect.
|
||||||
var domWindow = getDOMWindow(channel);
|
var domWindow = getDOMWindow(channel);
|
||||||
var actions;
|
var actions;
|
||||||
if (rangeRequest) {
|
if (rangeRequest || streamRequest) {
|
||||||
actions = new RangedChromeActions(
|
actions = new RangedChromeActions(
|
||||||
domWindow, contentDispositionFilename, aRequest, dataListener);
|
domWindow, contentDispositionFilename, aRequest,
|
||||||
|
rangeRequest, streamRequest, dataListener);
|
||||||
} else {
|
} else {
|
||||||
actions = new StandardChromeActions(
|
actions = new StandardChromeActions(
|
||||||
domWindow, contentDispositionFilename, dataListener);
|
domWindow, contentDispositionFilename, dataListener);
|
||||||
}
|
}
|
||||||
var requestListener = new RequestListener(actions);
|
var requestListener = new RequestListener(actions);
|
||||||
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
|
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
|
||||||
|
@ -30,7 +30,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
this.numChunksLoaded = 0;
|
this.numChunksLoaded = 0;
|
||||||
this.numChunks = Math.ceil(length / chunkSize);
|
this.numChunks = Math.ceil(length / chunkSize);
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.initialDataLength = 0;
|
this.progressiveDataLength = 0;
|
||||||
this.lastSuccessfulEnsureByteChunk = -1; // a single-entry cache
|
this.lastSuccessfulEnsureByteChunk = -1; // a single-entry cache
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
getMissingChunks: function ChunkedStream_getMissingChunks() {
|
getMissingChunks: function ChunkedStream_getMissingChunks() {
|
||||||
var chunks = [];
|
var chunks = [];
|
||||||
for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
|
for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
|
||||||
if (!(chunk in this.loadedChunks)) {
|
if (!this.loadedChunks[chunk]) {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,21 +73,29 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
var curChunk;
|
var curChunk;
|
||||||
|
|
||||||
for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
||||||
if (!(curChunk in this.loadedChunks)) {
|
if (!this.loadedChunks[curChunk]) {
|
||||||
this.loadedChunks[curChunk] = true;
|
this.loadedChunks[curChunk] = true;
|
||||||
++this.numChunksLoaded;
|
++this.numChunksLoaded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onReceiveInitialData: function ChunkedStream_onReceiveInitialData(data) {
|
onReceiveProgressiveData:
|
||||||
this.bytes.set(data);
|
function ChunkedStream_onReceiveProgressiveData(data) {
|
||||||
this.initialDataLength = data.length;
|
var position = this.progressiveDataLength;
|
||||||
var endChunk = (this.end === data.length ?
|
var beginChunk = Math.floor(position / this.chunkSize);
|
||||||
this.numChunks : Math.floor(data.length / this.chunkSize));
|
|
||||||
for (var i = 0; i < endChunk; i++) {
|
this.bytes.set(new Uint8Array(data), position);
|
||||||
this.loadedChunks[i] = true;
|
position += data.byteLength;
|
||||||
++this.numChunksLoaded;
|
this.progressiveDataLength = position;
|
||||||
|
var endChunk = position >= this.end ? this.numChunks :
|
||||||
|
Math.floor(position / this.chunkSize);
|
||||||
|
var curChunk;
|
||||||
|
for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
|
||||||
|
if (!this.loadedChunks[curChunk]) {
|
||||||
|
this.loadedChunks[curChunk] = true;
|
||||||
|
++this.numChunksLoaded;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -97,7 +105,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(chunk in this.loadedChunks)) {
|
if (!this.loadedChunks[chunk]) {
|
||||||
throw new MissingDataException(pos, pos + 1);
|
throw new MissingDataException(pos, pos + 1);
|
||||||
}
|
}
|
||||||
this.lastSuccessfulEnsureByteChunk = chunk;
|
this.lastSuccessfulEnsureByteChunk = chunk;
|
||||||
@ -108,7 +116,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end <= this.initialDataLength) {
|
if (end <= this.progressiveDataLength) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +124,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
var beginChunk = Math.floor(begin / chunkSize);
|
var beginChunk = Math.floor(begin / chunkSize);
|
||||||
var endChunk = Math.floor((end - 1) / chunkSize) + 1;
|
var endChunk = Math.floor((end - 1) / chunkSize) + 1;
|
||||||
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
|
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
|
||||||
if (!(chunk in this.loadedChunks)) {
|
if (!this.loadedChunks[chunk]) {
|
||||||
throw new MissingDataException(begin, end);
|
throw new MissingDataException(begin, end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,13 +133,13 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) {
|
nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) {
|
||||||
var chunk, n;
|
var chunk, n;
|
||||||
for (chunk = beginChunk, n = this.numChunks; chunk < n; ++chunk) {
|
for (chunk = beginChunk, n = this.numChunks; chunk < n; ++chunk) {
|
||||||
if (!(chunk in this.loadedChunks)) {
|
if (!this.loadedChunks[chunk]) {
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wrap around to beginning
|
// Wrap around to beginning
|
||||||
for (chunk = 0; chunk < beginChunk; ++chunk) {
|
for (chunk = 0; chunk < beginChunk; ++chunk) {
|
||||||
if (!(chunk in this.loadedChunks)) {
|
if (!this.loadedChunks[chunk]) {
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,7 +147,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
hasChunk: function ChunkedStream_hasChunk(chunk) {
|
hasChunk: function ChunkedStream_hasChunk(chunk) {
|
||||||
return chunk in this.loadedChunks;
|
return !!this.loadedChunks[chunk];
|
||||||
},
|
},
|
||||||
|
|
||||||
get length() {
|
get length() {
|
||||||
@ -238,7 +246,7 @@ var ChunkedStream = (function ChunkedStreamClosure() {
|
|||||||
var endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
|
var endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
|
||||||
var missingChunks = [];
|
var missingChunks = [];
|
||||||
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
|
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
|
||||||
if (!(chunk in this.loadedChunks)) {
|
if (!this.loadedChunks[chunk]) {
|
||||||
missingChunks.push(chunk);
|
missingChunks.push(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,28 +308,16 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
|
|||||||
this.chunksNeededByRequest = {};
|
this.chunksNeededByRequest = {};
|
||||||
this.requestsByChunk = {};
|
this.requestsByChunk = {};
|
||||||
this.callbacksByRequest = {};
|
this.callbacksByRequest = {};
|
||||||
|
this.progressiveDataLength = 0;
|
||||||
|
|
||||||
this._loadedStreamCapability = createPromiseCapability();
|
this._loadedStreamCapability = createPromiseCapability();
|
||||||
|
|
||||||
if (args.initialData) {
|
if (args.initialData) {
|
||||||
this.setInitialData(args.initialData);
|
this.onReceiveData({chunk: args.initialData});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChunkedStreamManager.prototype = {
|
ChunkedStreamManager.prototype = {
|
||||||
|
|
||||||
setInitialData: function ChunkedStreamManager_setInitialData(data) {
|
|
||||||
this.stream.onReceiveInitialData(data);
|
|
||||||
if (this.stream.allChunksLoaded()) {
|
|
||||||
this._loadedStreamCapability.resolve(this.stream);
|
|
||||||
} else if (this.msgHandler) {
|
|
||||||
this.msgHandler.send('DocProgress', {
|
|
||||||
loaded: data.length,
|
|
||||||
total: this.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onLoadedStream: function ChunkedStreamManager_getLoadedStream() {
|
onLoadedStream: function ChunkedStreamManager_getLoadedStream() {
|
||||||
return this._loadedStreamCapability.promise;
|
return this._loadedStreamCapability.promise;
|
||||||
},
|
},
|
||||||
@ -459,13 +455,21 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
|
|||||||
|
|
||||||
onReceiveData: function ChunkedStreamManager_onReceiveData(args) {
|
onReceiveData: function ChunkedStreamManager_onReceiveData(args) {
|
||||||
var chunk = args.chunk;
|
var chunk = args.chunk;
|
||||||
var begin = args.begin;
|
var isProgressive = args.begin === undefined;
|
||||||
|
var begin = isProgressive ? this.progressiveDataLength : args.begin;
|
||||||
var end = begin + chunk.byteLength;
|
var end = begin + chunk.byteLength;
|
||||||
|
|
||||||
var beginChunk = this.getBeginChunk(begin);
|
var beginChunk = Math.floor(begin / this.chunkSize);
|
||||||
var endChunk = this.getEndChunk(end);
|
var endChunk = end < this.length ? Math.floor(end / this.chunkSize) :
|
||||||
|
Math.ceil(end / this.chunkSize);
|
||||||
|
|
||||||
|
if (isProgressive) {
|
||||||
|
this.stream.onReceiveProgressiveData(chunk);
|
||||||
|
this.progressiveDataLength = end;
|
||||||
|
} else {
|
||||||
|
this.stream.onReceiveData(begin, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
this.stream.onReceiveData(begin, chunk);
|
|
||||||
if (this.stream.allChunksLoaded()) {
|
if (this.stream.allChunksLoaded()) {
|
||||||
this._loadedStreamCapability.resolve(this.stream);
|
this._loadedStreamCapability.resolve(this.stream);
|
||||||
}
|
}
|
||||||
|
@ -68,11 +68,11 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
var length = data.length;
|
var length = data.length;
|
||||||
var buffer = new Uint8Array(length);
|
var array = new Uint8Array(length);
|
||||||
for (var i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
buffer[i] = data.charCodeAt(i) & 0xFF;
|
array[i] = data.charCodeAt(i) & 0xFF;
|
||||||
}
|
}
|
||||||
return buffer;
|
return array.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManager.prototype = {
|
NetworkManager.prototype = {
|
||||||
@ -87,11 +87,11 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||||||
return this.request(args);
|
return this.request(args);
|
||||||
},
|
},
|
||||||
|
|
||||||
requestFull: function NetworkManager_requestRange(listeners) {
|
requestFull: function NetworkManager_requestFull(listeners) {
|
||||||
return this.request(listeners);
|
return this.request(listeners);
|
||||||
},
|
},
|
||||||
|
|
||||||
request: function NetworkManager_requestRange(args) {
|
request: function NetworkManager_request(args) {
|
||||||
var xhr = this.getXhr();
|
var xhr = this.getXhr();
|
||||||
var xhrId = this.currXhrId++;
|
var xhrId = this.currXhrId++;
|
||||||
var pendingRequest = this.pendingRequests[xhrId] = {
|
var pendingRequest = this.pendingRequests[xhrId] = {
|
||||||
@ -115,27 +115,54 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||||||
pendingRequest.expectedStatus = 200;
|
pendingRequest.expectedStatus = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
xhr.responseType = 'arraybuffer';
|
if (args.onProgressiveData) {
|
||||||
|
xhr.responseType = 'moz-chunked-arraybuffer';
|
||||||
if (args.onProgress) {
|
if (xhr.responseType === 'moz-chunked-arraybuffer') {
|
||||||
xhr.onprogress = args.onProgress;
|
pendingRequest.onProgressiveData = args.onProgressiveData;
|
||||||
|
pendingRequest.mozChunked = true;
|
||||||
|
} else {
|
||||||
|
xhr.responseType = 'arraybuffer';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xhr.responseType = 'arraybuffer';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.onError) {
|
if (args.onError) {
|
||||||
xhr.onerror = function(evt) {
|
xhr.onerror = function(evt) {
|
||||||
args.onError(xhr.status);
|
args.onError(xhr.status);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
|
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
|
||||||
|
xhr.onprogress = this.onProgress.bind(this, xhrId);
|
||||||
|
|
||||||
pendingRequest.onHeadersReceived = args.onHeadersReceived;
|
pendingRequest.onHeadersReceived = args.onHeadersReceived;
|
||||||
pendingRequest.onDone = args.onDone;
|
pendingRequest.onDone = args.onDone;
|
||||||
pendingRequest.onError = args.onError;
|
pendingRequest.onError = args.onError;
|
||||||
|
pendingRequest.onProgress = args.onProgress;
|
||||||
|
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
|
|
||||||
return xhrId;
|
return xhrId;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onProgress: function NetworkManager_onProgress(xhrId, evt) {
|
||||||
|
var pendingRequest = this.pendingRequests[xhrId];
|
||||||
|
if (!pendingRequest) {
|
||||||
|
// Maybe abortRequest was called...
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingRequest.mozChunked) {
|
||||||
|
var chunk = getArrayBuffer(pendingRequest.xhr);
|
||||||
|
pendingRequest.onProgressiveData(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
var onProgress = pendingRequest.onProgress;
|
||||||
|
if (onProgress) {
|
||||||
|
onProgress(evt);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onStateChange: function NetworkManager_onStateChange(xhrId, evt) {
|
onStateChange: function NetworkManager_onStateChange(xhrId, evt) {
|
||||||
var pendingRequest = this.pendingRequests[xhrId];
|
var pendingRequest = this.pendingRequests[xhrId];
|
||||||
if (!pendingRequest) {
|
if (!pendingRequest) {
|
||||||
@ -196,6 +223,8 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||||||
begin: begin,
|
begin: begin,
|
||||||
chunk: chunk
|
chunk: chunk
|
||||||
});
|
});
|
||||||
|
} else if (pendingRequest.onProgressiveData) {
|
||||||
|
pendingRequest.onDone(null);
|
||||||
} else {
|
} else {
|
||||||
pendingRequest.onDone({
|
pendingRequest.onDone({
|
||||||
begin: 0,
|
begin: 0,
|
||||||
@ -215,6 +244,10 @@ var NetworkManager = (function NetworkManagerClosure() {
|
|||||||
return this.pendingRequests[xhrId].xhr;
|
return this.pendingRequests[xhrId].xhr;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isStreamingRequest: function NetworkManager_isStreamingRequest(xhrId) {
|
||||||
|
return !!(this.pendingRequests[xhrId].onProgressiveData);
|
||||||
|
},
|
||||||
|
|
||||||
isPendingRequest: function NetworkManager_isPendingRequest(xhrId) {
|
isPendingRequest: function NetworkManager_isPendingRequest(xhrId) {
|
||||||
return xhrId in this.pendingRequests;
|
return xhrId in this.pendingRequests;
|
||||||
},
|
},
|
||||||
|
@ -65,6 +65,10 @@ var BasePdfManager = (function BasePdfManagerClosure() {
|
|||||||
return new NotImplementedException();
|
return new NotImplementedException();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sendProgressiveData: function BasePdfManager_sendProgressiveData(chunk) {
|
||||||
|
return new NotImplementedException();
|
||||||
|
},
|
||||||
|
|
||||||
updatePassword: function BasePdfManager_updatePassword(password) {
|
updatePassword: function BasePdfManager_updatePassword(password) {
|
||||||
this.pdfDocument.xref.password = this.password = password;
|
this.pdfDocument.xref.password = this.password = password;
|
||||||
if (this._passwordChangedCapability) {
|
if (this._passwordChangedCapability) {
|
||||||
@ -201,6 +205,11 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() {
|
|||||||
this.streamManager.requestAllChunks();
|
this.streamManager.requestAllChunks();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NetworkPdfManager.prototype.sendProgressiveData =
|
||||||
|
function NetworkPdfManager_sendProgressiveData(chunk) {
|
||||||
|
this.streamManager.onReceiveData({ chunk: chunk });
|
||||||
|
};
|
||||||
|
|
||||||
NetworkPdfManager.prototype.onLoadedStream =
|
NetworkPdfManager.prototype.onLoadedStream =
|
||||||
function NetworkPdfManager_getLoadedStream() {
|
function NetworkPdfManager_getLoadedStream() {
|
||||||
return this.streamManager.onLoadedStream();
|
return this.streamManager.onLoadedStream();
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
/* globals PDFJS, createPromiseCapability, LocalPdfManager, NetworkPdfManager,
|
/* globals PDFJS, createPromiseCapability, LocalPdfManager, NetworkPdfManager,
|
||||||
NetworkManager, isInt, RANGE_CHUNK_SIZE, MissingPDFException,
|
NetworkManager, isInt, RANGE_CHUNK_SIZE, MissingPDFException,
|
||||||
UnexpectedResponseException, PasswordException, Promise,
|
UnexpectedResponseException, PasswordException, Promise, warn,
|
||||||
PasswordResponses, InvalidPDFException, UnknownErrorException,
|
PasswordResponses, InvalidPDFException, UnknownErrorException,
|
||||||
XRefParseException, Ref, info, globalScope, error, MessageHandler */
|
XRefParseException, Ref, info, globalScope, error, MessageHandler */
|
||||||
|
|
||||||
@ -86,6 +86,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||||||
httpHeaders: source.httpHeaders,
|
httpHeaders: source.httpHeaders,
|
||||||
withCredentials: source.withCredentials
|
withCredentials: source.withCredentials
|
||||||
});
|
});
|
||||||
|
var cachedChunks = [];
|
||||||
var fullRequestXhrId = networkManager.requestFull({
|
var fullRequestXhrId = networkManager.requestFull({
|
||||||
onHeadersReceived: function onHeadersReceived() {
|
onHeadersReceived: function onHeadersReceived() {
|
||||||
if (disableRange) {
|
if (disableRange) {
|
||||||
@ -116,11 +117,18 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: by cancelling the full request, and then issuing range
|
if (networkManager.isStreamingRequest(fullRequestXhrId)) {
|
||||||
// requests, there will be an issue for sites where you can only
|
// We can continue fetching when progressive loading is enabled,
|
||||||
// request the pdf once. However, if this is the case, then the
|
// and we don't need the autoFetch feature.
|
||||||
// server should not be returning that it can support range requests.
|
source.disableAutoFetch = true;
|
||||||
networkManager.abortRequest(fullRequestXhrId);
|
} else {
|
||||||
|
// NOTE: by cancelling the full request, and then issuing range
|
||||||
|
// requests, there will be an issue for sites where you can only
|
||||||
|
// request the pdf once. However, if this is the case, then the
|
||||||
|
// server should not be returning that it can support range
|
||||||
|
// requests.
|
||||||
|
networkManager.abortRequest(fullRequestXhrId);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pdfManager = new NetworkPdfManager(source, handler);
|
pdfManager = new NetworkPdfManager(source, handler);
|
||||||
@ -130,10 +138,44 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onProgressiveData: PDFJS.disableStream ? null :
|
||||||
|
function onProgressiveData(chunk) {
|
||||||
|
if (!pdfManager) {
|
||||||
|
cachedChunks.push(chunk);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pdfManager.sendProgressiveData(chunk);
|
||||||
|
},
|
||||||
|
|
||||||
onDone: function onDone(args) {
|
onDone: function onDone(args) {
|
||||||
|
if (pdfManager) {
|
||||||
|
return; // already processed
|
||||||
|
}
|
||||||
|
|
||||||
|
var pdfFile;
|
||||||
|
if (args === null) {
|
||||||
|
// TODO add some streaming manager, e.g. for unknown length files.
|
||||||
|
// The data was returned in the onProgressiveData, combining...
|
||||||
|
var pdfFileLength = 0, pos = 0;
|
||||||
|
cachedChunks.forEach(function (chunk) {
|
||||||
|
pdfFileLength += chunk.byteLength;
|
||||||
|
});
|
||||||
|
if (source.length && pdfFileLength !== source.length) {
|
||||||
|
warn('reported HTTP length is different from actual');
|
||||||
|
}
|
||||||
|
var pdfFileArray = new Uint8Array(pdfFileLength);
|
||||||
|
cachedChunks.forEach(function (chunk) {
|
||||||
|
pdfFileArray.set(new Uint8Array(chunk), pos);
|
||||||
|
pos += chunk.byteLength;
|
||||||
|
});
|
||||||
|
pdfFile = pdfFileArray.buffer;
|
||||||
|
} else {
|
||||||
|
pdfFile = args.chunk;
|
||||||
|
}
|
||||||
|
|
||||||
// the data is array, instantiating directly from it
|
// the data is array, instantiating directly from it
|
||||||
try {
|
try {
|
||||||
pdfManager = new LocalPdfManager(args.chunk, source.password);
|
pdfManager = new LocalPdfManager(pdfFile, source.password);
|
||||||
pdfManagerCapability.resolve();
|
pdfManagerCapability.resolve();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
pdfManagerCapability.reject(ex);
|
pdfManagerCapability.reject(ex);
|
||||||
@ -228,6 +270,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||||||
PDFJS.cMapPacked = data.cMapPacked === true;
|
PDFJS.cMapPacked = data.cMapPacked === true;
|
||||||
|
|
||||||
getPdfManager(data).then(function () {
|
getPdfManager(data).then(function () {
|
||||||
|
handler.send('PDFManagerReady', null);
|
||||||
pdfManager.onLoadedStream().then(function(stream) {
|
pdfManager.onLoadedStream().then(function(stream) {
|
||||||
handler.send('DataLoaded', { length: stream.bytes.byteLength });
|
handler.send('DataLoaded', { length: stream.bytes.byteLength });
|
||||||
});
|
});
|
||||||
|
@ -86,6 +86,14 @@ PDFJS.workerSrc = (PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc);
|
|||||||
PDFJS.disableRange = (PDFJS.disableRange === undefined ?
|
PDFJS.disableRange = (PDFJS.disableRange === undefined ?
|
||||||
false : PDFJS.disableRange);
|
false : PDFJS.disableRange);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable streaming of PDF file data. By default PDF.js attempts to load PDF
|
||||||
|
* in chunks. This default behavior can be disabled.
|
||||||
|
* @var {boolean}
|
||||||
|
*/
|
||||||
|
PDFJS.disableStream = (PDFJS.disableStream === undefined ?
|
||||||
|
false : PDFJS.disableStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable pre-fetching of PDF file data. When range requests are enabled PDF.js
|
* Disable pre-fetching of PDF file data. When range requests are enabled PDF.js
|
||||||
* will automatically keep fetching more data even if it isn't needed to display
|
* will automatically keep fetching more data even if it isn't needed to display
|
||||||
@ -851,6 +859,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pdfDataRangeTransport.addProgressiveReadListener(function(chunk) {
|
||||||
|
messageHandler.send('OnDataRange', {
|
||||||
|
chunk: chunk
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
messageHandler.on('RequestDataRange',
|
messageHandler.on('RequestDataRange',
|
||||||
function transportDataRange(data) {
|
function transportDataRange(data) {
|
||||||
pdfDataRangeTransport.requestDataRange(data.begin, data.end);
|
pdfDataRangeTransport.requestDataRange(data.begin, data.end);
|
||||||
@ -911,6 +925,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||||||
this.downloadInfoCapability.resolve(data);
|
this.downloadInfoCapability.resolve(data);
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
|
messageHandler.on('PDFManagerReady', function transportPage(data) {
|
||||||
|
if (this.pdfDataRangeTransport) {
|
||||||
|
this.pdfDataRangeTransport.transportReady();
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
messageHandler.on('StartRenderPage', function transportRender(data) {
|
messageHandler.on('StartRenderPage', function transportRender(data) {
|
||||||
var page = this.pageCache[data.pageIndex];
|
var page = this.pageCache[data.pageIndex];
|
||||||
|
|
||||||
|
@ -167,6 +167,21 @@ if (typeof PDFJS === 'undefined') {
|
|||||||
// The worker will be using XHR, so we can save time and disable worker.
|
// The worker will be using XHR, so we can save time and disable worker.
|
||||||
PDFJS.disableWorker = true;
|
PDFJS.disableWorker = true;
|
||||||
|
|
||||||
|
Object.defineProperty(xhrPrototype, 'responseType', {
|
||||||
|
get: function xmlHttpRequestGetResponseType() {
|
||||||
|
return this._responseType || 'text';
|
||||||
|
},
|
||||||
|
set: function xmlHttpRequestSetResponseType(value) {
|
||||||
|
if (value === 'text' || value === 'arraybuffer') {
|
||||||
|
this._responseType = value;
|
||||||
|
if (value === 'arraybuffer' &&
|
||||||
|
typeof this.overrideMimeType === 'function') {
|
||||||
|
this.overrideMimeType('text/plain; charset=x-user-defined');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Support: IE9
|
// Support: IE9
|
||||||
if (typeof VBArray !== 'undefined') {
|
if (typeof VBArray !== 'undefined') {
|
||||||
Object.defineProperty(xhrPrototype, 'response', {
|
Object.defineProperty(xhrPrototype, 'response', {
|
||||||
@ -181,25 +196,20 @@ if (typeof PDFJS === 'undefined') {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// other browsers
|
Object.defineProperty(xhrPrototype, 'response', {
|
||||||
function responseTypeSetter() {
|
get: function xmlHttpRequestResponseGet() {
|
||||||
// will be only called to set "arraybuffer"
|
if (this.responseType !== 'arraybuffer') {
|
||||||
this.overrideMimeType('text/plain; charset=x-user-defined');
|
return this.responseText;
|
||||||
}
|
}
|
||||||
if (typeof xhr.overrideMimeType === 'function') {
|
var text = this.responseText;
|
||||||
Object.defineProperty(xhrPrototype, 'responseType',
|
var i, n = text.length;
|
||||||
{ set: responseTypeSetter });
|
var result = new Uint8Array(n);
|
||||||
}
|
for (i = 0; i < n; ++i) {
|
||||||
function responseGetter() {
|
result[i] = text.charCodeAt(i) & 0xFF;
|
||||||
var text = this.responseText;
|
}
|
||||||
var i, n = text.length;
|
return result.buffer;
|
||||||
var result = new Uint8Array(n);
|
|
||||||
for (i = 0; i < n; ++i) {
|
|
||||||
result[i] = text.charCodeAt(i) & 0xFF;
|
|
||||||
}
|
}
|
||||||
return result.buffer;
|
});
|
||||||
}
|
|
||||||
Object.defineProperty(xhrPrototype, 'response', { get: responseGetter });
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// window.btoa (base64 encode function) ?
|
// window.btoa (base64 encode function) ?
|
||||||
@ -471,6 +481,7 @@ if (typeof PDFJS === 'undefined') {
|
|||||||
|
|
||||||
if (isSafari || isOldAndroid) {
|
if (isSafari || isOldAndroid) {
|
||||||
PDFJS.disableRange = true;
|
PDFJS.disableRange = true;
|
||||||
|
PDFJS.disableStream = true;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ var DEFAULT_PREFERENCES = {
|
|||||||
enableWebGL: false,
|
enableWebGL: false,
|
||||||
pdfBugEnabled: false,
|
pdfBugEnabled: false,
|
||||||
disableRange: false,
|
disableRange: false,
|
||||||
|
disableStream: false,
|
||||||
disableAutoFetch: false,
|
disableAutoFetch: false,
|
||||||
disableFontFace: false,
|
disableFontFace: false,
|
||||||
//#if B2G
|
//#if B2G
|
||||||
|
@ -516,9 +516,15 @@ var PDFView = {
|
|||||||
|
|
||||||
//#if (FIREFOX || MOZCENTRAL)
|
//#if (FIREFOX || MOZCENTRAL)
|
||||||
initPassiveLoading: function pdfViewInitPassiveLoading() {
|
initPassiveLoading: function pdfViewInitPassiveLoading() {
|
||||||
|
var pdfDataRangeTransportReadyResolve;
|
||||||
|
var pdfDataRangeTransportReady = new Promise(function (resolve) {
|
||||||
|
pdfDataRangeTransportReadyResolve = resolve;
|
||||||
|
});
|
||||||
var pdfDataRangeTransport = {
|
var pdfDataRangeTransport = {
|
||||||
rangeListeners: [],
|
rangeListeners: [],
|
||||||
progressListeners: [],
|
progressListeners: [],
|
||||||
|
progressiveReadListeners: [],
|
||||||
|
ready: pdfDataRangeTransportReady,
|
||||||
|
|
||||||
addRangeListener: function PdfDataRangeTransport_addRangeListener(
|
addRangeListener: function PdfDataRangeTransport_addRangeListener(
|
||||||
listener) {
|
listener) {
|
||||||
@ -530,6 +536,11 @@ var PDFView = {
|
|||||||
this.progressListeners.push(listener);
|
this.progressListeners.push(listener);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addProgressiveReadListener:
|
||||||
|
function PdfDataRangeTransport_addProgressiveReadListener(listener) {
|
||||||
|
this.progressiveReadListeners.push(listener);
|
||||||
|
},
|
||||||
|
|
||||||
onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) {
|
onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) {
|
||||||
var listeners = this.rangeListeners;
|
var listeners = this.rangeListeners;
|
||||||
for (var i = 0, n = listeners.length; i < n; ++i) {
|
for (var i = 0, n = listeners.length; i < n; ++i) {
|
||||||
@ -538,10 +549,26 @@ var PDFView = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) {
|
onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) {
|
||||||
var listeners = this.progressListeners;
|
this.ready.then(function () {
|
||||||
for (var i = 0, n = listeners.length; i < n; ++i) {
|
var listeners = this.progressListeners;
|
||||||
listeners[i](loaded);
|
for (var i = 0, n = listeners.length; i < n; ++i) {
|
||||||
}
|
listeners[i](loaded);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
onDataProgressiveRead:
|
||||||
|
function PdfDataRangeTransport_onDataProgress(chunk) {
|
||||||
|
this.ready.then(function () {
|
||||||
|
var listeners = this.progressiveReadListeners;
|
||||||
|
for (var i = 0, n = listeners.length; i < n; ++i) {
|
||||||
|
listeners[i](chunk);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
transportReady: function PdfDataRangeTransport_transportReady() {
|
||||||
|
pdfDataRangeTransportReadyResolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
requestDataRange: function PdfDataRangeTransport_requestDataRange(
|
requestDataRange: function PdfDataRangeTransport_requestDataRange(
|
||||||
@ -574,6 +601,9 @@ var PDFView = {
|
|||||||
case 'rangeProgress':
|
case 'rangeProgress':
|
||||||
pdfDataRangeTransport.onDataProgress(args.loaded);
|
pdfDataRangeTransport.onDataProgress(args.loaded);
|
||||||
break;
|
break;
|
||||||
|
case 'progressiveRead':
|
||||||
|
pdfDataRangeTransport.onDataProgressiveRead(args.chunk);
|
||||||
|
break;
|
||||||
case 'progress':
|
case 'progress':
|
||||||
PDFView.progress(args.loaded / args.total);
|
PDFView.progress(args.loaded / args.total);
|
||||||
break;
|
break;
|
||||||
@ -1787,6 +1817,9 @@ function webViewerInitialized() {
|
|||||||
if ('disablerange' in hashParams) {
|
if ('disablerange' in hashParams) {
|
||||||
PDFJS.disableRange = (hashParams['disablerange'] === 'true');
|
PDFJS.disableRange = (hashParams['disablerange'] === 'true');
|
||||||
}
|
}
|
||||||
|
if ('disablestream' in hashParams) {
|
||||||
|
PDFJS.disableStream = (hashParams['disablestream'] === 'true');
|
||||||
|
}
|
||||||
if ('disableautofetch' in hashParams) {
|
if ('disableautofetch' in hashParams) {
|
||||||
PDFJS.disableAutoFetch = (hashParams['disableautofetch'] === 'true');
|
PDFJS.disableAutoFetch = (hashParams['disableautofetch'] === 'true');
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user