diff --git a/packages/react-native/Libraries/Core/polyfillPromise.js b/packages/react-native/Libraries/Core/polyfillPromise.js index 5bfec271d1c5..14124d809dc5 100644 --- a/packages/react-native/Libraries/Core/polyfillPromise.js +++ b/packages/react-native/Libraries/Core/polyfillPromise.js @@ -10,29 +10,22 @@ 'use strict'; -const {polyfillGlobal} = require('../Utilities/PolyfillFunctions'); - /** - * Set up Promise. The native Promise implementation throws the following error: - * ERROR: Event loop not supported. + * Set up Promise. Hermes provides a native Promise implementation that + * satisfies all requirements of React Native. * * If you don't need these polyfills, don't use InitializeCore; just directly * require the modules you need from InitializeCore for setup. */ -// If global.Promise is provided by Hermes, we are confident that it can provide -// all the methods needed by React Native, so we can directly use it. -if (global?.HermesInternal?.hasPromise?.()) { - const HermesPromise = global.Promise; +// Hermes is the only supported JS engine and always provides Promise natively. +const HermesPromise = global.Promise; - if (__DEV__) { - if (typeof HermesPromise !== 'function') { - console.error('HermesPromise does not exist'); - } - global.HermesInternal?.enablePromiseRejectionTracker?.( - require('../promiseRejectionTrackingOptions').default, - ); +if (__DEV__) { + if (typeof HermesPromise !== 'function') { + console.error('HermesPromise does not exist'); } -} else { - polyfillGlobal('Promise', () => require('../Promise').default); + global.HermesInternal?.enablePromiseRejectionTracker?.( + require('../promiseRejectionTrackingOptions').default, + ); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/image/ImageLoaderModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/image/ImageLoaderModule.kt index aaa6181d46c2..d807dc15f684 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/image/ImageLoaderModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/image/ImageLoaderModule.kt @@ -20,6 +20,7 @@ import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.fbreact.specs.NativeImageLoaderAndroidSpec import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.core.ImagePipeline +import com.facebook.imagepipeline.image.CloseableImage import com.facebook.imagepipeline.image.EncodedImage import com.facebook.imagepipeline.request.ImageRequest import com.facebook.imagepipeline.request.ImageRequestBuilder @@ -93,6 +94,13 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL resolveResourceSize(uriString, promise) return } + // Fast path: data: URIs are not supported by fetchEncodedImage's producer + // sequence (throws IllegalArgumentException). Route them through the decoded + // image pipeline which handles data: URIs via DataFetchProducer. + if ("data" == source.uri.scheme) { + resolveDecodedImageSize(source, promise) + return + } val request: ImageRequest = ImageRequestBuilder.newBuilderWithSource(source.uri) .setRotationOptions(RotationOptions.disableRotation()) @@ -122,6 +130,11 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL resolveResourceSize(uriString, promise) return } + // Fast path: data: URIs are self-contained; headers are not applicable. + if ("data" == source.uri.scheme) { + resolveDecodedImageSize(source, promise) + return + } val imageRequestBuilder: ImageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(source.uri) .setRotationOptions(RotationOptions.disableRotation()) @@ -217,6 +230,59 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL ) } + /** + * Resolve the size of a data: URI (or any URI unsupported by the encoded-image pipeline) + * by decoding the image through Fresco's decoded-image pipeline, which routes through + * DataFetchProducer and supports data: URIs. + */ + private fun resolveDecodedImageSize(source: ImageSource, promise: Promise) { + val request: ImageRequest = ImageRequestBuilder.newBuilderWithSource(source.uri).build() + val dataSource: DataSource> = + this.imagePipeline.fetchDecodedImage(request, this.callerContext) + dataSource.subscribe( + object : BaseDataSubscriber>() { + override fun onNewResultImpl( + dataSource: DataSource> + ) { + if (!dataSource.isFinished) { + return + } + val ref = dataSource.result + if (ref != null) { + try { + val image = ref.get() + val width = image.width + val height = image.height + if (width < 0 || height < 0) { + promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image") + return + } + promise.resolve( + buildReadableMap { + put("width", width) + put("height", height) + }, + ) + } catch (e: Exception) { + promise.reject(ERROR_GET_SIZE_FAILURE, e) + } finally { + CloseableReference.closeSafely(ref) + } + } else { + promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image") + } + } + + override fun onFailureImpl( + dataSource: DataSource> + ) { + promise.reject(ERROR_GET_SIZE_FAILURE, dataSource.failureCause) + } + }, + CallerThreadExecutor.getInstance(), + ) + } + /** * Prefetches the given image to the Fresco image disk cache. *