diff --git a/android/capacitor/src/main/assets/native-bridge.js b/android/capacitor/src/main/assets/native-bridge.js index 30b895772..0273db84c 100644 --- a/android/capacitor/src/main/assets/native-bridge.js +++ b/android/capacitor/src/main/assets/native-bridge.js @@ -166,9 +166,54 @@ const nativeBridge = (function (exports) { win.Capacitor = cap; win.Ionic.WebView = IonicWebView; }; + const shouldBeCloneable = (o) => { + const type = typeof o; + return (type === "undefined" || + o === null || + type === "boolean" || + type === "number" || + type === "string" || + o instanceof Date || + o instanceof RegExp || + o instanceof Blob || + o instanceof File || + o instanceof FileList || + o instanceof ArrayBuffer || + o instanceof Array || + o instanceof Map || + o instanceof Set); + }; + const isCloneable = (obj) => { + try { + postMessage(obj, "*"); + } + catch (error) { + if ((error === null || error === void 0 ? void 0 : error.code) === 25) + return false; // DATA_CLONE_ERR + } + return true; + }; + // https://stackoverflow.com/a/62544968 + const isProxy = (obj) => { + const _shouldBeCloneable = shouldBeCloneable(obj); + const _isCloneable = isCloneable(obj); + if (_isCloneable) { + return false; + } + if (!_shouldBeCloneable) { + return "maybe"; + } + return _shouldBeCloneable && !_isCloneable; + }; const safeStringify = (value) => { + if (isProxy(value)) { + return "proxy"; + } const seen = new Set(); return JSON.stringify(value, (_k, v) => { + if (isProxy(v)) { + return "proxy"; + } if (seen.has(v)) { return '...'; } @@ -242,6 +287,9 @@ const nativeBridge = (function (exports) { typeof c.dir === 'function'); }; const serializeConsoleMessage = (msg) => { + if (isProxy(msg)) { + return "proxy"; + } if (typeof msg === 'object') { try { msg = safeStringify(msg); @@ -258,12 +306,12 @@ const nativeBridge = (function (exports) { if (win.console && isIos) { for (const logfn of BRIDGED_CONSOLE_METHODS) { win.console[logfn] = (...args) => { - const msgs = [...args]; - originalConsole[logfn](...msgs); + // eslint-disable-next-line prefer-spread + originalConsole[logfn].apply(originalConsole, args); try { cap.toNative('Console', 'log', { level: logfn, - message: msgs.map(serializeConsoleMessage).join(' '), + message: args.map(serializeConsoleMessage).join(' '), }); } catch (e) { diff --git a/core/native-bridge.ts b/core/native-bridge.ts index 1e464796a..a304d6b5b 100644 --- a/core/native-bridge.ts +++ b/core/native-bridge.ts @@ -196,9 +196,59 @@ const initBridge = (w: any): void => { win.Ionic.WebView = IonicWebView; }; + const shouldBeCloneable = (o: any): boolean => { + const type = typeof o; + return ( + type === "undefined" || + o === null || + type === "boolean" || + type === "number" || + type === "string" || + o instanceof Date || + o instanceof RegExp || + o instanceof Blob || + o instanceof File || + o instanceof FileList || + o instanceof ArrayBuffer || + o instanceof Array || + o instanceof Map || + o instanceof Set + ); + } + + const isCloneable = (obj: any): boolean => { + try { + postMessage(obj, "*"); + } catch (error) { + if (error?.code === 25) return false; // DATA_CLONE_ERR + } + + return true; + } + + // https://stackoverflow.com/a/62544968 + const isProxy = (obj: any): string | boolean => { + const _shouldBeCloneable = shouldBeCloneable(obj); + const _isCloneable = isCloneable(obj); + + if(_isCloneable) { + return false; + } + + if(!_shouldBeCloneable) { + return "maybe"; + } + + return _shouldBeCloneable && !_isCloneable; + } + const safeStringify = (value: any): string => { + if (isProxy(value)) { return "proxy" } const seen = new Set(); return JSON.stringify(value, (_k, v) => { + if (isProxy(v)) { + return "proxy" + } if (seen.has(v)) { return '...'; } @@ -286,8 +336,9 @@ const initBridge = (w: any): void => { typeof c.dir === 'function' ); }; - + const serializeConsoleMessage = (msg: any): string => { + if (isProxy(msg)) { return "proxy" } if (typeof msg === 'object') { try { msg = safeStringify(msg); @@ -306,14 +357,14 @@ const initBridge = (w: any): void => { if (win.console && isIos) { for (const logfn of BRIDGED_CONSOLE_METHODS) { win.console[logfn] = (...args: any[]) => { - const msgs = [...args]; - originalConsole[logfn](...msgs); + // eslint-disable-next-line prefer-spread + originalConsole[logfn].apply(originalConsole, args); try { cap.toNative('Console', 'log', { level: logfn, - message: msgs.map(serializeConsoleMessage).join(' '), + message: args.map(serializeConsoleMessage).join(' '), }); } catch (e) { // error converting/posting console messages diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js index 30b895772..0273db84c 100644 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ b/ios/Capacitor/Capacitor/assets/native-bridge.js @@ -166,9 +166,54 @@ const nativeBridge = (function (exports) { win.Capacitor = cap; win.Ionic.WebView = IonicWebView; }; + const shouldBeCloneable = (o) => { + const type = typeof o; + return (type === "undefined" || + o === null || + type === "boolean" || + type === "number" || + type === "string" || + o instanceof Date || + o instanceof RegExp || + o instanceof Blob || + o instanceof File || + o instanceof FileList || + o instanceof ArrayBuffer || + o instanceof Array || + o instanceof Map || + o instanceof Set); + }; + const isCloneable = (obj) => { + try { + postMessage(obj, "*"); + } + catch (error) { + if ((error === null || error === void 0 ? void 0 : error.code) === 25) + return false; // DATA_CLONE_ERR + } + return true; + }; + // https://stackoverflow.com/a/62544968 + const isProxy = (obj) => { + const _shouldBeCloneable = shouldBeCloneable(obj); + const _isCloneable = isCloneable(obj); + if (_isCloneable) { + return false; + } + if (!_shouldBeCloneable) { + return "maybe"; + } + return _shouldBeCloneable && !_isCloneable; + }; const safeStringify = (value) => { + if (isProxy(value)) { + return "proxy"; + } const seen = new Set(); return JSON.stringify(value, (_k, v) => { + if (isProxy(v)) { + return "proxy"; + } if (seen.has(v)) { return '...'; } @@ -242,6 +287,9 @@ const nativeBridge = (function (exports) { typeof c.dir === 'function'); }; const serializeConsoleMessage = (msg) => { + if (isProxy(msg)) { + return "proxy"; + } if (typeof msg === 'object') { try { msg = safeStringify(msg); @@ -258,12 +306,12 @@ const nativeBridge = (function (exports) { if (win.console && isIos) { for (const logfn of BRIDGED_CONSOLE_METHODS) { win.console[logfn] = (...args) => { - const msgs = [...args]; - originalConsole[logfn](...msgs); + // eslint-disable-next-line prefer-spread + originalConsole[logfn].apply(originalConsole, args); try { cap.toNative('Console', 'log', { level: logfn, - message: msgs.map(serializeConsoleMessage).join(' '), + message: args.map(serializeConsoleMessage).join(' '), }); } catch (e) {