From abc1f3d33bf18125b36534dc4b3fa8b2172515c4 Mon Sep 17 00:00:00 2001 From: Calinteodor Date: Tue, 9 Sep 2025 12:46:11 +0300 Subject: [PATCH] dep(react-native-worklets-core): Replace duktape to align with Android 16kb page size alignment (#16393) * Replaced duktape lib with react-native-worklets-core and checked for compatibility with Android 16kb page-size requirement. --- android/sdk/build.gradle | 2 +- .../meet/sdk/JavaScriptSandboxModule.java | 57 --------- .../meet/sdk/ReactInstanceManagerHolder.java | 3 +- android/settings.gradle | 2 + babel.config.js | 12 +- ios/Podfile.lock | 25 ++++ ios/sdk/sdk.xcodeproj/project.pbxproj | 6 - ios/sdk/src/JavaScriptSandbox.m | 55 --------- package-lock.json | 113 ++++++++++++++++-- package.json | 3 + react-native-sdk/android/build.gradle | 5 +- react-native-sdk/prepare_sdk.js | 8 -- .../base/lib-jitsi-meet/functions.native.ts | 52 ++++++-- 13 files changed, 190 insertions(+), 153 deletions(-) delete mode 100644 android/sdk/src/main/java/org/jitsi/meet/sdk/JavaScriptSandboxModule.java delete mode 100644 ios/sdk/src/JavaScriptSandbox.m diff --git a/android/sdk/build.gradle b/android/sdk/build.gradle index b6aa563d23..9655ca4404 100644 --- a/android/sdk/build.gradle +++ b/android/sdk/build.gradle @@ -46,7 +46,6 @@ dependencies { implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1' implementation 'com.jakewharton.timber:timber:5.0.1' - implementation 'com.squareup.duktape:duktape-android:1.3.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'androidx.startup:startup-runtime:1.1.0' implementation 'com.google.j2objc:j2objc-annotations:3.0.0' @@ -87,6 +86,7 @@ dependencies { implementation project(':react-native-svg') implementation project(':react-native-video') implementation project(':react-native-webview') + implementation project(':react-native-worklets-core') // Use `api` here so consumers can use WebRTCModuleOptions. api project(':react-native-webrtc') diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JavaScriptSandboxModule.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JavaScriptSandboxModule.java deleted file mode 100644 index 38d8d94d91..0000000000 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JavaScriptSandboxModule.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright @ 2019-present 8x8, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jitsi.meet.sdk; - -import com.facebook.react.bridge.Promise; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.module.annotations.ReactModule; -import com.squareup.duktape.Duktape; - -@ReactModule(name = JavaScriptSandboxModule.NAME) -class JavaScriptSandboxModule extends ReactContextBaseJavaModule { - public static final String NAME = "JavaScriptSandbox"; - - public JavaScriptSandboxModule(ReactApplicationContext reactContext) { - super(reactContext); - } - - /** - * Evaluates the given code in a Duktape VM. - * @param code - The code that needs to evaluated. - * @param promise - Resolved with the output in case of success or rejected with an exception - * in case of failure. - */ - @ReactMethod - public void evaluate(String code, Promise promise) { - Duktape vm = Duktape.create(); - try { - Object res = vm.evaluate(code); - promise.resolve(res.toString()); - } catch (Throwable tr) { - promise.reject(tr); - } finally { - vm.close(); - } - } - - @Override - public String getName() { - return NAME; - } -} diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java index ee730edba5..3de592cd49 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java @@ -17,7 +17,6 @@ package org.jitsi.meet.sdk; import android.annotation.SuppressLint; -import android.app.Activity; import android.app.Application; import androidx.annotation.Nullable; @@ -65,7 +64,6 @@ class ReactInstanceManagerHolder { new AudioModeModule(reactContext), new DropboxModule(reactContext), new ExternalAPIModule(reactContext), - new JavaScriptSandboxModule(reactContext), new LocaleDetector(reactContext), new LogBridgeModule(reactContext), new PictureInPictureModule(reactContext), @@ -110,6 +108,7 @@ class ReactInstanceManagerHolder { new com.horcrux.svg.SvgPackage(), new org.wonday.orientation.OrientationPackage(), new com.splashview.SplashViewPackage(), + new com.worklets.WorkletsCorePackage(), new ReactPackageAdapter() { @Override public List createNativeModules(ReactApplicationContext reactContext) { diff --git a/android/settings.gradle b/android/settings.gradle index 43bb1401cf..a9ad548369 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -52,3 +52,5 @@ include ':react-native-webrtc' project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android') include ':react-native-webview' project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android') +include ':react-native-worklets-core' +project(':react-native-worklets-core').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-worklets-core/android') diff --git a/babel.config.js b/babel.config.js index c2e341daff..4141f6af91 100644 --- a/babel.config.js +++ b/babel.config.js @@ -8,9 +8,13 @@ module.exports = { // This happens because react native has conflict with @babel/plugin-transform-private-methods plugin // https://github.com/ethers-io/ethers.js/discussions/4309#discussioncomment-6694524 - plugins: [ 'optional-require', - [ '@babel/plugin-transform-private-methods', { - 'loose': true - } ] + plugins: [ + 'optional-require', + [ + '@babel/plugin-transform-private-methods', { + 'loose': true + } + ], + 'react-native-worklets-core/plugin' ] }; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 14042230e0..57b96016d2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1497,6 +1497,27 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-worklets-core (1.6.2): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - React-nativeconfig (0.77.2) - React-NativeModulesApple (0.77.2): - glog @@ -1932,6 +1953,7 @@ DEPENDENCIES: - react-native-video (from `../node_modules/react-native-video`) - react-native-webrtc (from `../node_modules/react-native-webrtc`) - react-native-webview (from `../node_modules/react-native-webview`) + - react-native-worklets-core (from `../node_modules/react-native-worklets-core`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -2101,6 +2123,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-webrtc" react-native-webview: :path: "../node_modules/react-native-webview" + react-native-worklets-core: + :path: "../node_modules/react-native-worklets-core" React-nativeconfig: :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: @@ -2257,6 +2281,7 @@ SPEC CHECKSUMS: react-native-video: eb861d67a71dfef1bbf6086a811af5f338b13781 react-native-webrtc: 2261a482150195092246fe70b3aff976f2e11ec5 react-native-webview: 079eca50edf657503318b66687dadfb903731aa8 + react-native-worklets-core: b59cf88762c8fb6132d8796babd4cec15217d6f0 React-nativeconfig: ecf4dc92c40b97e2b3f0c619938f78bfd6507b08 React-NativeModulesApple: f457bbfb30fb3bc41979b1a87b99d292d7340d39 React-perflogger: 1111b5feb064c4cc83df88fb403efda54b387951 diff --git a/ios/sdk/sdk.xcodeproj/project.pbxproj b/ios/sdk/sdk.xcodeproj/project.pbxproj index 17d658005c..e14b5a8aa9 100644 --- a/ios/sdk/sdk.xcodeproj/project.pbxproj +++ b/ios/sdk/sdk.xcodeproj/project.pbxproj @@ -51,7 +51,6 @@ C81E9AB925AC5AD800B134D9 /* ExternalAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */; }; C8AFD27F2462C613000293D2 /* InfoPlistUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; C8AFD2802462C613000293D2 /* InfoPlistUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */; }; - DE438CDA2350934700DD541D /* JavaScriptSandbox.m in Sources */ = {isa = PBXBuildFile; fileRef = DE438CD82350934700DD541D /* JavaScriptSandbox.m */; }; DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AAC92317FFCD00290BEC /* LogUtils.h */; }; DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */; }; DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -98,7 +97,6 @@ DE9A015C289A9A9A00E41CBB /* JitsiMeetLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = DE81A2D32316AC4D00AE1940 /* JitsiMeetLogger.m */; }; DE9A015E289A9A9A00E41CBB /* JitsiMeetView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */; }; DE9A015F289A9A9A00E41CBB /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; }; - DE9A0160289A9A9A00E41CBB /* JavaScriptSandbox.m in Sources */ = {isa = PBXBuildFile; fileRef = DE438CD82350934700DD541D /* JavaScriptSandbox.m */; }; DE9A0162289A9A9A00E41CBB /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BB9AD781F5EC6D7001C08DB /* Intents.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; DE9A0163289A9A9A00E41CBB /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BB9AD761F5EC6CE001C08DB /* CallKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; DE9A0166289A9A9A00E41CBB /* CallKitIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 0BC4B8681F8C01E100CE8B21 /* CallKitIcon.png */; }; @@ -161,7 +159,6 @@ C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExternalAPI.h; sourceTree = ""; }; C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InfoPlistUtil.h; sourceTree = ""; }; C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InfoPlistUtil.m; sourceTree = ""; }; - DE438CD82350934700DD541D /* JavaScriptSandbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JavaScriptSandbox.m; sourceTree = ""; }; DE65AAC92317FFCD00290BEC /* LogUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LogUtils.h; sourceTree = ""; }; DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetBaseLogHandler+Private.h"; sourceTree = ""; }; DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetUserInfo.h; sourceTree = ""; }; @@ -250,7 +247,6 @@ C69EFA02209A0EFD0027712B /* callkit */, A4A934E7212F3AB8001E9388 /* dropbox */, 0BD906E91EC0C00300C8C18E /* Info.plist */, - DE438CD82350934700DD541D /* JavaScriptSandbox.m */, 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */, DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */, DEA9F283258A5D9900D4CD74 /* JitsiMeetSDK.h */, @@ -681,7 +677,6 @@ DE81A2D52316AC4D00AE1940 /* JitsiMeetLogger.m in Sources */, 0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */, DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */, - DE438CDA2350934700DD541D /* JavaScriptSandbox.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -714,7 +709,6 @@ 4E0EF63328CA2FB3005D1B03 /* JMCallKitEmitter.m in Sources */, DE9A015E289A9A9A00E41CBB /* JitsiMeetView.m in Sources */, DE9A015F289A9A9A00E41CBB /* JitsiMeet.m in Sources */, - DE9A0160289A9A9A00E41CBB /* JavaScriptSandbox.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/sdk/src/JavaScriptSandbox.m b/ios/sdk/src/JavaScriptSandbox.m deleted file mode 100644 index 66a9d74a1e..0000000000 --- a/ios/sdk/src/JavaScriptSandbox.m +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright @ 2019-present 8x8, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@import JavaScriptCore; - -#import - - -@interface JavaScriptSandbox : NSObject -@end - -@implementation JavaScriptSandbox - -RCT_EXPORT_MODULE(); - -+ (BOOL)requiresMainQueueSetup { - return NO; -} - -#pragma mark - Exported methods - -RCT_EXPORT_METHOD(evaluate:(NSString *)code - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject) { - __block BOOL hasError = NO; - JSContext *ctx = [[JSContext alloc] init]; - ctx.exceptionHandler = ^(JSContext *context, JSValue *exception) { - hasError = YES; - reject(@"evaluate", [exception toString], nil); - }; - JSValue *ret = [ctx evaluateScript:code]; - if (!hasError) { - NSString *result = [ret toString]; - if (result == nil) { - reject(@"evaluate", @"Error in string coercion", nil); - } else { - resolve(result); - } - } -} - -@end diff --git a/package-lock.json b/package-lock.json index 986983d8ea..4684c67f54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -104,6 +104,7 @@ "react-native-watch-connectivity": "1.1.0", "react-native-webrtc": "124.0.4", "react-native-webview": "13.13.5", + "react-native-worklets-core": "https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18", "react-native-youtube-iframe": "2.3.0", "react-redux": "7.2.9", "react-textarea-autosize": "8.3.0", @@ -125,6 +126,8 @@ }, "devDependencies": { "@babel/core": "7.25.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", + "@babel/plugin-proposal-optional-chaining": "7.21.0", "@babel/plugin-transform-private-methods": "7.25.9", "@babel/preset-env": "7.25.9", "@babel/preset-react": "7.25.9", @@ -1001,6 +1004,43 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -1840,13 +1880,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -22491,6 +22531,19 @@ "react-native": "*" } }, + "node_modules/react-native-worklets-core": { + "version": "1.6.2", + "resolved": "git+ssh://git@github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18", + "integrity": "sha512-SW47DvuNLjhoj8PJK8haq0B/69//ChG+/3WyK9NkhwUSM1kqZLle1O7SqGeLFp2f37qyBXH5X+cXPDaabJ8CHA==", + "license": "MIT", + "dependencies": { + "string-hash-64": "^1.0.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-youtube-iframe": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.3.0.tgz", @@ -24474,6 +24527,12 @@ } ] }, + "node_modules/string-hash-64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz", + "integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==", + "license": "MIT" + }, "node_modules/string-replace-to-array": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string-replace-to-array/-/string-replace-to-array-1.0.3.tgz", @@ -27669,6 +27728,27 @@ "@babel/helper-plugin-utils": "^7.25.9" } }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, "@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -28153,12 +28233,12 @@ } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "requires": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" } }, "@babel/plugin-transform-parameters": { @@ -42665,6 +42745,14 @@ "invariant": "2.2.4" } }, + "react-native-worklets-core": { + "version": "git+ssh://git@github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18", + "integrity": "sha512-SW47DvuNLjhoj8PJK8haq0B/69//ChG+/3WyK9NkhwUSM1kqZLle1O7SqGeLFp2f37qyBXH5X+cXPDaabJ8CHA==", + "from": "react-native-worklets-core@https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18", + "requires": { + "string-hash-64": "^1.0.3" + } + }, "react-native-youtube-iframe": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.3.0.tgz", @@ -44022,6 +44110,11 @@ } } }, + "string-hash-64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz", + "integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==" + }, "string-replace-to-array": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string-replace-to-array/-/string-replace-to-array-1.0.3.tgz", diff --git a/package.json b/package.json index e0110c2774..5ef291ee1a 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "react-native-watch-connectivity": "1.1.0", "react-native-webrtc": "124.0.4", "react-native-webview": "13.13.5", + "react-native-worklets-core": "https://github.com/jitsi/react-native-worklets-core.git#8c5dfab2a5907305da8971696a781b60f0f9cb18", "react-native-youtube-iframe": "2.3.0", "react-redux": "7.2.9", "react-textarea-autosize": "8.3.0", @@ -131,6 +132,8 @@ }, "devDependencies": { "@babel/core": "7.25.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", + "@babel/plugin-proposal-optional-chaining": "7.21.0", "@babel/plugin-transform-private-methods": "7.25.9", "@babel/preset-env": "7.25.9", "@babel/preset-react": "7.25.9", diff --git a/react-native-sdk/android/build.gradle b/react-native-sdk/android/build.gradle index 6638f7d31b..83bfa791ce 100644 --- a/react-native-sdk/android/build.gradle +++ b/react-native-sdk/android/build.gradle @@ -124,12 +124,11 @@ repositories { dependencies { - //noinspection GradleDynamicVersion + // noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" - implementation 'com.squareup.duktape:duktape-android:1.3.0' implementation 'com.dropbox.core:dropbox-core-sdk:4.0.1' implementation 'com.jakewharton.timber:timber:4.7.1' -// From node_modules + // From node_modules } if (isNewArchitectureEnabled()) { diff --git a/react-native-sdk/prepare_sdk.js b/react-native-sdk/prepare_sdk.js index 5a16e3fb56..cc6ab78f39 100644 --- a/react-native-sdk/prepare_sdk.js +++ b/react-native-sdk/prepare_sdk.js @@ -100,10 +100,6 @@ fs.copyFileSync( `${iosSrcPath}/InfoPlistUtil.h`, `${iosDestPath}/InfoPlistUtil.h` ); -fs.copyFileSync( - `${iosSrcPath}/JavaScriptSandbox.m`, - `${iosDestPath}/JavaScriptSandbox.m` -); fs.copyFileSync( `${iosSrcPath}/JitsiAudioSession.m`, `${iosDestPath}/JitsiAudioSession.m` @@ -184,10 +180,6 @@ fs.copyFileSync( `${androidSourcePath}/ConnectionService.java`, `${androidTargetPath}/ConnectionService.java` ); -fs.copyFileSync( - `${androidSourcePath}/JavaScriptSandboxModule.java`, - `${androidTargetPath}/JavaScriptSandboxModule.java` -); fs.copyFileSync( `${androidSourcePath}/LocaleDetector.java`, `${androidTargetPath}/LocaleDetector.java` diff --git a/react/features/base/lib-jitsi-meet/functions.native.ts b/react/features/base/lib-jitsi-meet/functions.native.ts index 28b57d0a08..f87ea51252 100644 --- a/react/features/base/lib-jitsi-meet/functions.native.ts +++ b/react/features/base/lib-jitsi-meet/functions.native.ts @@ -1,6 +1,7 @@ // @ts-ignore import { safeJsonParse } from '@jitsi/js-utils/json'; -import { NativeModules } from 'react-native'; +// @ts-ignore +import { Worklets } from 'react-native-worklets-core'; import { loadScript } from '../util/loadScript.native'; @@ -8,7 +9,12 @@ import logger from './logger'; export * from './functions.any'; -const { JavaScriptSandbox } = NativeModules; + +/** + * Worklet context usefull for running small tasks off the JS thread. + */ +export const workletContext = Worklets.createContext('ConfigParser'); + /** * Loads config.js from a specific remote server. @@ -18,14 +24,46 @@ const { JavaScriptSandbox } = NativeModules; */ export async function loadConfig(url: string): Promise { try { - const configTxt = await loadScript(url, 10 * 1000 /* Timeout in ms */, true /* skipeval */); - const configJson = await JavaScriptSandbox.evaluate(`${configTxt}\nJSON.stringify(config);`); - const config = safeJsonParse(configJson); + const configTxt = await loadScript(url, 10 * 1000, true); - if (typeof config !== 'object') { - throw new Error('config is not an object'); + const parseConfigAsync = workletContext.createRunAsync(function parseConfig(configText: string): string { + 'worklet'; + try { + // Used IIFE wrapper to capture config object from config.js + const configObj = eval( + '(function(){\n' + + configText + + '\n; return (typeof config !== "undefined" ? config : globalThis.config); })()' + ); + + if (configObj == void 0) { + return 'Worklet_Error: config is undefined after eval()'; + } + + if (typeof configObj !== 'object') { + return 'Worklet_Error: config is not an object'; + } + + return JSON.stringify(configObj); + } catch (err) { + return 'Worklet_Error:' + ((err as Error)?.message ?? String(err)); + } + }); + + const workletConfig = await parseConfigAsync(configTxt); + + if (typeof workletConfig !== 'string') { + throw new Error('Worklet error: workletConfig is not a string'); } + if (workletConfig.startsWith('Worklet_Error:')) { + const msg = workletConfig.slice('Worklet_Error:'.length); + + throw new Error(`Worklet error: ${msg}`); + } + + const config = safeJsonParse(workletConfig); + logger.info(`Config loaded from ${url}`); return config;