diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 3ae6487afe4..472186b4b1f 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,11 +1,13 @@ -## NEXT +## 0.3.5+4 +* Fixes a `TypeError` in `availableCameras()` caused by browsers (e.g. Firefox) returning + an invalid `facingMode` capability value instead of the expected `DOMString` sequence. * Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. ## 0.3.5+3 * Fixes camera initialization failure on Safari by fixing a null check operator error using - a nullable getter and null safe practices. + a nullable getter and null-safe practices. ## 0.3.5+2 diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 26fe6a2fd74..f545904d76b 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -512,6 +512,60 @@ void main() { expect(facingMode, equals('environment')); }); + testWidgets('returns null ' + 'when the facing mode setting is empty and ' + 'the first facingMode capability is not a JavaScript string', ( + WidgetTester tester, + ) async { + mockVideoTrack.getSettings = () { + return web.MediaTrackSettings(facingMode: ''); + }.toJS; + mockVideoTrack.getCapabilities = () { + return createJSInteropWrapper(FakeMediaTrackCapabilities()) + as web.MediaTrackCapabilities; + }.toJS; + + when( + jsUtil.hasProperty(videoTrack, 'getCapabilities'.toJS), + ).thenReturn(true); + when( + jsUtil.getProperty(any, 'facingMode'.toJS), + ).thenReturn([true.toJS].toJS); + + final String? facingMode = cameraService.getFacingModeForVideoTrack( + videoTrack, + ); + + expect(facingMode, isNull); + }); + + testWidgets('returns null ' + 'when the facing mode setting is empty and ' + 'the facingMode capability is not a JavaScript array', ( + WidgetTester tester, + ) async { + mockVideoTrack.getSettings = () { + return web.MediaTrackSettings(facingMode: ''); + }.toJS; + mockVideoTrack.getCapabilities = () { + return createJSInteropWrapper(FakeMediaTrackCapabilities()) + as web.MediaTrackCapabilities; + }.toJS; + + when( + jsUtil.hasProperty(videoTrack, 'getCapabilities'.toJS), + ).thenReturn(true); + when( + jsUtil.getProperty(any, 'facingMode'.toJS), + ).thenReturn(true.toJS); + + final String? facingMode = cameraService.getFacingModeForVideoTrack( + videoTrack, + ); + + expect(facingMode, isNull); + }); + testWidgets('returns null ' 'when the facing mode setting ' 'and capabilities are empty', (WidgetTester tester) async { diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index d620b4a43cf..482ea70d888 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -196,20 +196,31 @@ class CameraService { final web.MediaTrackCapabilities videoTrackCapabilities = videoTrack.getCapabilities(); // A list of facing mode capabilities as - // the camera may support multiple facing modes. - final List facingModeCapabilities = - videoTrackCapabilities.facingModeNullable?.toDart - .map((JSString e) => e.toDart) - .toList() ?? - []; - - if (facingModeCapabilities.isNotEmpty) { - final String facingModeCapability = facingModeCapabilities.first; - return facingModeCapability; - } else { - // Return null if there are no facing mode capabilities. + //The camera may support multiple facing modes. +// Some browsers (e.g., Firefox) do not conform to the MediaTrackCapabilities +// spec and may return `facingMode` as a non-array value (e.g., an empty string, +// a plain object, or a boolean) Rather than the expected DOMString sequence. +// We use jsUtil.getProperty to safely read the raw JS value, then explicitly +// validate it is a JSArray before accessing its elements to prevent a TypeError. + +final JSAny? facingModeCapabilities = jsUtil.getProperty( + videoTrackCapabilities, + 'facingMode'.toJS, +); +if (facingModeCapabilities == null || + !facingModeCapabilities.isA()) { return null; } + + final List facingModes = + (facingModeCapabilities as JSArray).toDart; + + if (facingModes.isNotEmpty && facingModes.first.isA()) { + return (facingModes.first! as JSString).toDart; + } + + // Return null if there are no facing mode capabilities. + return null; } return facingMode; diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index bba4fae6d14..084f2caf8cf 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.3.5+3 +version: 0.3.5+4 environment: sdk: ^3.10.0