Skip to content
6 changes: 4 additions & 2 deletions packages/camera/camera_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above this line, there should be a new line explaining what this change is doing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have made an update to it.


## 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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,46 @@ 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(<JSAny>[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 {
Expand Down
33 changes: 21 additions & 12 deletions packages/camera/camera_web/lib/src/camera_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,20 +196,29 @@ class CameraService {
final web.MediaTrackCapabilities videoTrackCapabilities = videoTrack.getCapabilities();

// A list of facing mode capabilities as
// the camera may support multiple facing modes.
final List<String> facingModeCapabilities =
videoTrackCapabilities.facingModeNullable?.toDart
.map((JSString e) => e.toDart)
.toList() ??
<String>[];

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<JSArray>()) {
return null;
}

final List<JSAny?> facingModes = (facingModeCapabilities as JSArray).toDart;

if (facingModes.isNotEmpty && facingModes.first.isA<JSString>()) {
return (facingModes.first! as JSString).toDart;
}

// Return null if there are no facing mode capabilities.
return null;
}

return facingMode;
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_web/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading