Skip to content

Commit f96af43

Browse files
committed
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] mozilla/pdf.js#11655
1 parent cb8d380 commit f96af43

File tree

3 files changed

+94
-19
lines changed

3 files changed

+94
-19
lines changed

src/annotator/plugin/pdf-metadata.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,32 @@ export default class PDFMetadata {
4242
const finish = () => {
4343
window.removeEventListener('documentload', finish);
4444
window.removeEventListener('documentloaded', finish);
45+
app.eventBus?.off('documentloaded', finish);
46+
4547
resolve(app);
4648
};
4749

4850
if (app.downloadComplete) {
4951
resolve(app);
5052
} else {
51-
// Listen for either the `documentload` (older PDF.js) or
52-
// `documentloaded` (newer PDF.js) events which signal that the document
53+
// Listen for "documentloaded" event which signals that the document
5354
// has been downloaded and the first page has been rendered.
54-
//
55+
56+
// Newer versions of PDF.js (>= v2.5.207) report events only via
57+
// the PDFViewerApplication's own event bus.
58+
app.initializedPromise?.then(() => {
59+
app.eventBus?.on('documentloaded', finish);
60+
});
61+
62+
// Older versions of PDF.js (< v2.5.207) dispatch events to the DOM
63+
// instead or as well.
64+
65+
// PDF.js >= v2.0.943.
5566
// See https://github.com/mozilla/pdf.js/commit/7bc4bfcc8b7f52b14107f0a551becdf01643c5c2
56-
window.addEventListener('documentload', finish);
5767
window.addEventListener('documentloaded', finish);
68+
69+
// PDF.js < v2.0.943.
70+
window.addEventListener('documentload', finish);
5871
}
5972
});
6073
}

src/annotator/plugin/test/pdf-metadata-test.js

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import EventEmitter from 'tiny-emitter';
2+
13
import PDFMetadata from '../pdf-metadata';
24

35
/**
@@ -41,11 +43,22 @@ class FakePDFViewerApplication {
4143
* Initialize the "PDF viewer" as it would be when loading a document or
4244
* when a document fails to load.
4345
*/
44-
constructor(url = '') {
46+
constructor(url = '', { domEvents = false, eventBusEvents = true } = {}) {
4547
this.url = url;
4648
this.documentInfo = undefined;
4749
this.metadata = undefined;
4850
this.pdfDocument = null;
51+
this.dispatchDOMEvents = domEvents;
52+
53+
// Use `EventEmitter` as a fake version of PDF.js's `EventBus` class as the
54+
// API for subscribing to events is the same.
55+
if (eventBusEvents) {
56+
this.eventBus = new EventEmitter();
57+
}
58+
59+
this.initializedPromise = new Promise(resolve => {
60+
this._resolveInitializedPromise = resolve;
61+
});
4962
}
5063

5164
/**
@@ -58,10 +71,6 @@ class FakePDFViewerApplication {
5871
title,
5972
eventName = 'documentload',
6073
}) {
61-
const event = document.createEvent('Event');
62-
event.initEvent(eventName, false, false);
63-
window.dispatchEvent(event);
64-
6574
this.url = url;
6675
this.downloadComplete = true;
6776
this.documentInfo = {};
@@ -75,29 +84,69 @@ class FakePDFViewerApplication {
7584
}
7685

7786
this.pdfDocument = new FakePDFDocumentProxy({ fingerprint });
87+
88+
if (this.dispatchDOMEvents) {
89+
const event = document.createEvent('Event');
90+
event.initEvent(eventName, false, false);
91+
window.dispatchEvent(event);
92+
}
93+
this.eventBus?.emit(eventName);
7894
}
95+
96+
/**
97+
* Simulate PDF viewer initialization completing.
98+
*
99+
* At this point the event bus becomes available.
100+
*/
101+
completeInit() {
102+
this._resolveInitializedPromise();
103+
}
104+
}
105+
106+
function delay(ms) {
107+
return new Promise(resolve => setTimeout(resolve, ms));
79108
}
80109

81110
describe('annotator/plugin/pdf-metadata', function () {
82111
[
83-
// Event dispatched in older PDF.js versions (pre-7bc4bfcc).
84-
'documentload',
85-
// Event dispatched in newer PDF.js versions (post-7bc4bfcc).
86-
'documentloaded',
87-
].forEach(eventName => {
88-
it('waits for the PDF to load before returning metadata', function () {
89-
const fakeApp = new FakePDFViewerApplication();
112+
{
113+
// Oldest PDF.js versions (pre-2.x)
114+
eventName: 'documentload',
115+
domEvents: true,
116+
eventBusEvents: false,
117+
},
118+
{
119+
// Newer PDF.js versions (~ < 2.5.x)
120+
eventName: 'documentloaded',
121+
domEvents: true,
122+
eventBusEvents: false,
123+
},
124+
{
125+
// Current PDF.js versions (>= 2.5.x)
126+
eventName: 'documentloaded',
127+
domEvents: false,
128+
eventBusEvents: true,
129+
},
130+
].forEach(({ eventName, domEvents = false, eventBusEvents = false }, i) => {
131+
it(`waits for PDF to load (${i})`, async () => {
132+
const fakeApp = new FakePDFViewerApplication('', {
133+
domEvents,
134+
eventBusEvents,
135+
});
90136
const pdfMetadata = new PDFMetadata(fakeApp);
91137

138+
fakeApp.completeInit();
139+
140+
// Give `PDFMetadata` a chance to register the "documentloaded" event listener.
141+
await delay(0);
142+
92143
fakeApp.finishLoading({
93144
eventName,
94145
url: 'http://fake.com',
95146
fingerprint: 'fakeFingerprint',
96147
});
97148

98-
return pdfMetadata.getUri().then(function (uri) {
99-
assert.equal(uri, 'http://fake.com/');
100-
});
149+
assert.equal(await pdfMetadata.getUri(), 'http://fake.com/');
101150
});
102151
});
103152

src/types/pdfjs.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,30 @@
6767
* @prop {(page: number) => PDFPageView|null} getPageView
6868
*/
6969

70+
/**
71+
* Defined in `web/ui_utils.js` in the PDF.js source.
72+
*
73+
* @typedef EventBus
74+
* @prop {(event: string, listener: Function) => void} on
75+
* @prop {(event: string, listener: Function) => void} off
76+
*/
77+
7078
/**
7179
* The `PDFViewerApplication` global which is the entry-point for accessing PDF.js.
7280
*
7381
* Defined in `web/app.js` in the PDF.js source.
7482
*
7583
* @typedef PDFViewerApplication
84+
* @prop {EventBus} [eventBus] -
85+
* Global event bus. Since v1.6.210.
7686
* @prop {PDFDocument} pdfDocument
7787
* @prop {PDFViewer} pdfViewer
7888
* @prop {boolean} downloadComplete
7989
* @prop {PDFDocumentInfo} documentInfo
8090
* @prop {Metadata} metadata
91+
* @prop {Promise<void>} [initializedPromise] -
92+
* Promise that resolves when PDF.js is initialized. Since v2.4.456.
93+
* See https://github.com/mozilla/pdf.js/wiki/Third-party-viewer-usage#initialization-promise.
8194
*/
8295

8396
/**

0 commit comments

Comments
 (0)