Skip to content

Commit cef3246

Browse files
committed
fix(Image): support data: URIs in getSize/getSizeWithHeaders on Android
After #56736 changed getSize() from fetchDecodedImage to fetchEncodedImage, data: URIs started throwing IllegalArgumentException because Fresco's encoded-image producer sequence does not support the 'data' URI scheme. Add a fast path (similar to the res:// fast path from #56944) that routes data: URIs through fetchDecodedImage, which supports data: URIs via DataFetchProducer. This restores the behavior from 0.84.x where Image.getSize() worked correctly with base64-encoded data: URIs. Fixes #57787
1 parent dce5283 commit cef3246

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

  • packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/image

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/image/ImageLoaderModule.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.facebook.drawee.backends.pipeline.Fresco
2020
import com.facebook.fbreact.specs.NativeImageLoaderAndroidSpec
2121
import com.facebook.imagepipeline.common.RotationOptions
2222
import com.facebook.imagepipeline.core.ImagePipeline
23+
import com.facebook.imagepipeline.image.CloseableImage
2324
import com.facebook.imagepipeline.image.EncodedImage
2425
import com.facebook.imagepipeline.request.ImageRequest
2526
import com.facebook.imagepipeline.request.ImageRequestBuilder
@@ -93,6 +94,13 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
9394
resolveResourceSize(uriString, promise)
9495
return
9596
}
97+
// Fast path: data: URIs are not supported by fetchEncodedImage's producer
98+
// sequence (throws IllegalArgumentException). Route them through the decoded
99+
// image pipeline which handles data: URIs via DataFetchProducer.
100+
if ("data" == source.uri.scheme) {
101+
resolveDecodedImageSize(source, promise)
102+
return
103+
}
96104
val request: ImageRequest =
97105
ImageRequestBuilder.newBuilderWithSource(source.uri)
98106
.setRotationOptions(RotationOptions.disableRotation())
@@ -122,6 +130,11 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
122130
resolveResourceSize(uriString, promise)
123131
return
124132
}
133+
// Fast path: data: URIs are self-contained; headers are not applicable.
134+
if ("data" == source.uri.scheme) {
135+
resolveDecodedImageSize(source, promise)
136+
return
137+
}
125138
val imageRequestBuilder: ImageRequestBuilder =
126139
ImageRequestBuilder.newBuilderWithSource(source.uri)
127140
.setRotationOptions(RotationOptions.disableRotation())
@@ -217,6 +230,59 @@ internal class ImageLoaderModule : NativeImageLoaderAndroidSpec, LifecycleEventL
217230
)
218231
}
219232

233+
/**
234+
* Resolve the size of a data: URI (or any URI unsupported by the encoded-image pipeline)
235+
* by decoding the image through Fresco's decoded-image pipeline, which routes through
236+
* DataFetchProducer and supports data: URIs.
237+
*/
238+
private fun resolveDecodedImageSize(source: ImageSource, promise: Promise) {
239+
val request: ImageRequest = ImageRequestBuilder.newBuilderWithSource(source.uri).build()
240+
val dataSource: DataSource<CloseableReference<CloseableImage>> =
241+
this.imagePipeline.fetchDecodedImage(request, this.callerContext)
242+
dataSource.subscribe(
243+
object : BaseDataSubscriber<CloseableReference<CloseableImage>>() {
244+
override fun onNewResultImpl(
245+
dataSource: DataSource<CloseableReference<CloseableImage>>
246+
) {
247+
if (!dataSource.isFinished) {
248+
return
249+
}
250+
val ref = dataSource.result
251+
if (ref != null) {
252+
try {
253+
val image = ref.get()
254+
val width = image.width
255+
val height = image.height
256+
if (width < 0 || height < 0) {
257+
promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image")
258+
return
259+
}
260+
promise.resolve(
261+
buildReadableMap {
262+
put("width", width)
263+
put("height", height)
264+
},
265+
)
266+
} catch (e: Exception) {
267+
promise.reject(ERROR_GET_SIZE_FAILURE, e)
268+
} finally {
269+
CloseableReference.closeSafely(ref)
270+
}
271+
} else {
272+
promise.reject(ERROR_GET_SIZE_FAILURE, "Failed to get the size of the image")
273+
}
274+
}
275+
276+
override fun onFailureImpl(
277+
dataSource: DataSource<CloseableReference<CloseableImage>>
278+
) {
279+
promise.reject(ERROR_GET_SIZE_FAILURE, dataSource.failureCause)
280+
}
281+
},
282+
CallerThreadExecutor.getInstance(),
283+
)
284+
}
285+
220286
/**
221287
* Prefetches the given image to the Fresco image disk cache.
222288
*

0 commit comments

Comments
 (0)