Mac端Safari 15.5中navigator.permissions为undefined的相机权限问题
Safari 15.5 摄像头权限检测兼容方案
问题根因
Mac端Safari 15.5存在原生实现缺陷:该版本的Permissions API未完整支持摄像头权限查询能力,未触发过媒体设备申请时navigator.permissions会直接返回undefined;即使用户已经完成摄像头授权,也无法通过permissions.query({ name: 'camera' })获取正确状态。该bug仅存在于桌面端Safari 15.x系列版本,移动端同版本Safari、更高/更低版本的桌面Safari均无此问题。
实现逻辑
不要单独依赖Permissions API做摄像头权限判断,结合navigator.mediaDevices.getUserMedia做降级探测是该场景下唯一可靠的方案:
- 对非Safari 15桌面端的环境,优先使用原有Permissions API查询逻辑,性能更好也不会触发权限弹窗
- 对命中问题版本、或Permissions API查询失败/不存在的场景,用
getUserMedia做轻量探测,根据调用结果映射权限状态 - 探测完成后立刻关闭拿到的媒体流轨道,避免长期占用摄像头硬件
兼容代码
// 判断是否为存在缺陷的Mac端Safari 15版本 const isMacSafari15 = () => { const ua = navigator.userAgent; const isMac = ua.includes('Mac OS X'); const isSafari = ua.includes('Safari') && !ua.includes('Chrome') && !ua.includes('Firefox'); const versionMatch = ua.match(/Version\/(\d+)\.(\d+)/); if (!isMac || !isSafari || !versionMatch) return false; const majorVersion = parseInt(versionMatch[1], 10); return majorVersion === 15; }; // 降级探测方法:通过getUserMedia判断权限状态 const detectCameraPermissionByGetUserMedia = (resolve: (state: CameraPermissionState) => void) => { if (!navigator.mediaDevices?.getUserMedia) { resolve(CameraPermissionState.UNKNOWN); return; } navigator.mediaDevices.getUserMedia({ video: true }) .then((stream) => { // 成功拿到流说明已授权,立刻释放摄像头占用 stream.getTracks().forEach(track => track.stop()); resolve(CameraPermissionState.GRANTED); }) .catch((err) => { // 根据错误类型映射权限状态 if (err?.name === 'NotAllowedError' || err?.name === 'PermissionDeniedError') { resolve(CameraPermissionState.DENIED); } else if (err?.name === 'NotFoundError') { // 设备未搭载摄像头,返回未知状态 resolve(CameraPermissionState.UNKNOWN); } else { resolve(CameraPermissionState.PROMPT); } }); }; public getCameraPermission = (): Promise<CameraPermissionState> => new Promise((resolve) => { // 非问题版本优先走原有Permissions API逻辑 if (!isMacSafari15() && (navigator as any).permissions) { (navigator as any).permissions.query({ name: 'camera' }) .then(({ state }: { state: CameraPermissionState }) => resolve(state)) .catch(() => { // API查询失败走降级探测 detectCameraPermissionByGetUserMedia(resolve); }); return; } // 问题版本或API不可用时走降级探测 detectCameraPermissionByGetUserMedia(resolve); });
注意事项
- 降级探测的
getUserMedia调用必须放在用户点击、触摸等主动交互事件的执行链路里,否则Safari会直接拦截请求,既不会弹出权限提示,也会返回错误的权限结果 - 如果用户已经完成授权,
getUserMedia调用不会重复弹出权限提示,用户无感知,不会影响使用体验 - 不要尝试通过本地缓存记录权限状态,用户随时可能在浏览器设置里修改站点权限,缓存结果不可靠
- 如果不想写UA判断逻辑,也可以全量使用
getUserMedia探测逻辑,该逻辑在所有支持摄像头调用的现代浏览器上都能正常运行,不会和原有Permissions API逻辑冲突
内容的提问来源于stack exchange,提问作者Ahmet B. Simsek
相关产品推荐
相关产品推荐

