PDF integration throws `Cannot read properties of null (reading 'getDownloadInfo')` with PDF.js ≥ 4.5 when the client loads before a document is open

Aperta Adatta ai principianti
#7,592 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
83/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
javascript
Ambito
frontend

Direzione di ricerca

Inizia in src/annotator/integrations/pdf-metadata.ts, in corrispondenza di isPDFDownloaded(), e leggi come il listener documentloaded gestisce un risultato false. Riproduci il problema con PDF.js 4.5 o versioni successive avviando il client prima di aprire un documento. Poi conferma che il caso del documento null non provoca più un rifiuto e che il visualizzatore carica le annotazioni dopo l’apertura di un PDF.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Summary

PDFMetadata (src/annotator/integrations/pdf-metadata.ts) dereferences PDFViewerApplication.pdfDocument as soon as PDFViewerApplication.initializedPromise resolves. With PDF.js ≥ 4.5, where PDFViewerApplication.downloadComplete no longer exists (removed in mozilla/pdf.js#18463), that is the only code path. PDF.js resolves initializedPromise before it opens a document, so pdfDocument is still null whenever the client boots before the document is open. isPDFDownloaded() then throws, getUri() and getMetadata() reject, the sidebar never receives the document info and stays on its loading spinner. No annotation is loaded, and nothing is logged apart from the uncaught rejection.

The fallback added in #6541 assumed a document is already open (the bookmarklet case). Whether pdfDocument can be unset was raised in that review, and a follow-up issue was announced there, but I could not find one. The symptom is also mentioned in passing in #6784 ("Intermittently I have seen an error about a getDownloadInfo property …").

Steps to reproduce

Tested on 2026-11-08 with client 1.1769.0 (the build served by https://hypothes.is/embed.js) in Chrome 151, headless.

  1. Unzip a PDF.js generic viewer release, for example pdfjs-6.5.438-dist.zip.
  2. In web/viewer.html, load the client with the page, right after the viewer's own module, and remove (or widen) the viewer's Content-Security-Policy meta tag so the client can load:
    <script src="viewer.mjs" type="module"></script>
    <script src="https://hypothes.is/embed.js" async></script>
    
  3. Serve the directory (for example python3 -m http.server) and open web/viewer.html?file= (an empty file, so the viewer starts without a document) with the console open.
  4. Open a PDF through the viewer's file picker, or run PDFViewerApplication.open({ url: 'compressed.tracemonkey-pldi-09.pdf' }) in the console.

Observed with PDF.js 4.5.136 and 6.5.438: at step 3 the console shows

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'getDownloadInfo')
    annotator.bundle.js

At step 4 the document renders, but the sidebar keeps spinning, lists nothing, and no highlight appears (0 <hypothesis-highlight> elements for the TraceMonkey paper, which has about 200 annotations in the Public group).

Expected, and observed with PDF.js 4.4.168 and the same steps: no error; after step 4 the sidebar lists the 199 public annotations and the highlights anchor (786 <hypothesis-highlight> elements).

With ?file=<pdf> instead of an empty file, the outcome depends on a race: the client works if PDF.js finishes opening the document before the client's PDFIntegration is constructed, and fails as above otherwise. In our deployment (client and viewer served from the same host, a 17-page PDF) it failed every time; locally, with the 1 MB TraceMonkey PDF and the client coming from the CDN, it usually works. That is presumably the intermittent error seen in #6784. Hypothesis's own pdfjs-init.js (this repo's dev-server, via, the browser extension, pdf.js-hypothes.is) also injects the client right after initializedPromise and only then calls PDFViewerApplication.open(), so those viewers hit the same race once they move past PDF.js 4.4.

Versions
PDF.js PDFViewerApplication.downloadComplete Client 1.1769.0 booting before the document is open
3.11.174 … 4.4.168 present works (tested: 4.4.168)
4.5.136 … 6.5.438 removed in mozilla/pdf.js#18463, merged 2024-07-20 TypeError, no annotations (tested: 4.5.136, 6.5.438)

Presence checked in web/viewer.mjs of the release zips: 3.11.174, 4.0.379, 4.2.67, 4.3.136 and 4.4.168 define it; 4.5.136, 4.6.82, 4.7.76, 4.8.69, 4.9.155, 4.10.38, 5.0.375, 5.2.133, 5.4.394 and 6.5.438 do not.

Where

isPDFDownloaded() in src/annotator/integrations/pdf-metadata.ts, unchanged on main (a7b7400):

async function isPDFDownloaded(app: PDFViewerApplication): Promise<boolean> {
  if (app.downloadComplete !== undefined) {
    return app.downloadComplete;
  }

  await app.pdfDocument.getDownloadInfo(); // pdfDocument is null until PDFViewerApplication.load()
  return true;
}

On the PDF.js side, PDFViewerApplication.run() awaits initialize(), which resolves initializedPromise, and only then calls open(). pdfDocument is assigned in load(), once getDocument() has resolved; until then it is null.

Suggested fix

Treat a null pdfDocument as "not downloaded yet", so the existing documentloaded listener in the PDFMetadata constructor takes over:

if (app.downloadComplete !== undefined) {
  return app.downloadComplete;
}
if (!app.pdfDocument) {
  // Viewer initialized, but no document opened yet (PDF.js >= 4.5).
  return false;
}
await app.pdfDocument.getDownloadInfo();
return true;

PDF.js ≥ 4.5 still dispatches documentloaded after getDownloadInfo() resolves and the first page has rendered, so the fallback keeps the semantics of the old downloadComplete flag. Embedders can work around the problem today by loading the client only after the viewer's documentinit or documentloaded event, which is what we do.

Lingua principale
Mustache
Stelle
743
Fork
227
Merge medio
6m
PR unite (30g)
1

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di hypothesis/client

Tutte le issue di hypothesis/client

Issue simili

Altre issue su Web Dev

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.