Commit f96af43d authored by Robert Knight's avatar Robert Knight

Support PDF.js >= 2.5.207

Since [1] PDF.js does not dispatch events to the DOM any more. Therefore
the client needs to listen for the `documentloaded` event from
`PDFApplicationViewer.eventBus`. The `eventBus` property is only
available once the viewer is initialized, so the client needs to wait
for `initializedPromise` to resolve before checking it.

[1] https://github.com/mozilla/pdf.js/pull/11655
parent cb8d3802
......@@ -42,19 +42,32 @@ export default class PDFMetadata {
const finish = () => {
window.removeEventListener('documentload', finish);
window.removeEventListener('documentloaded', finish);
app.eventBus?.off('documentloaded', finish);
resolve(app);
};
if (app.downloadComplete) {
resolve(app);
} else {
// Listen for either the `documentload` (older PDF.js) or
// `documentloaded` (newer PDF.js) events which signal that the document
// Listen for "documentloaded" event which signals that the document
// has been downloaded and the first page has been rendered.
//
// Newer versions of PDF.js (>= v2.5.207) report events only via
// the PDFViewerApplication's own event bus.
app.initializedPromise?.then(() => {
app.eventBus?.on('documentloaded', finish);
});
// Older versions of PDF.js (< v2.5.207) dispatch events to the DOM
// instead or as well.
// PDF.js >= v2.0.943.
// See https://github.com/mozilla/pdf.js/commit/7bc4bfcc8b7f52b14107f0a551becdf01643c5c2
window.addEventListener('documentload', finish);
window.addEventListener('documentloaded', finish);
// PDF.js < v2.0.943.
window.addEventListener('documentload', finish);
}
});
}
......
import EventEmitter from 'tiny-emitter';
import PDFMetadata from '../pdf-metadata';
/**
......@@ -41,11 +43,22 @@ class FakePDFViewerApplication {
* Initialize the "PDF viewer" as it would be when loading a document or
* when a document fails to load.
*/
constructor(url = '') {
constructor(url = '', { domEvents = false, eventBusEvents = true } = {}) {
this.url = url;
this.documentInfo = undefined;
this.metadata = undefined;
this.pdfDocument = null;
this.dispatchDOMEvents = domEvents;
// Use `EventEmitter` as a fake version of PDF.js's `EventBus` class as the
// API for subscribing to events is the same.
if (eventBusEvents) {
this.eventBus = new EventEmitter();
}
this.initializedPromise = new Promise(resolve => {
this._resolveInitializedPromise = resolve;
});
}
/**
......@@ -58,10 +71,6 @@ class FakePDFViewerApplication {
title,
eventName = 'documentload',
}) {
const event = document.createEvent('Event');
event.initEvent(eventName, false, false);
window.dispatchEvent(event);
this.url = url;
this.downloadComplete = true;
this.documentInfo = {};
......@@ -75,29 +84,69 @@ class FakePDFViewerApplication {
}
this.pdfDocument = new FakePDFDocumentProxy({ fingerprint });
if (this.dispatchDOMEvents) {
const event = document.createEvent('Event');
event.initEvent(eventName, false, false);
window.dispatchEvent(event);
}
this.eventBus?.emit(eventName);
}
/**
* Simulate PDF viewer initialization completing.
*
* At this point the event bus becomes available.
*/
completeInit() {
this._resolveInitializedPromise();
}
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
describe('annotator/plugin/pdf-metadata', function () {
[
// Event dispatched in older PDF.js versions (pre-7bc4bfcc).
'documentload',
// Event dispatched in newer PDF.js versions (post-7bc4bfcc).
'documentloaded',
].forEach(eventName => {
it('waits for the PDF to load before returning metadata', function () {
const fakeApp = new FakePDFViewerApplication();
{
// Oldest PDF.js versions (pre-2.x)
eventName: 'documentload',
domEvents: true,
eventBusEvents: false,
},
{
// Newer PDF.js versions (~ < 2.5.x)
eventName: 'documentloaded',
domEvents: true,
eventBusEvents: false,
},
{
// Current PDF.js versions (>= 2.5.x)
eventName: 'documentloaded',
domEvents: false,
eventBusEvents: true,
},
].forEach(({ eventName, domEvents = false, eventBusEvents = false }, i) => {
it(`waits for PDF to load (${i})`, async () => {
const fakeApp = new FakePDFViewerApplication('', {
domEvents,
eventBusEvents,
});
const pdfMetadata = new PDFMetadata(fakeApp);
fakeApp.completeInit();
// Give `PDFMetadata` a chance to register the "documentloaded" event listener.
await delay(0);
fakeApp.finishLoading({
eventName,
url: 'http://fake.com',
fingerprint: 'fakeFingerprint',
});
return pdfMetadata.getUri().then(function (uri) {
assert.equal(uri, 'http://fake.com/');
});
assert.equal(await pdfMetadata.getUri(), 'http://fake.com/');
});
});
......
......@@ -67,17 +67,30 @@
* @prop {(page: number) => PDFPageView|null} getPageView
*/
/**
* Defined in `web/ui_utils.js` in the PDF.js source.
*
* @typedef EventBus
* @prop {(event: string, listener: Function) => void} on
* @prop {(event: string, listener: Function) => void} off
*/
/**
* The `PDFViewerApplication` global which is the entry-point for accessing PDF.js.
*
* Defined in `web/app.js` in the PDF.js source.
*
* @typedef PDFViewerApplication
* @prop {EventBus} [eventBus] -
* Global event bus. Since v1.6.210.
* @prop {PDFDocument} pdfDocument
* @prop {PDFViewer} pdfViewer
* @prop {boolean} downloadComplete
* @prop {PDFDocumentInfo} documentInfo
* @prop {Metadata} metadata
* @prop {Promise<void>} [initializedPromise] -
* Promise that resolves when PDF.js is initialized. Since v2.4.456.
* See https://github.com/mozilla/pdf.js/wiki/Third-party-viewer-usage#initialization-promise.
*/
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment