From 7cc761a8c0ff12217ff8478bdb5a2fce3a07833a Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 18 Jan 2022 13:07:39 +0100 Subject: [PATCH] Polyfill `structuredClone` with core-js (PR 13948 follow-up) This allows us to remove the manually implemented `structuredClone` polyfill, thus reducing the maintenance burden for the `LoopbackPort` class; refer to https://github.com/zloirock/core-js#structuredclone *Please note:* While `structuredClone` support landed already in Firefox 94, Google Chrome only added it in version 98 (currently in Beta). However, given that the `LoopbackPort` will only be used together with *fake workers* in browsers this shouldn't be too much of a problem.[1] For Node.js environments, where *fake workers* are unfortunately necessary, using a `legacy/`-build is already required which thus guarantees that the `structuredClone` polyfill is available. Also, the patch updates core-js to the latest version since that one includes `structuredClone` improvements; please see https://github.com/zloirock/core-js/releases/tag/v3.20.3 --- [1] Given that we only support browsers with proper worker support, if *fake workers* are being used that essentially indicates a configuration problem/error. --- .eslintrc | 1 + package-lock.json | 36 +++++++++----- package.json | 2 +- src/display/api.js | 93 ++++--------------------------------- src/shared/compatibility.js | 13 ++++++ 5 files changed, 47 insertions(+), 98 deletions(-) diff --git a/.eslintrc b/.eslintrc index 22ade7ddb..acd6331c5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -28,6 +28,7 @@ "globals": { "PDFJSDev": false, "exports": false, + "structuredClone": false, "SystemJS": false, }, diff --git a/package-lock.json b/package-lock.json index 41bece0bf..12632b8d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "autoprefixer": "^10.4.2", "babel-loader": "^8.2.3", "canvas": "^2.9.0", - "core-js": "^3.20.2", + "core-js": "^3.20.3", "cross-env": "^7.0.3", "dommatrix": "^0.0.24", "es-module-shims": "^1.4.1", @@ -3899,9 +3899,9 @@ } }, "node_modules/core-js": { - "version": "3.20.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.2.tgz", - "integrity": "sha512-nuqhq11DcOAbFBV4zCbKeGbKQsUDRqTX0oqx7AttUBuqe3h20ixsE039QHelbL6P4h+9kytVqyEtyZ6gsiwEYw==", + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", "dev": true, "hasInstallScript": true, "funding": { @@ -12106,6 +12106,7 @@ }, "node_modules/npm/node_modules/lodash._baseindexof": { "version": "3.1.0", + "extraneous": true, "inBundle": true, "license": "MIT" }, @@ -12121,16 +12122,19 @@ }, "node_modules/npm/node_modules/lodash._bindcallback": { "version": "3.0.1", + "extraneous": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/lodash._cacheindexof": { "version": "3.0.2", + "extraneous": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/lodash._createcache": { "version": "3.1.2", + "extraneous": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -12145,6 +12149,7 @@ }, "node_modules/npm/node_modules/lodash._getnative": { "version": "3.9.1", + "extraneous": true, "inBundle": true, "license": "MIT" }, @@ -12162,6 +12167,7 @@ }, "node_modules/npm/node_modules/lodash.restparam": { "version": "3.6.1", + "extraneous": true, "inBundle": true, "license": "MIT" }, @@ -21554,9 +21560,9 @@ } }, "core-js": { - "version": "3.20.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.2.tgz", - "integrity": "sha512-nuqhq11DcOAbFBV4zCbKeGbKQsUDRqTX0oqx7AttUBuqe3h20ixsE039QHelbL6P4h+9kytVqyEtyZ6gsiwEYw==", + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", "dev": true }, "core-js-compat": { @@ -27927,7 +27933,8 @@ }, "lodash._baseindexof": { "version": "3.1.0", - "bundled": true + "bundled": true, + "extraneous": true }, "lodash._baseuniq": { "version": "4.6.0", @@ -27940,15 +27947,18 @@ }, "lodash._bindcallback": { "version": "3.0.1", - "bundled": true + "bundled": true, + "extraneous": true }, "lodash._cacheindexof": { "version": "3.0.2", - "bundled": true + "bundled": true, + "extraneous": true }, "lodash._createcache": { "version": "3.1.2", "bundled": true, + "extraneous": true, "requires": { "lodash._getnative": "^3.0.0" } @@ -27960,7 +27970,8 @@ }, "lodash._getnative": { "version": "3.9.1", - "bundled": true + "bundled": true, + "extraneous": true }, "lodash._root": { "version": "3.0.1", @@ -27974,7 +27985,8 @@ }, "lodash.restparam": { "version": "3.6.1", - "bundled": true + "bundled": true, + "extraneous": true }, "lodash.union": { "version": "4.6.0", diff --git a/package.json b/package.json index d41ac12fb..ae431a577 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "autoprefixer": "^10.4.2", "babel-loader": "^8.2.3", "canvas": "^2.9.0", - "core-js": "^3.20.2", + "core-js": "^3.20.3", "cross-env": "^7.0.3", "dommatrix": "^0.0.24", "es-module-shims": "^1.4.1", diff --git a/src/display/api.js b/src/display/api.js index beb6ba9f2..67b3dd178 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -1913,91 +1913,14 @@ class LoopbackPort { } postMessage(obj, transfers) { - function cloneValue(object) { - if ( - (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || - globalThis.structuredClone - ) { - return globalThis.structuredClone(object, transfers); - } - - // Trying to perform a structured clone close to the spec, including - // transfers. - function fallbackCloneValue(value) { - if ( - typeof value === "function" || - typeof value === "symbol" || - value instanceof URL - ) { - throw new Error( - `LoopbackPort.postMessage - cannot clone: ${value?.toString()}` - ); - } - - if (typeof value !== "object" || value === null) { - return value; - } - if (cloned.has(value)) { - // already cloned the object - return cloned.get(value); - } - let buffer, result; - if ((buffer = value.buffer) && isArrayBuffer(buffer)) { - // We found object with ArrayBuffer (typed array). - if (transfers?.includes(buffer)) { - result = new value.constructor( - buffer, - value.byteOffset, - value.byteLength - ); - } else { - result = new value.constructor(value); - } - cloned.set(value, result); - return result; - } - if (value instanceof Map) { - result = new Map(); - cloned.set(value, result); // Adding to cache now for cyclic references. - for (const [key, val] of value) { - result.set(key, fallbackCloneValue(val)); - } - return result; - } - if (value instanceof Set) { - result = new Set(); - cloned.set(value, result); // Adding to cache now for cyclic references. - for (const val of value) { - result.add(fallbackCloneValue(val)); - } - return result; - } - result = Array.isArray(value) ? [] : Object.create(null); - cloned.set(value, result); // Adding to cache now for cyclic references. - // Cloning all value and object properties, however ignoring properties - // defined via getter. - for (const i in value) { - let desc, - p = value; - while (!(desc = Object.getOwnPropertyDescriptor(p, i))) { - p = Object.getPrototypeOf(p); - } - if (typeof desc.value === "undefined") { - continue; - } - if (typeof desc.value === "function" && !value.hasOwnProperty?.(i)) { - continue; - } - result[i] = fallbackCloneValue(desc.value); - } - return result; - } - - const cloned = new WeakMap(); - return fallbackCloneValue(object); - } - - const event = { data: cloneValue(obj) }; + const event = { + data: + typeof PDFJSDev === "undefined" || + PDFJSDev.test("SKIP_BABEL") || + transfers + ? structuredClone(obj, transfers) + : structuredClone(obj), + }; this._deferred.then(() => { for (const listener of this._listeners) { diff --git a/src/shared/compatibility.js b/src/shared/compatibility.js index f418f8f1e..3eed8a577 100644 --- a/src/shared/compatibility.js +++ b/src/shared/compatibility.js @@ -100,4 +100,17 @@ if ( globalThis.ReadableStream = require("web-streams-polyfill/dist/ponyfill.js").ReadableStream; })(); + + // Support: Firefox<94, Chrome<98, Safari, Node.js<17.0.0 + (function checkStructuredClone() { + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("IMAGE_DECODERS")) { + // The current image decoders are synchronous, hence `structuredClone` + // shouldn't need to be polyfilled for the IMAGE_DECODERS build target. + return; + } + if (globalThis.structuredClone) { + return; + } + require("core-js/web/structured-clone.js"); + })(); }