Skip to content

[camera] Fix web facingMode capability handling#11728

Open
mackings wants to merge 7 commits into
flutter:mainfrom
mackings:fix-camera-web-available-cameras-firefox
Open

[camera] Fix web facingMode capability handling#11728
mackings wants to merge 7 commits into
flutter:mainfrom
mackings:fix-camera-web-available-cameras-firefox

Conversation

@mackings

@mackings mackings commented May 18, 2026

Copy link
Copy Markdown

[camera_web] Fix facingMode capability handling

Fixes flutter/flutter#174985

Prevents availableCameras() from throwing a TypeError when a browser returns an invalid facingMode capability. The plugin now treats invalid or missing facing mode capabilities as unavailable, allowing availableCameras() to continue and fall back to CameraLensDirection.external.

Tests:

  • dart format packages/camera/camera_web/lib/src/camera_service.dart packages/camera/camera_web/example/integration_test/camera_service_test.dart
  • flutter analyze
  • flutter test
  • flutter drive --release --driver=test_driver/integration_test.dart --target=integration_test/camera_service_test.dart -d chrome
  • flutter drive --release --driver=test_driver/integration_test.dart --target=integration_test/camera_web_test.dart -d chrome

Pre-Review Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran [the auto-formatter].
  • I signed the [CLA].
  • I [linked to at least one issue that this PR fixes] in the description above.
  • I followed [the version and CHANGELOG instructions], using [semantic versioning] and the [repository CHANGELOG style], or I have commented below to indicate which documented exception this PR falls under[^1].
  • I updated/added any relevant documentation (doc comments with ///).
  • Added packages/camera/camera_web/pubspec.yaml version bump: 0.3.5+3 -> 0.3.5+4
  • Changed CHANGELOG.md from ## NEXT to ## 0.3.5+4
  • All existing and new tests are passing.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request fixes a TypeError in the camera_web package by validating that the facingMode capability is a valid JavaScript array before processing. The changes include logic in CameraService to handle invalid capabilities and a new integration test. Review feedback notes that the current runtime check does not verify the types of the array's elements and suggests a more efficient approach to validate and retrieve the first element safely.

Comment on lines 212 to 221
final List<String> facingModes =
(facingModeCapabilities as JSArray<JSString>).toDart
.map((JSString e) => e.toDart)
.toList() ??
<String>[];
.toList();

if (facingModeCapabilities.isNotEmpty) {
final String facingModeCapability = facingModeCapabilities.first;
if (facingModes.isNotEmpty) {
final String facingModeCapability = facingModes.first;
return facingModeCapability;
} else {
// Return null if there are no facing mode capabilities.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current implementation converts the entire array to a Dart list and maps every element to a string, which is inefficient since only the first element is needed. More importantly, isA<JSArray<JSString>>() only verifies that the object is a JS array at runtime; it does not check the types of the elements. If the browser returns an array containing non-string values (e.g., [true]), calling e.toDart will still result in a TypeError.

Consider a more robust and efficient approach that only checks the first element's type before converting it.

      final List<JSAny?> facingModes = (facingModeCapabilities as JSArray).toDart;
      if (facingModes.isNotEmpty && facingModes.first.isA<JSString>()) {
        return (facingModes.first as JSString).toDart;
      } else {
        // Return null if there are no facing mode capabilities.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Updated to validate the first facingMode capability element before converting it, and added a regression test for a non-string array entry. The focused web integration test passes.

@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

Thanks for the contribution! You’ve checked boxes in the PR checklist above that are not reflected in this PR, so I’m assuming this is a work in progress and am marking it as a Draft. Please review the checklist, updating the PR as appropriate, and when the state of the PR as posted reflects the checklist please feel free to mark it as ready for review.

Please also check that the contents of the PR are all actually relevant to the change you are attempting to make.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request addresses a TypeError in the web implementation of availableCameras by improving the validation of the facingMode capability. The changes include safe property access using jsUtil and explicit type checks to ensure facingMode is a JSArray containing a JSString. Corresponding integration tests have been added to verify these scenarios. Feedback was provided to remove an unnecessary else block in camera_service.dart to improve code readability and adhere to Dart style conventions.

Comment on lines 215 to 219
if (facingModes.isNotEmpty && facingModes.first.isA<JSString>()) {
return (facingModes.first! as JSString).toDart;
} else {
// Return null if there are no facing mode capabilities.
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This else block is unnecessary. To improve readability and reduce nesting, it's better to remove the else and have a single return null; after the if block.

      if (facingModes.isNotEmpty && facingModes.first.isA<JSString>()) {
        return (facingModes.first! as JSString).toDart;
      }
      // Return null if there are no facing mode capabilities.
      return null;
References
  1. The repository style guide (lines 10-12) requires following language-specific style guides. For Dart, this change aligns with the Effective Dart style guide, which recommends avoiding else after an if statement that contains a return. (link)

@stuartmorgan-g stuartmorgan-g changed the title Fix camera_web facingMode capability handling [camera] Fix web facingMode capability handling May 19, 2026
@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

[x] I followed [the version and CHANGELOG instructions], using [semantic versioning] and the [repository CHANGELOG style], or I have commented below to indicate which documented exception this PR falls under[^1].

As with #11727, this step needs to be completed.

@mackings

Copy link
Copy Markdown
Author

[x] I followed [the version and CHANGELOG instructions], using [semantic versioning] and the [repository CHANGELOG style], or I have commented below to indicate which documented exception this PR falls under[^1].

As with #11727, this step needs to be completed.

Yes Updated.

@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

As with the other PR: Could you explain why you believe this PR does not need to follow our documented process?

@mackings

Copy link
Copy Markdown
Author

As with the other PR: Could you explain why you believe this PR does not need to follow our documented process?

You’re right, I misunderstood that checklist item earlier.

I’ve updated the PR to follow the version and CHANGELOG process now:

  • bumped the package version in pubspec.yaml.
  • updated the package CHANGELOG.md with the release heading and entry
  • restored the checklist item in the PR description

@stuartmorgan-g stuartmorgan-g added the triage-web Should be looked at in web triage label May 20, 2026
@stuartmorgan-g stuartmorgan-g requested a review from mdebbar May 20, 2026 10:57
## NEXT
## 0.3.5+4

* 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
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.

Comment on lines +203 to +208
final JSAny? facingModeCapabilities = jsUtil.getProperty(
videoTrackCapabilities,
'facingMode'.toJS,
);
if (facingModeCapabilities == null ||
!facingModeCapabilities.isA<JSArray>()) {

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.

Could you add a comment explaining why it's necessary to do all of this type-checking? Is it because facingMode is sometimes an object and not an array?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, the type-checking is necessary because some browsers (notably Firefox) don't conform to the MediaTrackCapabilities spec and return facingMode as a non-array value (e.g. an empty string or a plain object) instead of the expected DOMString sequence. The original code cast it directly to JSArray without validation, which caused a TypeError at runtime when availableCameras() was called on those browsers.
I have added a comment to the code to explain this.

@mdebbar mdebbar added the CICD Run CI/CD label Jun 1, 2026
Enhance comments for facing mode capabilities handling in camera service.
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 1, 2026

@mackings mackings left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

mackings added 2 commits June 1, 2026 22:09
@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

@bparrishMines for secondary review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p: camera platform-web triage-web Should be looked at in web triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[camera_web] calling availableCameras causes a TypeError in JS

3 participants