diff --git a/app/_layout.tsx b/app/_layout.tsx index 2021613..0e054fe 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -13,7 +13,7 @@ import * as React from "react"; import { SafeAreaView } from "react-native"; import { NAV_THEME } from "~/lib/constants"; import { useColorScheme } from "~/lib/useColorScheme"; -import PolyfillCrypto from "react-native-webview-crypto"; +import PolyfillCrypto from "~/polyfill"; import { SWRConfig } from "swr"; import { swrConfiguration } from "lib/swr"; import Toast from "react-native-toast-message"; diff --git a/package.json b/package.json index ccd6748..810547b 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "react-native-toast-message": "^2.2.0", "react-native-url-polyfill": "^2.0.0", "react-native-webview": "13.8.6", - "react-native-webview-crypto": "^0.0.25", + "webview-crypto": "^0.1.13", "swr": "^2.2.5", "tailwind-merge": "^2.3.0", "text-encoding": "^0.7.0", diff --git a/polyfill/index.js b/polyfill/index.js new file mode 100644 index 0000000..cd56fc9 --- /dev/null +++ b/polyfill/index.js @@ -0,0 +1,183 @@ +const React = require("react"); +const { StyleSheet, View } = require("react-native"); +const { WebView } = require("react-native-webview"); +const { MainWorker, webViewWorkerString } = require("webview-crypto"); + +const styles = StyleSheet.create({ + hide: { + display: "none", + position: "absolute", + width: 0, + height: 0, + flexGrow: 0, + flexShrink: 1, + }, +}); + +const internalLibrary = ` +(function () { + function postMessage (message) { + if (window.ReactNativeWebView.postMessage === undefined) { + setTimeout(postMessage, 200, message) + } else { + window.ReactNativeWebView.postMessage(message) + } + } + var wvw = new WebViewWorker(postMessage) + // for Android + window.document.addEventListener('message', function (e) {wvw.onMainMessage(e.data);}) + // for iOS + window.addEventListener('message', function (e) {wvw.onMainMessage(e.data);}) +}()) +`; + +let resolveWorker; +let workerPromise = new Promise((resolve) => { + resolveWorker = resolve; +}); + +function sendToWorker(message) { + workerPromise.then((worker) => worker.onWebViewMessage(message)); +} + +const subtle = { + fake: true, + decrypt(...args) { + return workerPromise.then((worker) => worker.crypto.subtle.decrypt(...args)); + }, + deriveBits(...args) { + return workerPromise.then((worker) => + worker.crypto.subtle.deriveBits(...args) + ); + }, + deriveKey(...args) { + return workerPromise.then((worker) => + worker.crypto.subtle.deriveKey(...args) + ); + }, + digest(...args) { + return workerPromise.then((worker) => worker.crypto.subtle.digest(...args)); + }, + encrypt(...args) { + return workerPromise.then((worker) => worker.crypto.subtle.encrypt(...args)); + }, + exportKey(...args) { + return workerPromise.then((worker) => + worker.crypto.subtle.exportKey(...args) + ); + }, + generateKey(...args) { + return workerPromise.then((worker) => + worker.crypto.subtle.generateKey(...args) + ); + }, + importKey(...args) { + return workerPromise.then((worker) => + worker.crypto.subtle.importKey(...args) + ); + }, + sign(...args) { + return workerPromise.then((worker) => worker.crypto.subtle.sign(...args)); + }, + unwrapKey(...args) { + return workerPromise.then((worker) => + worker.crypto.subtle.unwrapKey(...args) + ); + }, + verify(...args) { + return workerPromise.then((worker) => worker.crypto.subtle.verify(...args)); + }, + wrapKey(...args) { + return workerPromise.then((worker) => worker.crypto.subtle.wrapKey(...args)); + }, +}; + +class PolyfillCrypto extends React.Component { + constructor(props) { + super(props); + this.props = props; + this.webViewRef = React.createRef(); + this.state = { + webViewKey: 0, + }; + } + + shouldComponentUpdate(nextProps, nextState) { + return nextState.webViewKey !== this.state.webViewKey; + } + + componentDidMount() { + const webView = this.webViewRef.current; + + resolveWorker( + new MainWorker(msg => { + webView.postMessage(msg); + }, this.props.debug) + ); + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.webViewKey !== this.state.webViewKey) { + const webView = this.webViewRef.current; + resolveWorker( + new MainWorker( + (msg) => { + webView.postMessage(msg); + }, + this.props.debug + ) + ); + } + } + + componentWillUnmount() { + resolveWorker = undefined; + workerPromise = new Promise((resolve) => { + resolveWorker = resolve; + }); + } + + onContentProcessDidTerminate = (event) => { + const { nativeEvent } = event; + console.warn("Content process terminated, reloading", nativeEvent); + resolveWorker = undefined; + workerPromise = new Promise((resolve) => { + resolveWorker = resolve; + }); + this.setState((prevState) => ({ webViewKey: prevState.webViewKey + 1 })); + }; + + render() { + const code = `((function () {${webViewWorkerString};${internalLibrary}})())`; + const html = ``; + return ( + + + console.error(Object.keys(a), a.type, a.nativeEvent.description) + } + onMessage={(ev) => sendToWorker(ev.nativeEvent.data)} + ref={this.webViewRef} + originWhitelist={["*"]} + onContentProcessDidTerminate={this.onContentProcessDidTerminate} + source={{ html: html, baseUrl: "https://localhost" }} + /> + + ); + } +} + +if (typeof global.crypto !== "object") { + global.crypto = {}; +} + +global.crypto.fake = true; + +if (typeof global.crypto.subtle !== "object") { + global.crypto.subtle = subtle; +} + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = PolyfillCrypto; diff --git a/yarn.lock b/yarn.lock index 26b0345..c213af6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3541,7 +3541,7 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -encode-utf8@^1.0.2, encode-utf8@^1.0.3: +encode-utf8@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== @@ -3911,11 +3911,6 @@ fast-base64-decode@^1.0.0: resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== -fast-base64-encode@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-base64-encode/-/fast-base64-encode-1.0.0.tgz#883945eb67e139dbf5a877bcca57a89e6824c7d4" - integrity sha512-z2XCzVK4fde2cuTEHu2QGkLD6BPtJNKJPn0Z7oINvmhq/quUuIIVPYKUdN0gYeZqOyurjJjBH/bUzK5gafyHvw== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -6547,15 +6542,6 @@ react-native-url-polyfill@^2.0.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -react-native-webview-crypto@^0.0.25: - version "0.0.25" - resolved "https://registry.yarnpkg.com/react-native-webview-crypto/-/react-native-webview-crypto-0.0.25.tgz#c35506e1f092f7633db684f388f2b449667a05a2" - integrity sha512-H1kn5FFk0tBq5JDpkopyonAQTFEDAGoVJG+9Ip84jx4QmHmh5hxaJ5PkOXsMeNb2wHnwuvsg5p3krCOYNf20+A== - dependencies: - encode-utf8 "^1.0.2" - fast-base64-encode "^1.0.0" - webview-crypto "^0.1.13" - react-native-webview@13.8.6: version "13.8.6" resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.8.6.tgz#5d4a62cb311d5ef8d910a8e112b3f1f2807bcd18"