diff --git a/src/core/worker.js b/src/core/worker.js index 45c8e33cc..6cfa09950 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -335,7 +335,7 @@ var WorkerMessageHandler = { // check if Uint8Array can be sent to worker if (!(data instanceof Uint8Array)) { - handler.send('test', 'main', false); + handler.send('test', false); return; } // making sure postMessage transfers are working @@ -390,8 +390,8 @@ var WorkerMessageHandler = { var workerHandlerName = docParams.docId + '_worker'; var handler = new MessageHandler(workerHandlerName, docId, port); - // Ensure that postMessage transfers are correctly enabled/disabled, - // to prevent "DataCloneError" in older versions of IE (see issue 6957). + // Ensure that postMessage transfers are always correctly enabled/disabled, + // to prevent "DataCloneError" in browsers without transfers support. handler.postMessageTransfers = docParams.postMessageTransfers; function ensureNotTerminated() { @@ -629,9 +629,8 @@ var WorkerMessageHandler = { 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, }); }); @@ -640,19 +639,17 @@ var WorkerMessageHandler = { 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 userUnitPromise = pdfManager.ensure(page, 'userUnit'); - var viewPromise = pdfManager.ensure(page, 'view'); - return Promise.all([ - rotatePromise, refPromise, userUnitPromise, viewPromise - ]).then(function(results) { + pdfManager.ensure(page, 'rotate'), + pdfManager.ensure(page, 'ref'), + pdfManager.ensure(page, 'userUnit'), + pdfManager.ensure(page, 'view'), + ]).then(function([rotate, ref, userUnit, view]) { return { - rotate: results[0], - ref: results[1], - userUnit: results[2], - view: results[3], + rotate, + ref, + userUnit, + view, }; }); }); diff --git a/src/display/api.js b/src/display/api.js index b121661f8..c9c8d4164 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -16,13 +16,13 @@ import { assert, createPromiseCapability, getVerbosityLevel, info, InvalidPDFException, - isArrayBuffer, isNum, isSameOrigin, MissingPDFException, NativeImageDecoding, + isArrayBuffer, isSameOrigin, MissingPDFException, NativeImageDecoding, PasswordException, setVerbosityLevel, shadow, stringToBytes, UnexpectedResponseException, UnknownErrorException, unreachable, Util, warn } from '../shared/util'; import { - DOMCanvasFactory, DOMCMapReaderFactory, DummyStatTimer, PageViewport, - RenderingCancelledException, StatTimer + DOMCanvasFactory, DOMCMapReaderFactory, DummyStatTimer, loadScript, + PageViewport, RenderingCancelledException, StatTimer } from './dom_utils'; import { FontFaceObject, FontLoader } from './font_loader'; import { apiCompatibilityParams } from './api_compatibility'; @@ -231,7 +231,6 @@ function getDocument(src) { let params = Object.create(null); var rangeTransport = null; let worker = null; - let CMapReaderFactory = DOMCMapReaderFactory; for (var key in source) { if (key === 'url' && typeof window !== 'undefined') { @@ -260,14 +259,12 @@ function getDocument(src) { 'data property.'); } continue; - } else if (key === 'CMapReaderFactory') { - CMapReaderFactory = source[key]; - continue; } params[key] = source[key]; } params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE; + params.CMapReaderFactory = params.CMapReaderFactory || DOMCMapReaderFactory; params.ignoreErrors = params.stopAtErrors !== true; params.pdfBug = params.pdfBug === true; @@ -355,7 +352,7 @@ function getDocument(src) { var messageHandler = new MessageHandler(docId, workerId, worker.port); messageHandler.postMessageTransfers = worker.postMessageTransfers; var transport = new WorkerTransport(messageHandler, task, networkStream, - params, CMapReaderFactory); + params); task._transport = transport; messageHandler.send('Ready', null); }); @@ -387,7 +384,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) { docId, apiVersion: (typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('BUNDLE_VERSION') : null), - source: { + source: { // Only send the required properties, and *not* the entire object. data: source.data, url: source.url, password: source.password, @@ -585,7 +582,7 @@ var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() { */ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { function PDFDocumentProxy(pdfInfo, transport, loadingTask) { - this.pdfInfo = pdfInfo; + this._pdfInfo = pdfInfo; this.transport = transport; this.loadingTask = loadingTask; } @@ -594,14 +591,14 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * @return {number} Total number of pages the PDF contains. */ get numPages() { - return this.pdfInfo.numPages; + return this._pdfInfo.numPages; }, /** * @return {string} A unique ID to identify a PDF. Not guaranteed to be * unique. */ get fingerprint() { - return this.pdfInfo.fingerprint; + return this._pdfInfo.fingerprint; }, /** * @param {number} pageNumber The page number to get. The first page is 1. @@ -831,7 +828,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { var PDFPageProxy = (function PDFPageProxyClosure() { function PDFPageProxy(pageIndex, pageInfo, transport, pdfBug = false) { this.pageIndex = pageIndex; - this.pageInfo = pageInfo; + this._pageInfo = pageInfo; this.transport = transport; this._stats = (pdfBug ? new StatTimer() : DummyStatTimer); this._pdfBug = pdfBug; @@ -853,27 +850,27 @@ var PDFPageProxy = (function PDFPageProxyClosure() { * @return {number} The number of degrees the page is rotated clockwise. */ get rotate() { - return this.pageInfo.rotate; + return this._pageInfo.rotate; }, /** * @return {Object} The reference that points to this page. It has 'num' and * 'gen' properties. */ get ref() { - return this.pageInfo.ref; + return this._pageInfo.ref; }, /** * @return {number} The default size of units in 1/72nds of an inch. */ get userUnit() { - return this.pageInfo.userUnit; + return this._pageInfo.userUnit; }, /** * @return {Array} An array of the visible portion of the PDF page in the * user space units - [x1, y1, x2, y2]. */ get view() { - return this.pageInfo.view; + return this._pageInfo.view; }, /** @@ -1359,7 +1356,7 @@ var PDFWorker = (function PDFWorkerClosure() { } } else { let loader = fakeWorkerFilesLoader || function(callback) { - Util.loadScript(getWorkerSrc(), function() { + loadScript(getWorkerSrc()).then(function() { callback(window.pdfjsWorker.WorkerMessageHandler); }); }; @@ -1382,7 +1379,8 @@ var PDFWorker = (function PDFWorkerClosure() { * @param {PDFWorkerParameters} params - The worker initialization parameters. */ function PDFWorker({ name = null, port = null, - postMessageTransfers = true, verbosity = null, } = {}) { + postMessageTransfers = true, + verbosity = getVerbosityLevel(), } = {}) { if (port && pdfWorkerPorts.has(port)) { throw new Error('Cannot use more than one PDFWorker per port'); } @@ -1390,7 +1388,7 @@ var PDFWorker = (function PDFWorkerClosure() { this.name = name; this.destroyed = false; this.postMessageTransfers = postMessageTransfers !== false; - this.verbosity = (isNum(verbosity) ? verbosity : getVerbosityLevel()); + this.verbosity = verbosity; this._readyCapability = createPromiseCapability(); this._port = null; @@ -1480,8 +1478,7 @@ var PDFWorker = (function PDFWorkerClosure() { terminateEarly(); return; // worker was destroyed } - var supportTypedArray = data && data.supportTypedArray; - if (supportTypedArray) { + if (data && data.supportTypedArray) { this._messageHandler = messageHandler; this._port = worker; this._webWorker = worker; @@ -1553,12 +1550,7 @@ var PDFWorker = (function PDFWorkerClosure() { this._readyCapability.reject(new Error('Worker was destroyed')); return; } - - // We cannot turn on proper fake port simulation (this includes - // structured cloning) when typed arrays are not supported. Relying - // on a chance that messages will be sent in proper order. - var isTypedArraysPresent = Uint8Array !== Float32Array; - var port = new LoopbackPort(isTypedArraysPresent); + let port = new LoopbackPort(); this._port = port; // All fake workers use the same port, making id unique. @@ -1598,6 +1590,9 @@ var PDFWorker = (function PDFWorkerClosure() { * @param {PDFWorkerParameters} params - The worker initialization parameters. */ PDFWorker.fromPort = function(params) { + if (!params || !params.port) { + throw new Error('PDFWorker.fromPort - invalid method signature.'); + } if (pdfWorkerPorts.has(params.port)) { return pdfWorkerPorts.get(params.port); } @@ -1616,14 +1611,13 @@ var PDFWorker = (function PDFWorkerClosure() { * @ignore */ var WorkerTransport = (function WorkerTransportClosure() { - function WorkerTransport(messageHandler, loadingTask, networkStream, - params, CMapReaderFactory) { + function WorkerTransport(messageHandler, loadingTask, networkStream, params) { this.messageHandler = messageHandler; this.loadingTask = loadingTask; this.commonObjs = new PDFObjects(); this.fontLoader = new FontLoader(loadingTask.docId); this._params = params; - this.CMapReaderFactory = new CMapReaderFactory({ + this.CMapReaderFactory = new params.CMapReaderFactory({ baseUrl: params.cMapUrl, isCompressed: params.cMapPacked, }); @@ -1837,9 +1831,6 @@ var WorkerTransport = (function WorkerTransportClosure() { this.downloadInfoCapability.resolve(data); }, this); - messageHandler.on('PDFManagerReady', function transportPage(data) { - }, this); - messageHandler.on('StartRenderPage', function transportRender(data) { if (this.destroyed) { return; // Ignore any pending requests if the worker was terminated. @@ -2030,7 +2021,7 @@ var WorkerTransport = (function WorkerTransportClosure() { var height = img.height; var size = width * height; var rgbaLength = size * 4; - var buf = new Uint8Array(size * components); + var buf = new Uint8ClampedArray(size * components); var tmpCanvas = document.createElement('canvas'); tmpCanvas.width = width; tmpCanvas.height = height; diff --git a/src/display/dom_utils.js b/src/display/dom_utils.js index 94e3b10f2..06f7a7b8e 100644 --- a/src/display/dom_utils.js +++ b/src/display/dom_utils.js @@ -428,6 +428,19 @@ class DummyStatTimer { } } +function loadScript(src) { + return new Promise((resolve, reject) => { + let script = document.createElement('script'); + script.src = src; + + script.onload = resolve; + script.onerror = function() { + reject(new Error(`Cannot load script at: ${script.src}`)); + }; + (document.head || document.documentElement).appendChild(script); + }); +} + export { PageViewport, RenderingCancelledException, @@ -440,4 +453,5 @@ export { DOMSVGFactory, StatTimer, DummyStatTimer, + loadScript, }; diff --git a/src/display/node_stream.js b/src/display/node_stream.js index 3332d46d9..84ab37a8b 100644 --- a/src/display/node_stream.js +++ b/src/display/node_stream.js @@ -90,8 +90,7 @@ class BaseFullReader { constructor(stream) { this._url = stream.url; this._done = false; - this._errored = false; - this._reason = null; + this._storedError = null; this.onProgress = null; let source = stream.source; this._contentLength = source.length; // optional @@ -137,8 +136,8 @@ class BaseFullReader { if (this._done) { return Promise.resolve({ value: undefined, done: true, }); } - if (this._errored) { - return Promise.reject(this._reason); + if (this._storedError) { + return Promise.reject(this._storedError); } let chunk = this._readableStream.read(); @@ -170,8 +169,7 @@ class BaseFullReader { } _error(reason) { - this._errored = true; - this._reason = reason; + this._storedError = reason; this._readCapability.resolve(); } @@ -199,8 +197,8 @@ class BaseFullReader { } // Destroy ReadableStream if already in errored state. - if (this._errored) { - this._readableStream.destroy(this._reason); + if (this._storedError) { + this._readableStream.destroy(this._storedError); } } } @@ -209,8 +207,7 @@ class BaseRangeReader { constructor(stream) { this._url = stream.url; this._done = false; - this._errored = false; - this._reason = null; + this._storedError = null; this.onProgress = null; this._loaded = 0; this._readableStream = null; @@ -228,8 +225,8 @@ class BaseRangeReader { if (this._done) { return Promise.resolve({ value: undefined, done: true, }); } - if (this._errored) { - return Promise.reject(this._reason); + if (this._storedError) { + return Promise.reject(this._storedError); } let chunk = this._readableStream.read(); @@ -258,8 +255,7 @@ class BaseRangeReader { } _error(reason) { - this._errored = true; - this._reason = reason; + this._storedError = reason; this._readCapability.resolve(); } @@ -281,8 +277,8 @@ class BaseRangeReader { }); // Destroy readableStream if already in errored state. - if (this._errored) { - this._readableStream.destroy(this._reason); + if (this._storedError) { + this._readableStream.destroy(this._storedError); } } } @@ -339,8 +335,7 @@ class PDFNodeStreamFullReader extends BaseFullReader { } this._request.on('error', (reason) => { - this._errored = true; - this._reason = reason; + this._storedError = reason; this._headersCapability.reject(reason); }); // Note: `request.end(data)` is used to write `data` to request body @@ -378,8 +373,7 @@ class PDFNodeStreamRangeReader extends BaseRangeReader { } this._request.on('error', (reason) => { - this._errored = true; - this._reason = reason; + this._storedError = reason; }); this._request.end(); } @@ -398,8 +392,7 @@ class PDFNodeStreamFsFullReader extends BaseFullReader { fs.lstat(path, (error, stat) => { if (error) { - this._errored = true; - this._reason = error; + this._storedError = error; this._headersCapability.reject(error); return; } diff --git a/src/pdf.js b/src/pdf.js index 1de68d330..cc4c2c5e6 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -108,6 +108,7 @@ exports.RenderingCancelledException = exports.getFilenameFromUrl = pdfjsDisplayDOMUtils.getFilenameFromUrl; exports.LinkTarget = pdfjsDisplayDOMUtils.LinkTarget; exports.addLinkAttributes = pdfjsDisplayDOMUtils.addLinkAttributes; +exports.loadScript = pdfjsDisplayDOMUtils.loadScript; exports.GlobalWorkerOptions = pdfjsDisplayWorkerOptions.GlobalWorkerOptions; exports.apiCompatibilityParams = pdfjsDisplayAPICompatibility.apiCompatibilityParams; diff --git a/src/shared/util.js b/src/shared/util.js index 6ffbedf60..a52df0c42 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -908,21 +908,6 @@ var Util = (function UtilClosure() { } }; - Util.loadScript = function Util_loadScript(src, callback) { - var script = document.createElement('script'); - var loaded = false; - script.setAttribute('src', src); - if (callback) { - script.onload = function() { - if (!loaded) { - callback(); - } - loaded = true; - }; - } - document.getElementsByTagName('head')[0].appendChild(script); - }; - return Util; })(); diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 3b0cdf23f..1d3264649 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -164,7 +164,9 @@ describe('api', function() { }); it('creates pdf doc from non-existent URL', function(done) { if (isNodeJS()) { - pending('XMLHttpRequest is not supported in Node.js.'); + pending('Fix `src/display/node_stream.js` to actually throw ' + + 'a `MissingPDFException` in all cases where a PDF file ' + + 'cannot be found, such that this test-case can be enabled.'); } var loadingTask = getDocument( buildGetDocumentParams('non-existent.pdf')); diff --git a/web/app.js b/web/app.js index de4f8c8ec..b9f3f1d62 100644 --- a/web/app.js +++ b/web/app.js @@ -22,8 +22,8 @@ import { } from './ui_utils'; import { build, createBlob, getDocument, getFilenameFromUrl, GlobalWorkerOptions, - InvalidPDFException, LinkTarget, MissingPDFException, OPS, PDFWorker, shadow, - UnexpectedResponseException, UNSUPPORTED_FEATURES, version + InvalidPDFException, LinkTarget, loadScript, MissingPDFException, OPS, + PDFWorker, shadow, UnexpectedResponseException, UNSUPPORTED_FEATURES, version } from 'pdfjs-lib'; import { CursorTool, PDFCursorTools } from './pdf_cursor_tools'; import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue'; @@ -1551,11 +1551,11 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { } function loadFakeWorker() { - return new Promise(function(resolve, reject) { - if (!GlobalWorkerOptions.workerSrc) { - GlobalWorkerOptions.workerSrc = AppOptions.get('workerSrc'); - } - if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')) { + if (!GlobalWorkerOptions.workerSrc) { + GlobalWorkerOptions.workerSrc = AppOptions.get('workerSrc'); + } + if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')) { + return new Promise(function(resolve, reject) { if (typeof SystemJS === 'object') { SystemJS.import('pdfjs/core/worker').then((worker) => { window.pdfjsWorker = worker; @@ -1568,37 +1568,18 @@ function loadFakeWorker() { reject(new Error( 'SystemJS or CommonJS must be used to load fake worker.')); } - } else { - let script = document.createElement('script'); - script.src = PDFWorker.getWorkerSrc(); - script.onload = function() { - resolve(); - }; - script.onerror = function() { - reject(new Error(`Cannot load fake worker at: ${script.src}`)); - }; - (document.head || document.documentElement).appendChild(script); - } - }); + }); + } + return loadScript(PDFWorker.getWorkerSrc()); } function loadAndEnablePDFBug(enabledTabs) { - return new Promise(function (resolve, reject) { - let appConfig = PDFViewerApplication.appConfig; - let script = document.createElement('script'); - script.src = appConfig.debuggerScriptPath; - script.onload = function () { - PDFBug.enable(enabledTabs); - PDFBug.init({ - OPS, - }, appConfig.mainContainer); - resolve(); - }; - script.onerror = function () { - reject(new Error('Cannot load debugger at ' + script.src)); - }; - (document.getElementsByTagName('head')[0] || document.body). - appendChild(script); + let appConfig = PDFViewerApplication.appConfig; + return loadScript(appConfig.debuggerScriptPath).then(function() { + PDFBug.enable(enabledTabs); + PDFBug.init({ + OPS, + }, appConfig.mainContainer); }); }