From dcc7f33ee7ee0790ee34e2106a8d04254bc66c8c Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 1 Jun 2018 12:52:06 +0200 Subject: [PATCH 1/4] Prevent "ReferenceError: window is not defined" errors, from `web/ui_utils.js`, when running the unit-tests in Node.js/Travis --- web/ui_utils.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/web/ui_utils.js b/web/ui_utils.js index 0c506f983..1728d8b8c 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -681,6 +681,13 @@ function waitOnEventOrTimeout({ target, name, delay = 0, }) { * Promise that is resolved when DOM window becomes visible. */ let animationStarted = new Promise(function (resolve) { + if ((typeof PDFJSDev !== 'undefined' && PDFJSDev.test('LIB')) && + typeof window === 'undefined') { + // Prevent "ReferenceError: window is not defined" errors when running the + // unit-tests in Node.js/Travis. + setTimeout(resolve, 20); + return; + } window.requestAnimationFrame(resolve); }); From 0ecc22cb04b54e57bbe0e498791f9a604c8c2e57 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 1 Jun 2018 12:52:29 +0200 Subject: [PATCH 2/4] Attempt to provide better default values for the `disableFontFace`/`nativeImageDecoderSupport` API options in Node.js This should provide a better out-of-the-box experience when using PDF.js in a Node.js environment, since it's missing native support for both `@font-face` and `Image`. Please note that this change *only* affects the default values, hence it's still possible for an API consumer to override those values when calling `getDocument`. Also, prevents "ReferenceError: document is not defined" errors, when running the unit-tests in Node.js/Travis. --- examples/node/pdf2png/pdf2png.js | 10 ++-------- src/display/api.js | 8 ++++++-- src/display/api_compatibility.js | 18 +++++++++++++----- web/viewer_compatibility.js | 5 +---- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/examples/node/pdf2png/pdf2png.js b/examples/node/pdf2png/pdf2png.js index d42ba4b40..ae739a35b 100644 --- a/examples/node/pdf2png/pdf2png.js +++ b/examples/node/pdf2png/pdf2png.js @@ -56,14 +56,8 @@ var pdfURL = '../../../web/compressed.tracemonkey-pldi-09.pdf'; // Read the PDF file into a typed array so PDF.js can load it. var rawData = new Uint8Array(fs.readFileSync(pdfURL)); -// Load the PDF file. The `disableFontFace` and `nativeImageDecoderSupport` -// options must be passed because Node.js has no native `@font-face` and -// `Image` support. -pdfjsLib.getDocument({ - data: rawData, - disableFontFace: true, - nativeImageDecoderSupport: 'none', -}).then(function (pdfDocument) { +// Load the PDF file. +pdfjsLib.getDocument(rawData).then(function (pdfDocument) { console.log('# PDF document loaded.'); // Get the first page. diff --git a/src/display/api.js b/src/display/api.js index 4f7259c50..ca673438b 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -274,7 +274,9 @@ function getDocument(src) { const NativeImageDecoderValues = Object.values(NativeImageDecoding); if (params.nativeImageDecoderSupport === undefined || !NativeImageDecoderValues.includes(params.nativeImageDecoderSupport)) { - params.nativeImageDecoderSupport = NativeImageDecoding.DECODE; + params.nativeImageDecoderSupport = + (apiCompatibilityParams.nativeImageDecoderSupport || + NativeImageDecoding.DECODE); } if (!Number.isInteger(params.maxImageSize)) { params.maxImageSize = -1; @@ -283,7 +285,7 @@ function getDocument(src) { params.isEvalSupported = true; } if (typeof params.disableFontFace !== 'boolean') { - params.disableFontFace = false; + params.disableFontFace = apiCompatibilityParams.disableFontFace || false; } if (typeof params.disableRange !== 'boolean') { @@ -2168,6 +2170,8 @@ var WorkerTransport = (function WorkerTransportClosure() { disableStream: params.disableStream, disableAutoFetch: params.disableAutoFetch, disableCreateObjectURL: params.disableCreateObjectURL, + disableFontFace: params.disableFontFace, + nativeImageDecoderSupport: params.nativeImageDecoderSupport, }); }, }; diff --git a/src/display/api_compatibility.js b/src/display/api_compatibility.js index 09e283e75..beb49fe65 100644 --- a/src/display/api_compatibility.js +++ b/src/display/api_compatibility.js @@ -15,6 +15,8 @@ let compatibilityParams = Object.create(null); if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { + const isNodeJS = require('../shared/is_node'); + const userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || ''; const isIE = /Trident/.test(userAgent); @@ -42,9 +44,15 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { compatibilityParams.disableStream = true; } })(); -} -const apiCompatibilityParams = Object.freeze(compatibilityParams); -export { - apiCompatibilityParams, -}; + // Support: Node.js + (function checkFontFaceAndImage() { + // Node.js is missing native support for `@font-face` and `Image`. + if (isNodeJS()) { + compatibilityParams.disableFontFace = true; + compatibilityParams.nativeImageDecoderSupport = 'none'; + } + })(); +} + +exports.apiCompatibilityParams = Object.freeze(compatibilityParams); diff --git a/web/viewer_compatibility.js b/web/viewer_compatibility.js index e6aadea80..2debdc412 100644 --- a/web/viewer_compatibility.js +++ b/web/viewer_compatibility.js @@ -37,8 +37,5 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { } })(); } -const viewerCompatibilityParams = Object.freeze(compatibilityParams); -export { - viewerCompatibilityParams, -}; +exports.viewerCompatibilityParams = Object.freeze(compatibilityParams); From ef081a053182bb323d3c769e8f6ed11b35c4357c Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 1 Jun 2018 12:52:47 +0200 Subject: [PATCH 3/4] Ensure that the `WorkerTransport._passwordCapability` is always rejected, even when errors are thrown in `PDFDocumentLoadingTask.onPassword` callback Please note that while the current code works, both in the viewer and the unit-tests, it can leave the `WorkerTransport._passwordCapability` Promise in a pending state. In the `PasswordRequest` handler, in src/display/api.js, we're returning the Promise from a `capability` object (rather than just a "plain" Promise). While an error thrown anywhere within this handler was fortunately enough to propagate it to the Worker side, it won't cause the Promise (in `WorkerTransport._passwordCapability`) to actually be rejected. Finally note that while we're now catching errors in the `PasswordRequest` handler, those errors are still propagated to the Worker side via the (now) rejected Promise and the existing `return this._passwordCapability.promise;` line. This prevents warnings about uncaught Promises, with messages such as "Error: Worker was destroyed during onPassword callback", when running the unit-tests both in browsers *and* in Node.js/Travis. --- src/core/worker.js | 4 ++-- src/display/api.js | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/worker.js b/src/core/worker.js index 742718aad..72c381a60 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -574,9 +574,9 @@ var WorkerMessageHandler = { finishWorkerTask(task); pdfManager.updatePassword(data.password); pdfManagerReady(); - }).catch(function (ex) { + }).catch(function (boundException) { finishWorkerTask(task); - handler.send('PasswordException', ex); + handler.send('PasswordException', boundException); }.bind(null, e)); } else if (e instanceof InvalidPDFException) { handler.send('InvalidPDF', e); diff --git a/src/display/api.js b/src/display/api.js index ca673438b..0003b8090 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -1788,7 +1788,11 @@ var WorkerTransport = (function WorkerTransportClosure() { password, }); }; - loadingTask.onPassword(updatePassword, exception.code); + try { + loadingTask.onPassword(updatePassword, exception.code); + } catch (ex) { + this._passwordCapability.reject(ex); + } } else { this._passwordCapability.reject( new PasswordException(exception.message, exception.code)); From 11b4613e203d48c896fc66a24f228ca4e40549f5 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 1 Jun 2018 12:56:46 +0200 Subject: [PATCH 4/4] Reduce the amount of console "spam", by ignoring `info`/`warn` calls, when running the unit-tests in Node.js/Travis Compared to running the unit-tests in "regular" browsers, where any console output won't get mixed up with test output, in Node.js/Travis the test output looks quite noisy. By ignoring `info`/`warn` calls, when running unit-tests in Node.js/Travis, the test output is a lot smaller not to mention that any *actual* failures are more easily spotted. --- test/unit/clitests_helper.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unit/clitests_helper.js b/test/unit/clitests_helper.js index 15f6ffa2d..11d9475e9 100644 --- a/test/unit/clitests_helper.js +++ b/test/unit/clitests_helper.js @@ -13,6 +13,7 @@ * limitations under the License. */ +import { setVerbosityLevel, VerbosityLevel } from '../../src/shared/util'; import isNodeJS from '../../src/shared/is_node'; import { PDFNodeStream } from '../../src/display/node_stream'; import { setPDFNetworkStreamFactory } from '../../src/display/api'; @@ -23,6 +24,10 @@ if (!isNodeJS()) { 'Node.js environments.'); } +// Reduce the amount of console "spam", by ignoring `info`/`warn` calls, +// when running the unit-tests in Node.js/Travis. +setVerbosityLevel(VerbosityLevel.ERRORS); + // Set the network stream factory for the unit-tests. setPDFNetworkStreamFactory(function(params) { return new PDFNodeStream(params);