liutian hai 3 meses
pai
achega
bdebbb36ce
Modificáronse 2 ficheiros con 307 adicións e 7 borrados
  1. 23 7
      packages/core/src/worker/index.ts
  2. 284 0
      packages/core/src/worker/messageHandler.ts

+ 23 - 7
packages/core/src/worker/index.ts

@@ -1,14 +1,30 @@
 importScripts("./ComPDFkit.js")
 
-class ComPDFKitWorker {
-  static setup() {
+import MessageHandler from "./messageHandler"
 
+class ComPDFKitWorker {
+  static setup(gloabal) {
+    const messageHandler = new MessageHandler(gloabal)
+    this.messageHandler = messageHandler
   }
 }
 
-function (gloabal) {
-  gloabal.onmessage = function (e) {
-    const action = e.data.action
-    if (action === 'init')
+async function setUp() {
+  while (!runtimeInitialized) {
+    await sleep(100)
   }
-}(typeof window !== 'undefined' ? window : self)
+  return new Promise((resolve, reject) => {
+    resolve()
+  })
+}
+
+setUp().then(() => {
+  function (gloabal) {
+    gloabal.onmessage = function (e) {
+      const action = e.data.action
+      if (action === 'init') {
+        ComPDFKitWorker.setup(gloabal)
+      }
+    }
+  }(typeof window !== 'undefined' ? window : self)
+})

+ 284 - 0
packages/core/src/worker/messageHandler.ts

@@ -0,0 +1,284 @@
+type ActionHandler = {
+  [key: string]: [Function, any]
+}
+
+type CallbackCapabilities = {
+  [key: number]: PromiseCapability<any>
+}
+
+interface PromiseCapability<T> {
+  promise: Promise<T>
+  resolve: (value: T | PromiseLike<T>) => void
+  reject: (reason?: any) => void
+}
+
+class MessageHandler {
+  name: string
+  comObj: Window | Worker
+  callbackId: number
+  postMessageTransfers: boolean
+  callbacksCapabilities: CallbackCapabilities
+  actionHandler: ActionHandler
+  actionHandlerAsync: ActionHandler
+  nextAsync: Function | null
+  msgHandler: (event: MessageEvent) => void
+
+  constructor(name: string, comObj: Window | Worker) {
+    this.name = name
+    this.comObj = comObj
+    this.callbackId = 1
+    this.postMessageTransfers = true
+    this.callbacksCapabilities = {}
+    this.actionHandler = Object.create(null)
+    this.actionHandlerAsync = {}
+    this.nextAsync = null
+
+    this.actionHandler.console_log = [(k: any) => console.log(k)]
+    this.actionHandler.console_error = [(k: any) => console.error(k)]
+    this.actionHandler.workerLoaded = [() => {}]
+
+    this.msgHandler = this.handleMessage.bind(this)
+    comObj.addEventListener("message", this.msgHandler)
+  }
+
+  on(actionName: string, handler: Function, context?: any) {
+    if (this.actionHandler[actionName]) {
+      throw new Error(`There is already an actionName called "${actionName}"`)
+    }
+    this.actionHandler[actionName] = [handler, context]
+  }
+
+  clearActionHandlers() {
+    this.actionHandler = {}
+    this.comObj.removeEventListener("message", this.msgHandler)
+  }
+
+  reset() {
+    this.clearActionHandlers()
+    if (this.comObj.reset) {
+      this.comObj.reset()
+    }
+  }
+
+  replace(actionName: string, handler: Function, context?: any) {
+    this.actionHandler[actionName] = [handler, context]
+  }
+
+  onAsync(actionName: string, handler: Function, context?: any) {
+    if (this.actionHandlerAsync[actionName]) {
+      console.error(`There is already an actionName called "${actionName}"`)
+    }
+    this.actionHandlerAsync[actionName] = [handler, context]
+  }
+
+  replaceAsync(actionName: string, handler: Function, context?: any) {
+    if (this.actionHandler[actionName]) {
+      delete this.actionHandler[actionName]
+    }
+    this.actionHandlerAsync[actionName] = [handler, context]
+  }
+
+  onNextAsync(handler: Function) {
+    this.nextAsync = handler
+  }
+
+  send(action: string, data: any) {
+    this.postMessage({ action, data })
+  }
+
+  getNextId(): number {
+    return this.callbackId++
+  }
+
+  sendWithPromise(action: string, data: any, priority?: number): Promise<any> {
+    const callbackId = this.getNextId()
+    const message = { action, data, callbackId, priority }
+    const capability = this.createPromiseCapability()
+    this.callbacksCapabilities[callbackId] = capability
+
+    try {
+      this.postMessage(message)
+    } catch (error) {
+      capability.reject(error)
+    }
+
+    return capability.promise
+  }
+
+  sendWithPromiseReturnId(action: string, data: any, priority?: number): { promise: Promise<any>, callbackId: number } {
+    const callbackId = this.getNextId()
+    const message = { action, data, callbackId, priority }
+    const capability = this.createPromiseCapability()
+    this.callbacksCapabilities[callbackId] = capability
+
+    try {
+      this.postMessage(message)
+    } catch (error) {
+      capability.reject(error)
+    }
+
+    return { promise: capability.promise, callbackId }
+  }
+
+  sendWithPromiseWithId(action: string, callbackId: number, data: any): Promise<any> {
+    if (callbackId > this.callbackId) {
+      console.error(`Can't reuse callbackId ${callbackId} lesser than callbackId ${this.callbackId}`)
+    }
+    if (callbackId in this.callbacksCapabilities) {
+      console.error(`Can't reuse callbackId ${callbackId}. There is a capability waiting to be resolved.`)
+    }
+
+    const message = { action, data, callbackId }
+    const capability = this.createPromiseCapability()
+    this.callbacksCapabilities[callbackId] = capability
+
+    try {
+      this.postMessage(message)
+    } catch (error) {
+      capability.reject(error)
+    }
+
+    return capability.promise
+  }
+
+  sendError(error: any, callbackId: number) {
+    if (error.message || error.errorData) {
+      if (error.message && error.message.message) {
+        error.message = error.message.message
+      }
+      const errorData = error.errorData
+      error = {
+        type: error.type ? error.type : "JavascriptError",
+        message: error.message
+      }
+      if (errorData) {
+        Object.keys(errorData).forEach(key => {
+          if (errorData.hasOwnProperty(key)) {
+            error[key] = errorData[key]
+          }
+        })
+      }
+    }
+    this.postMessage({
+      isReply: true,
+      callbackId,
+      error
+    })
+  }
+
+  getPromise(callbackId: number): PromiseCapability<any> | undefined {
+    if (callbackId in this.callbacksCapabilities) {
+      return this.callbacksCapabilities[callbackId]
+    }
+    console.error(`Cannot get promise for callback ${callbackId}`)
+  }
+
+  cancelPromise(callbackId: number) {
+    if (callbackId in this.callbacksCapabilities) {
+      const capability = this.callbacksCapabilities[callbackId]
+      delete this.callbacksCapabilities[callbackId]
+      capability.reject({
+        type: "Cancelled",
+        message: "Request has been cancelled."
+      })
+      this.postMessage({
+        action: "actionCancel",
+        data: { callbackId }
+      })
+    } else {
+      console.warn(`Cannot cancel callback ${callbackId}`)
+    }
+  }
+
+  postMessage(message: any) {
+    if (this.postMessageTransfers) {
+      const transfers = this.getTransfersArray(message)
+      this.comObj.postMessage(message, transfers)
+    } else {
+      this.comObj.postMessage(message)
+    }
+  }
+
+  getObjectTransfers(obj: any, transfers: any[]) {
+    if (obj !== null && typeof obj === "object") {
+      if (obj instanceof Uint8Array) {
+        transfers.push(obj.buffer)
+      } else if (obj instanceof ArrayBuffer) {
+        transfers.push(obj)
+      } else {
+        Object.keys(obj).forEach(key => {
+          if (obj.hasOwnProperty(key)) {
+            this.getObjectTransfers(obj[key], transfers)
+          }
+        })
+      }
+    }
+  }
+
+  getTransfersArray(message: any): any[] | undefined {
+    const transfers: any[] = []
+    this.getObjectTransfers(message, transfers)
+    return transfers.length === 0 ? undefined : transfers
+  }
+
+  handleMessage(event: MessageEvent) {
+    const data = event.data
+
+    const actionHandler = this.actionHandler
+    const actionHandlerAsync = this.actionHandlerAsync
+    const callbacksCapabilities = this.callbacksCapabilities
+
+    if (data.action in actionHandler) {
+      const handler = actionHandler[data.action]
+      if (data.callbackId) {
+        Promise.resolve()
+          .then(() => handler[0].call(handler[1], data.data))
+          .then(result => {
+            this.postMessage({
+              isReply: true,
+              callbackId: data.callbackId,
+              data: result
+            })
+          })
+          .catch(error => {
+            this.sendError(error, data.callbackId)
+          })
+      } else {
+        handler[0].call(handler[1], data.data)
+      }
+    } else if (data.action in actionHandlerAsync) {
+      const handler = actionHandlerAsync[data.action]
+      if (data.callbackId) {
+        handler[0].call(handler[1], data)
+          .then(result => {
+            this.postMessage({
+              isReply: true,
+              callbackId: data.callbackId,
+              data: result
+            })
+            this.nextAsync()
+          })
+          .catch(error => {
+            this.sendError(error, data.callbackId)
+            this.nextAsync()
+          })
+      } else {
+        handler[0].call(handler[1], data)
+          .then(() => {
+            this.nextAsync()
+          })
+          .catch(() => {
+            this.nextAsync()
+          })
+      }
+    } else {
+      console.error(`Unknown action from worker: ${data.action}`)
+    }
+  }
+
+  private createPromiseCapability<T>(): PromiseCapability<T> {
+    const { promise, resolve, reject } = Promise.withResolvers<T>()
+    return { promise, resolve, reject }
+  }
+}
+export default MessageHandler