@@ -33,29 +33,21 @@ class SandboxReactNativeDelegate(
3333) {
3434 companion object {
3535 private const val TAG = " SandboxRNDelegate"
36+
37+ private val sharedHosts = mutableMapOf<String , SharedReactHost >()
38+
39+ private data class SharedReactHost (
40+ val reactHost : ReactHostImpl ,
41+ val sandboxContext : Context ,
42+ var refCount : Int ,
43+ )
3644 }
3745
38- var origin: String = " "
39- set(value) {
40- if (field == value) return
41- if (field.isNotEmpty()) {
42- SandboxRegistry .unregister(field)
43- }
44- field = value
45- if (value.isNotEmpty()) {
46- SandboxRegistry .register(value, this , allowedOrigins)
47- }
48- }
46+ @JvmField var origin: String = " "
4947
5048 var jsBundleSource: String = " "
5149 var allowedTurboModules: Set <String > = emptySet()
5250 var allowedOrigins: Set <String > = emptySet()
53- set(value) {
54- field = value
55- if (origin.isNotEmpty()) {
56- SandboxRegistry .register(origin, this , value)
57- }
58- }
5951
6052 @JvmField var hasOnMessageHandler: Boolean = false
6153
@@ -66,6 +58,7 @@ class SandboxReactNativeDelegate(
6658 private var reactSurface: ReactSurface ? = null
6759 private var jsiStateHandle: Long = 0
6860 private var sandboxReactContext: ReactContext ? = null
61+ private var ownsReactHost = false
6962
7063 @OptIn(UnstableReactNativeAPI ::class )
7164 fun loadReactNativeView (
@@ -79,49 +72,75 @@ class SandboxReactNativeDelegate(
7972
8073 val capturedBundleSource = jsBundleSource
8174 val capturedAllowedModules = allowedTurboModules
82- val sandboxId = System .identityHashCode(this ).toString(16 )
83- val sandboxContext = SandboxContextWrapper (context, sandboxId)
8475
8576 try {
86- val packages: List <ReactPackage > =
87- listOf (
88- FilteredReactPackage (MainReactPackage (), capturedAllowedModules),
89- )
90-
91- val bundleLoader = createBundleLoader(capturedBundleSource) ? : return null
92-
93- val tmmDelegateBuilder = DefaultTurboModuleManagerDelegate .Builder ()
94-
95- val bindingsInstaller = SandboxBindingsInstaller .create(this )
96-
97- val hostDelegate =
98- DefaultReactHostDelegate (
99- jsMainModulePath = capturedBundleSource,
100- jsBundleLoader = bundleLoader,
101- reactPackages = packages,
102- jsRuntimeFactory = HermesInstance (),
103- turboModuleManagerDelegateBuilder = tmmDelegateBuilder,
104- bindingsInstaller = bindingsInstaller,
105- )
106-
107- val componentFactory = ComponentFactory ()
108- DefaultComponentsRegistry .register(componentFactory)
109-
110- val host =
111- ReactHostImpl (
112- sandboxContext,
113- hostDelegate,
114- componentFactory,
115- true ,
116- true ,
117- )
77+ val shared = if (origin.isNotEmpty()) sharedHosts[origin] else null
78+
79+ val host: ReactHostImpl
80+ val sandboxContext: Context
81+
82+ if (shared != null ) {
83+ host = shared.reactHost
84+ sandboxContext = shared.sandboxContext
85+ shared.refCount++
86+ ownsReactHost = false
87+ Log .d(TAG , " Reusing shared ReactHost for origin '$origin ' (refCount=${shared.refCount} )" )
88+ } else {
89+ val sandboxId = System .identityHashCode(this ).toString(16 )
90+ sandboxContext = SandboxContextWrapper (context, sandboxId)
91+
92+ val packages: List <ReactPackage > =
93+ listOf (
94+ FilteredReactPackage (MainReactPackage (), capturedAllowedModules),
95+ )
96+
97+ val bundleLoader = createBundleLoader(capturedBundleSource) ? : return null
98+
99+ val tmmDelegateBuilder = DefaultTurboModuleManagerDelegate .Builder ()
100+
101+ val bindingsInstaller = SandboxBindingsInstaller .create(this )
102+
103+ val hostDelegate =
104+ DefaultReactHostDelegate (
105+ jsMainModulePath = capturedBundleSource,
106+ jsBundleLoader = bundleLoader,
107+ reactPackages = packages,
108+ jsRuntimeFactory = HermesInstance (),
109+ turboModuleManagerDelegateBuilder = tmmDelegateBuilder,
110+ bindingsInstaller = bindingsInstaller,
111+ )
112+
113+ val componentFactory = ComponentFactory ()
114+ DefaultComponentsRegistry .register(componentFactory)
115+
116+ host =
117+ ReactHostImpl (
118+ sandboxContext,
119+ hostDelegate,
120+ componentFactory,
121+ true ,
122+ true ,
123+ )
124+
125+ ownsReactHost = true
126+
127+ if (origin.isNotEmpty()) {
128+ sharedHosts[origin] = SharedReactHost (host, sandboxContext, refCount = 1 )
129+ Log .d(TAG , " Created shared ReactHost for origin '$origin '" )
130+ }
131+ }
118132
119133 reactHost = host
120134
121135 host.addReactInstanceEventListener(
122136 object : ReactInstanceEventListener {
123137 override fun onReactContextInitialized (reactContext : ReactContext ) {
124138 sandboxReactContext = reactContext
139+ if (jsiStateHandle != 0L ) {
140+ reactContext.runOnJSQueueThread {
141+ SandboxJSIInstaller .nativeInstallErrorHandler(jsiStateHandle)
142+ }
143+ }
125144 }
126145 },
127146 )
@@ -240,7 +259,8 @@ class SandboxReactNativeDelegate(
240259 return false
241260 }
242261
243- return routeMessage(messageJson, targetOrigin)
262+ // Routing handled entirely in C++ SandboxRegistry (see SandboxJSIInstaller.cpp)
263+ return false
244264 }
245265
246266 @Suppress(" unused" )
@@ -261,28 +281,6 @@ class SandboxReactNativeDelegate(
261281 }
262282 }
263283
264- fun routeMessage (
265- message : String ,
266- targetId : String ,
267- ): Boolean {
268- val target = SandboxRegistry .find(targetId)
269- Log .d(TAG , " routeMessage from '$origin ' to '$targetId ': target found=${target != null } " )
270- if (target == null ) return false
271-
272- if (! SandboxRegistry .isPermittedFrom(origin, targetId)) {
273- Log .w(TAG , " routeMessage DENIED: '$origin ' -> '$targetId '" )
274- sandboxView?.emitOnError(
275- " AccessDeniedError" ,
276- " Access denied: Sandbox '$origin ' is not permitted to send messages to '$targetId '" ,
277- )
278- return false
279- }
280-
281- Log .d(TAG , " routeMessage PERMITTED: '$origin ' -> '$targetId ', delivering..." )
282- target.postMessage(message)
283- return true
284- }
285-
286284 private fun getActivity (): Activity ? {
287285 var ctx = context
288286 while (ctx is android.content.ContextWrapper ) {
@@ -305,17 +303,28 @@ class SandboxReactNativeDelegate(
305303 }
306304 reactSurface = null
307305
308- reactHost?.let {
309- it.onHostDestroy()
310- it.destroy(" sandbox cleanup" , null )
306+ val host = reactHost
307+ if (host != null ) {
308+ if (origin.isNotEmpty()) {
309+ val shared = sharedHosts[origin]
310+ if (shared != null && shared.reactHost == = host) {
311+ shared.refCount--
312+ if (shared.refCount <= 0 ) {
313+ sharedHosts.remove(origin)
314+ host.onHostDestroy()
315+ host.destroy(" sandbox cleanup" , null )
316+ }
317+ }
318+ } else if (ownsReactHost) {
319+ host.onHostDestroy()
320+ host.destroy(" sandbox cleanup" , null )
321+ }
311322 }
312323 reactHost = null
324+ ownsReactHost = false
313325 }
314326
315327 fun destroy () {
316- if (origin.isNotEmpty()) {
317- SandboxRegistry .unregister(origin)
318- }
319328 cleanup()
320329 }
321330
0 commit comments