Skip to content

Commit

Permalink
fix: webview crypto polyfill issue (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya authored Sep 16, 2024
1 parent 716b1fa commit 698fc9a
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 17 deletions.
2 changes: 1 addition & 1 deletion app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
183 changes: 183 additions & 0 deletions polyfill/index.js
Original file line number Diff line number Diff line change
@@ -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 = `<html><body><script>${code}</script></body></html>`;
return (
<View style={styles.hide}>
<WebView
key={this.state.webViewKey}
javaScriptEnabled={true}
onError={(a) =>
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" }}
/>
</View>
);
}
}

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;
16 changes: 1 addition & 15 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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==
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"

[email protected]:
version "13.8.6"
resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.8.6.tgz#5d4a62cb311d5ef8d910a8e112b3f1f2807bcd18"
Expand Down

0 comments on commit 698fc9a

Please sign in to comment.