// Native Chromium PDF viewer using a single persistent iframe. // The iframe is created once in index.html and reused for all documents. class NativePdfViewer { constructor(frame, container) { this.frame = frame; this.container = container; this.generation = 0; this.currentKey = null; this.lastUrl = null; this.loadTimer = null; this._onLoad = null; this._createDOM(); } _createDOM() { this.loadingDiv = document.createElement("div"); this.loadingDiv.className = "native-pdf-loading"; this.loadingDiv.textContent = "Loading PDF\u2026"; this.loadingDiv.hidden = true; this.container.appendChild(this.loadingDiv); this.errorDiv = document.createElement("div"); this.errorDiv.className = "native-pdf-error"; this.errorDiv.hidden = true; this.errorMsg = document.createElement("div"); this.errorMsg.className = "native-pdf-error-msg"; this.errorDiv.appendChild(this.errorMsg); this.retryBtn = document.createElement("button"); this.retryBtn.className = "native-pdf-retry"; this.retryBtn.textContent = "Retry"; this.retryBtn.addEventListener("click", () => { if (this.lastUrl && this.currentKey) { this._navigate(this.lastUrl, this.currentKey, true); } }); this.errorDiv.appendChild(this.retryBtn); this.container.appendChild(this.errorDiv); // Log iframe sizing once console.log( "[BizMatch QC] iframe size:", this.frame.clientWidth + "x" + this.frame.clientHeight, "container:", this.container.clientWidth + "x" + this.container.clientHeight, ); } _showState(state) { this.loadingDiv.hidden = state !== "loading"; this.errorDiv.hidden = state !== "error"; } load(url, _byteSize, key) { this._navigate(url, key, false); } _navigate(url, key, force) { // Deduplicate only AFTER the key is accepted if (!force && key && key === this.currentKey) { return; } const generation = ++this.generation; this.currentKey = key; this.lastUrl = url; this._showState("loading"); console.log(`[BizMatch QC] Native PDF src assigned: ${key}`); console.log( `[BizMatch QC] Native PDF iframe size: ${this.frame.clientWidth}x${this.frame.clientHeight}`, ); // Register per-navigation load handler if (this._onLoad) { this.frame.removeEventListener("load", this._onLoad); } this._onLoad = () => { if (generation !== this.generation) return; if (this.loadTimer) { clearTimeout(this.loadTimer); this.loadTimer = null; } this._showState("loaded"); console.log(`[BizMatch QC] Native PDF load event: ${key}`); }; this.frame.addEventListener("load", this._onLoad, { once: true }); // Safety timeout if (this.loadTimer) clearTimeout(this.loadTimer); this.loadTimer = setTimeout(() => { if (generation === this.generation) { console.warn(`[BizMatch QC] Native PDF load timeout: ${key}`); this._showState("error"); this.errorMsg.textContent = "PDF load timed out. The file may be very large."; } }, 15000); this.frame.src = url; } destroy() { if (this.loadTimer) clearTimeout(this.loadTimer); if (this._onLoad) this.frame.removeEventListener("load", this._onLoad); this.generation = 0; this.frame.src = ""; this._showState("loading"); } } globalThis.NativePdfViewer = NativePdfViewer;