Currently when an exception is thrown, we try to reject `workerReadyCapability` with multiple arguments in src/core/api.js. This obviously doesn't work, hence this patch changes that to instead reject with the exception object as is. In src/core/worker.js the exception is currently (unncessarily) wrapped in an object, so this patch also simplifies that to directly send the exception object instead.
447 lines
14 KiB
JavaScript
447 lines
14 KiB
JavaScript
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
/* Copyright 2012 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/* globals error, globalScope, InvalidPDFException, info,
|
|
MissingPDFException, PasswordException, PDFJS, Promise,
|
|
UnknownErrorException, NetworkManager, LocalPdfManager,
|
|
NetworkPdfManager, XRefParseException, createPromiseCapability,
|
|
isInt, PasswordResponses, MessageHandler, Ref, RANGE_CHUNK_SIZE */
|
|
|
|
'use strict';
|
|
|
|
var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|
setup: function wphSetup(handler) {
|
|
var pdfManager;
|
|
|
|
function loadDocument(recoveryMode) {
|
|
var loadDocumentCapability = createPromiseCapability();
|
|
|
|
var parseSuccess = function parseSuccess() {
|
|
var numPagesPromise = pdfManager.ensureDoc('numPages');
|
|
var fingerprintPromise = pdfManager.ensureDoc('fingerprint');
|
|
var encryptedPromise = pdfManager.ensureXRef('encrypt');
|
|
Promise.all([numPagesPromise, fingerprintPromise,
|
|
encryptedPromise]).then(function onDocReady(results) {
|
|
var doc = {
|
|
numPages: results[0],
|
|
fingerprint: results[1],
|
|
encrypted: !!results[2],
|
|
};
|
|
loadDocumentCapability.resolve(doc);
|
|
},
|
|
parseFailure);
|
|
};
|
|
|
|
var parseFailure = function parseFailure(e) {
|
|
loadDocumentCapability.reject(e);
|
|
};
|
|
|
|
pdfManager.ensureDoc('checkHeader', []).then(function() {
|
|
pdfManager.ensureDoc('parseStartXRef', []).then(function() {
|
|
pdfManager.ensureDoc('parse', [recoveryMode]).then(
|
|
parseSuccess, parseFailure);
|
|
}, parseFailure);
|
|
}, parseFailure);
|
|
|
|
return loadDocumentCapability.promise;
|
|
}
|
|
|
|
function getPdfManager(data) {
|
|
var pdfManagerCapability = createPromiseCapability();
|
|
|
|
var source = data.source;
|
|
var disableRange = data.disableRange;
|
|
if (source.data) {
|
|
try {
|
|
pdfManager = new LocalPdfManager(source.data, source.password);
|
|
pdfManagerCapability.resolve();
|
|
} catch (ex) {
|
|
pdfManagerCapability.reject(ex);
|
|
}
|
|
return pdfManagerCapability.promise;
|
|
} else if (source.chunkedViewerLoading) {
|
|
try {
|
|
pdfManager = new NetworkPdfManager(source, handler);
|
|
pdfManagerCapability.resolve();
|
|
} catch (ex) {
|
|
pdfManagerCapability.reject(ex);
|
|
}
|
|
return pdfManagerCapability.promise;
|
|
}
|
|
|
|
var networkManager = new NetworkManager(source.url, {
|
|
httpHeaders: source.httpHeaders,
|
|
withCredentials: source.withCredentials
|
|
});
|
|
var fullRequestXhrId = networkManager.requestFull({
|
|
onHeadersReceived: function onHeadersReceived() {
|
|
if (disableRange) {
|
|
return;
|
|
}
|
|
|
|
var fullRequestXhr = networkManager.getRequestXhr(fullRequestXhrId);
|
|
if (fullRequestXhr.getResponseHeader('Accept-Ranges') !== 'bytes') {
|
|
return;
|
|
}
|
|
|
|
var contentEncoding =
|
|
fullRequestXhr.getResponseHeader('Content-Encoding') || 'identity';
|
|
if (contentEncoding !== 'identity') {
|
|
return;
|
|
}
|
|
|
|
var length = fullRequestXhr.getResponseHeader('Content-Length');
|
|
length = parseInt(length, 10);
|
|
if (!isInt(length)) {
|
|
return;
|
|
}
|
|
source.length = length;
|
|
if (length <= 2 * RANGE_CHUNK_SIZE) {
|
|
// The file size is smaller than the size of two chunks, so it does
|
|
// not make any sense to abort the request and retry with a range
|
|
// request.
|
|
return;
|
|
}
|
|
|
|
// 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 {
|
|
pdfManager = new NetworkPdfManager(source, handler);
|
|
pdfManagerCapability.resolve(pdfManager);
|
|
} catch (ex) {
|
|
pdfManagerCapability.reject(ex);
|
|
}
|
|
},
|
|
|
|
onDone: function onDone(args) {
|
|
// the data is array, instantiating directly from it
|
|
try {
|
|
pdfManager = new LocalPdfManager(args.chunk, source.password);
|
|
pdfManagerCapability.resolve();
|
|
} catch (ex) {
|
|
pdfManagerCapability.reject(ex);
|
|
}
|
|
},
|
|
|
|
onError: function onError(status) {
|
|
if (status === 404) {
|
|
var exception = new MissingPDFException('Missing PDF "' +
|
|
source.url + '".');
|
|
handler.send('MissingPDF', exception);
|
|
} else {
|
|
handler.send('DocError', 'Unexpected server response (' +
|
|
status + ') while retrieving PDF "' +
|
|
source.url + '".');
|
|
}
|
|
},
|
|
|
|
onProgress: function onProgress(evt) {
|
|
handler.send('DocProgress', {
|
|
loaded: evt.loaded,
|
|
total: evt.lengthComputable ? evt.total : source.length
|
|
});
|
|
}
|
|
});
|
|
|
|
return pdfManagerCapability.promise;
|
|
}
|
|
|
|
handler.on('test', function wphSetupTest(data) {
|
|
// check if Uint8Array can be sent to worker
|
|
if (!(data instanceof Uint8Array)) {
|
|
handler.send('test', false);
|
|
return;
|
|
}
|
|
// making sure postMessage transfers are working
|
|
var supportTransfers = data[0] === 255;
|
|
handler.postMessageTransfers = supportTransfers;
|
|
// check if the response property is supported by xhr
|
|
var xhr = new XMLHttpRequest();
|
|
var responseExists = 'response' in xhr;
|
|
// check if the property is actually implemented
|
|
try {
|
|
var dummy = xhr.responseType;
|
|
} catch (e) {
|
|
responseExists = false;
|
|
}
|
|
if (!responseExists) {
|
|
handler.send('test', false);
|
|
return;
|
|
}
|
|
handler.send('test', {
|
|
supportTypedArray: true,
|
|
supportTransfers: supportTransfers
|
|
});
|
|
});
|
|
|
|
handler.on('GetDocRequest', function wphSetupDoc(data) {
|
|
|
|
var onSuccess = function(doc) {
|
|
handler.send('GetDoc', { pdfInfo: doc });
|
|
};
|
|
|
|
var onFailure = function(e) {
|
|
if (e instanceof PasswordException) {
|
|
if (e.code === PasswordResponses.NEED_PASSWORD) {
|
|
handler.send('NeedPassword', e);
|
|
} else if (e.code === PasswordResponses.INCORRECT_PASSWORD) {
|
|
handler.send('IncorrectPassword', e);
|
|
}
|
|
} else if (e instanceof InvalidPDFException) {
|
|
handler.send('InvalidPDF', e);
|
|
} else if (e instanceof MissingPDFException) {
|
|
handler.send('MissingPDF', e);
|
|
} else {
|
|
handler.send('UnknownError',
|
|
new UnknownErrorException(e.message, e.toString()));
|
|
}
|
|
};
|
|
|
|
PDFJS.maxImageSize = data.maxImageSize === undefined ?
|
|
-1 : data.maxImageSize;
|
|
PDFJS.disableFontFace = data.disableFontFace;
|
|
PDFJS.disableCreateObjectURL = data.disableCreateObjectURL;
|
|
PDFJS.verbosity = data.verbosity;
|
|
PDFJS.cMapUrl = data.cMapUrl === undefined ?
|
|
null : data.cMapUrl;
|
|
PDFJS.cMapPacked = data.cMapPacked === true;
|
|
|
|
getPdfManager(data).then(function () {
|
|
pdfManager.onLoadedStream().then(function(stream) {
|
|
handler.send('DataLoaded', { length: stream.bytes.byteLength });
|
|
});
|
|
}).then(function pdfManagerReady() {
|
|
loadDocument(false).then(onSuccess, function loadFailure(ex) {
|
|
// Try again with recoveryMode == true
|
|
if (!(ex instanceof XRefParseException)) {
|
|
if (ex instanceof PasswordException) {
|
|
// after password exception prepare to receive a new password
|
|
// to repeat loading
|
|
pdfManager.passwordChanged().then(pdfManagerReady);
|
|
}
|
|
|
|
onFailure(ex);
|
|
return;
|
|
}
|
|
|
|
pdfManager.requestLoadedStream();
|
|
pdfManager.onLoadedStream().then(function() {
|
|
loadDocument(true).then(onSuccess, onFailure);
|
|
});
|
|
}, onFailure);
|
|
}, onFailure);
|
|
});
|
|
|
|
handler.on('GetPage', function wphSetupGetPage(data) {
|
|
return pdfManager.getPage(data.pageIndex).then(function(page) {
|
|
var rotatePromise = pdfManager.ensure(page, 'rotate');
|
|
var refPromise = pdfManager.ensure(page, 'ref');
|
|
var viewPromise = pdfManager.ensure(page, 'view');
|
|
|
|
return Promise.all([rotatePromise, refPromise, viewPromise]).then(
|
|
function(results) {
|
|
return {
|
|
rotate: results[0],
|
|
ref: results[1],
|
|
view: results[2]
|
|
};
|
|
});
|
|
});
|
|
});
|
|
|
|
handler.on('GetPageIndex', function wphSetupGetPageIndex(data) {
|
|
var ref = new Ref(data.ref.num, data.ref.gen);
|
|
var catalog = pdfManager.pdfDocument.catalog;
|
|
return catalog.getPageIndex(ref);
|
|
});
|
|
|
|
handler.on('GetDestinations',
|
|
function wphSetupGetDestinations(data) {
|
|
return pdfManager.ensureCatalog('destinations');
|
|
}
|
|
);
|
|
|
|
handler.on('GetAttachments',
|
|
function wphSetupGetAttachments(data) {
|
|
return pdfManager.ensureCatalog('attachments');
|
|
}
|
|
);
|
|
|
|
handler.on('GetJavaScript',
|
|
function wphSetupGetJavaScript(data) {
|
|
return pdfManager.ensureCatalog('javaScript');
|
|
}
|
|
);
|
|
|
|
handler.on('GetOutline',
|
|
function wphSetupGetOutline(data) {
|
|
return pdfManager.ensureCatalog('documentOutline');
|
|
}
|
|
);
|
|
|
|
handler.on('GetMetadata',
|
|
function wphSetupGetMetadata(data) {
|
|
return Promise.all([pdfManager.ensureDoc('documentInfo'),
|
|
pdfManager.ensureCatalog('metadata')]);
|
|
}
|
|
);
|
|
|
|
handler.on('GetData', function wphSetupGetData(data) {
|
|
pdfManager.requestLoadedStream();
|
|
return pdfManager.onLoadedStream().then(function(stream) {
|
|
return stream.bytes;
|
|
});
|
|
});
|
|
|
|
handler.on('GetStats',
|
|
function wphSetupGetStats(data) {
|
|
return pdfManager.pdfDocument.xref.stats;
|
|
}
|
|
);
|
|
|
|
handler.on('UpdatePassword', function wphSetupUpdatePassword(data) {
|
|
pdfManager.updatePassword(data);
|
|
});
|
|
|
|
handler.on('GetAnnotations', function wphSetupGetAnnotations(data) {
|
|
return pdfManager.getPage(data.pageIndex).then(function(page) {
|
|
return pdfManager.ensure(page, 'getAnnotationsData', []);
|
|
});
|
|
});
|
|
|
|
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
|
|
pdfManager.getPage(data.pageIndex).then(function(page) {
|
|
|
|
var pageNum = data.pageIndex + 1;
|
|
var start = Date.now();
|
|
// Pre compile the pdf page and fetch the fonts/images.
|
|
page.getOperatorList(handler, data.intent).then(function(operatorList) {
|
|
|
|
info('page=' + pageNum + ' - getOperatorList: time=' +
|
|
(Date.now() - start) + 'ms, len=' + operatorList.fnArray.length);
|
|
|
|
}, function(e) {
|
|
|
|
var minimumStackMessage =
|
|
'worker.js: while trying to getPage() and getOperatorList()';
|
|
|
|
var wrappedException;
|
|
|
|
// Turn the error into an obj that can be serialized
|
|
if (typeof e === 'string') {
|
|
wrappedException = {
|
|
message: e,
|
|
stack: minimumStackMessage
|
|
};
|
|
} else if (typeof e === 'object') {
|
|
wrappedException = {
|
|
message: e.message || e.toString(),
|
|
stack: e.stack || minimumStackMessage
|
|
};
|
|
} else {
|
|
wrappedException = {
|
|
message: 'Unknown exception type: ' + (typeof e),
|
|
stack: minimumStackMessage
|
|
};
|
|
}
|
|
|
|
handler.send('PageError', {
|
|
pageNum: pageNum,
|
|
error: wrappedException,
|
|
intent: data.intent
|
|
});
|
|
});
|
|
});
|
|
}, this);
|
|
|
|
handler.on('GetTextContent', function wphExtractText(data) {
|
|
return pdfManager.getPage(data.pageIndex).then(function(page) {
|
|
var pageNum = data.pageIndex + 1;
|
|
var start = Date.now();
|
|
return page.extractTextContent().then(function(textContent) {
|
|
info('text indexing: page=' + pageNum + ' - time=' +
|
|
(Date.now() - start) + 'ms');
|
|
return textContent;
|
|
});
|
|
});
|
|
});
|
|
|
|
handler.on('Cleanup', function wphCleanup(data) {
|
|
return pdfManager.cleanup();
|
|
});
|
|
|
|
handler.on('Terminate', function wphTerminate(data) {
|
|
pdfManager.terminate();
|
|
});
|
|
}
|
|
};
|
|
|
|
var consoleTimer = {};
|
|
|
|
var workerConsole = {
|
|
log: function log() {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
globalScope.postMessage({
|
|
action: 'console_log',
|
|
data: args
|
|
});
|
|
},
|
|
|
|
error: function error() {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
globalScope.postMessage({
|
|
action: 'console_error',
|
|
data: args
|
|
});
|
|
throw 'pdf.js execution error';
|
|
},
|
|
|
|
time: function time(name) {
|
|
consoleTimer[name] = Date.now();
|
|
},
|
|
|
|
timeEnd: function timeEnd(name) {
|
|
var time = consoleTimer[name];
|
|
if (!time) {
|
|
error('Unknown timer name ' + name);
|
|
}
|
|
this.log('Timer:', name, Date.now() - time);
|
|
}
|
|
};
|
|
|
|
|
|
// Worker thread?
|
|
if (typeof window === 'undefined') {
|
|
if (!('console' in globalScope)) {
|
|
globalScope.console = workerConsole;
|
|
}
|
|
|
|
// Listen for unsupported features so we can pass them on to the main thread.
|
|
PDFJS.UnsupportedManager.listen(function (msg) {
|
|
globalScope.postMessage({
|
|
action: '_unsupported_feature',
|
|
data: msg
|
|
});
|
|
});
|
|
|
|
var handler = new MessageHandler('worker_processor', this);
|
|
WorkerMessageHandler.setup(handler);
|
|
}
|