Add browser camera permission support and metadata (#760) (#913)

This commit is contained in:
Austin Wang 2026-03-04 18:56:22 -08:00 committed by GitHub
parent 0a490b0e03
commit 80baae355a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 50 additions and 3 deletions

View file

@ -28,6 +28,8 @@
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string>A program running within cmux would like to use your microphone.</string>
<key>NSCameraUsageDescription</key>
<string>A program running within cmux would like to use your camera.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSServices</key>

View file

@ -2,6 +2,23 @@
"sourceLanguage": "en",
"version": "1.0",
"strings": {
"NSCameraUsageDescription": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "A program running within cmux would like to use your camera."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "cmux 内で実行中のプログラムがカメラの使用を求めています。"
}
}
}
},
"NSMicrophoneUsageDescription": {
"extractionState": "manual",
"localizations": {

View file

@ -1533,6 +1533,7 @@ final class BrowserPanel: Panel, ObservableObject {
private static func makeWebView() -> CmuxWebView {
let config = WKWebViewConfiguration()
config.processPool = BrowserPanel.sharedProcessPool
config.mediaTypesRequiringUserActionForPlayback = []
// Ensure browser cookies/storage persist across navigations and launches.
// This reduces repeated consent/bot-challenge flows on sites like Google.
config.websiteDataStore = .default()
@ -3652,6 +3653,16 @@ private class BrowserUIDelegate: NSObject, WKUIDelegate {
}
}
func webView(
_ webView: WKWebView,
requestMediaCapturePermissionFor origin: WKSecurityOrigin,
initiatedByFrame frame: WKFrameInfo,
type: WKMediaCaptureType,
decisionHandler: @escaping (WKPermissionDecision) -> Void
) {
decisionHandler(.prompt)
}
func webView(
_ webView: WKWebView,
runJavaScriptAlertPanelWithMessage message: String,

View file

@ -8,6 +8,8 @@
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.automation.apple-events</key>

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Regression test: cmux advertises and allows microphone access."""
"""Regression test: cmux advertises media-capture access metadata."""
from __future__ import annotations
@ -36,6 +36,7 @@ def main() -> int:
entitlements = load_plist(repo_root / "cmux.entitlements", failures)
mic_usage = info.get("NSMicrophoneUsageDescription")
camera_usage = info.get("NSCameraUsageDescription")
if not isinstance(mic_usage, str) or not mic_usage.strip():
failures.append(
"Resources/Info.plist must define a non-empty NSMicrophoneUsageDescription"
@ -50,13 +51,27 @@ def main() -> int:
"cmux.entitlements must set com.apple.security.device.audio-input to true"
)
if not isinstance(camera_usage, str) or not camera_usage.strip():
failures.append(
"Resources/Info.plist must define a non-empty NSCameraUsageDescription"
)
elif camera_usage.strip() != "A program running within cmux would like to use your camera.":
failures.append(
"Resources/Info.plist NSCameraUsageDescription should match the Ghostty-style wording"
)
if entitlements.get("com.apple.security.device.camera") is not True:
failures.append(
"cmux.entitlements must set com.apple.security.device.camera to true"
)
if failures:
print("FAIL: microphone access metadata regression(s) detected")
print("FAIL: media-capture metadata regression(s) detected")
for failure in failures:
print(f"- {failure}")
return 1
print("PASS: microphone usage description and entitlement are present")
print("PASS: microphone/camera usage descriptions and entitlements are present")
return 0