From 65c5a5f99a89c775f641b8d52954ae34664a47db Mon Sep 17 00:00:00 2001 From: MurakamiShinyu Date: Fri, 23 Feb 2024 01:26:15 +0900 Subject: [PATCH] feat: Add errorCallback config option to VivliostylePrint/printHTML() So far VivliostylePrint has no way to handle errors. This commit adds a new config option `errorCallback` to VivliostylePrint/`printHTML()` to handle errors. Usage example: ```ts printHTML(htmlDoc, { errorCallback: (msg) => { alert(msg); }, }); ``` This commit also fixes the following issues on error handling: - `Payload.content` type should be `ErrorInfo` instead of `string`. - `loadDocument()` and `loadPublication()` in CoreViewer needed to fix the "No URL specified" error handling. - `Logging.logger.addListener(level, listener)` used to push a listener to an array per level so that multiple listeners can be registered for the same level. However, that was problematic because the `Logging.logger` is a singleton instance but the CoreViewer instance is created each time `printHTML()` is called, and new listeners are added each time without removing the old ones, causing the listeners to be duplicated. This commit changes the `addListener` method to replace the listener for the level if it already exists. --- packages/core/src/vivliostyle/core-viewer.ts | 11 +++++++---- packages/core/src/vivliostyle/logging.ts | 16 +++++----------- packages/core/src/vivliostyle/print.ts | 13 +++++++++++++ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/core/src/vivliostyle/core-viewer.ts b/packages/core/src/vivliostyle/core-viewer.ts index 88dbd7d06..204d34f36 100644 --- a/packages/core/src/vivliostyle/core-viewer.ts +++ b/packages/core/src/vivliostyle/core-viewer.ts @@ -23,12 +23,13 @@ import * as Constants from "./constants"; import * as Epub from "./epub"; import * as Profile from "./profile"; import * as Toc from "./toc"; +import { ErrorInfo } from "./logging"; export interface Payload { type: string; internal: boolean; href: string; - content: string; + content: ErrorInfo; cfi: string; first: boolean; last: boolean; @@ -251,11 +252,12 @@ export class CoreViewer { opt_documentOptions?: DocumentOptions, opt_viewerOptions?: CoreViewerOptions, ) { - if (!singleDocumentOptions) { + if (!singleDocumentOptions || !singleDocumentOptions[0]) { this.eventTarget.dispatchEvent({ type: "error", - content: "No URL specified", + content: { error: new Error("No URL specified") }, }); + return; } this.loadDocumentOrPublication( singleDocumentOptions, @@ -276,8 +278,9 @@ export class CoreViewer { if (!pubUrl) { this.eventTarget.dispatchEvent({ type: "error", - content: "No URL specified", + content: { error: new Error("No URL specified") }, }); + return; } this.loadDocumentOrPublication( null, diff --git a/packages/core/src/vivliostyle/logging.ts b/packages/core/src/vivliostyle/logging.ts index 76c116f73..f77a20fda 100644 --- a/packages/core/src/vivliostyle/logging.ts +++ b/packages/core/src/vivliostyle/logging.ts @@ -38,7 +38,7 @@ export type ErrorInfo = { * Class logging error, warning, information or debug messages. */ export class Logger { - private listeners: { [key in LogLevel]?: ((p1: ErrorInfo) => void)[] } = {}; + private listeners: { [key in LogLevel]?: (p1: ErrorInfo) => void } = {}; constructor(private opt_console?: Console) {} @@ -91,11 +91,9 @@ export class Logger { } private triggerListeners(level: LogLevel, args: ErrorInfo) { - const listeners = this.listeners[level]; - if (listeners) { - listeners.forEach((listener) => { - listener(args); - }); + const listener = this.listeners[level]; + if (listener) { + listener(args); } } @@ -104,11 +102,7 @@ export class Logger { * occurs. */ addListener(level: LogLevel, listener: (p1: ErrorInfo) => void) { - let listeners = this.listeners[level]; - if (!listeners) { - listeners = this.listeners[level] = []; - } - listeners.push(listener); + this.listeners[level] = listener; } debug(...var_args: any[]) { diff --git a/packages/core/src/vivliostyle/print.ts b/packages/core/src/vivliostyle/print.ts index 255c67bea..1ca9c4291 100644 --- a/packages/core/src/vivliostyle/print.ts +++ b/packages/core/src/vivliostyle/print.ts @@ -10,6 +10,7 @@ interface IFrameWindowForPrint { export interface PrintConfig { title: string; printCallback: (iframeWin: Window) => void; + errorCallback: (msg: string) => void | null; hideIframe: boolean; removeIframe: boolean; } @@ -18,6 +19,7 @@ class VivliostylePrint { htmlDoc: string; title: string; printCallback: (iframeWin: Window) => void; + errorCallback: (msg: string) => void; hideIframe: boolean; removeIframe: boolean; iframe: HTMLIFrameElement; @@ -29,6 +31,7 @@ class VivliostylePrint { { title = "", printCallback = (iframeWin: Window) => iframeWin.print(), + errorCallback = null, hideIframe = true, removeIframe = true, }: PrintConfig, @@ -36,6 +39,7 @@ class VivliostylePrint { this.htmlDoc = htmlDoc; this.title = title; this.printCallback = printCallback; + this.errorCallback = errorCallback; this.hideIframe = hideIframe; this.removeIframe = removeIframe; } @@ -110,6 +114,15 @@ class VivliostylePrint { } }); + if (this.errorCallback) { + Viewer.addListener("error", (payload) => { + const msg = + payload.content.error?.toString() || + payload.content.messages.join("\n"); + this.errorCallback(msg); + }); + } + Viewer.loadDocument({ url: docURL, });