blob: 9dd495705d7fa938a6d2a0cc0e715ff8c73f54aa [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/permissions/system/system_media_capture_permissions_mac.h"
#import <AVFoundation/AVFoundation.h>
#import <Cocoa/Cocoa.h>
#include "base/apple/foundation_util.h"
#include "base/apple/scoped_cftyperef.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/no_destructor.h"
#import "base/task/sequenced_task_runner.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/permissions/system/media_authorization_wrapper_mac.h"
#include "media/base/media_switches.h"
#include "ui/base/cocoa/permissions_utils.h"
namespace system_permission_settings {
namespace {
std::optional<bool> g_is_screen_capture_allowed_for_testing = std::nullopt;
bool UsingFakeMediaDevices() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kUseFakeDeviceForMediaStream);
}
// Pointer to OS call wrapper that tests can set.
MediaAuthorizationWrapper* g_media_authorization_wrapper_for_tests = nullptr;
// Implementation of OS call wrapper that does the actual OS calls.
class MediaAuthorizationWrapperImpl final : public MediaAuthorizationWrapper {
public:
MediaAuthorizationWrapperImpl() = default;
MediaAuthorizationWrapperImpl(const MediaAuthorizationWrapperImpl&) = delete;
MediaAuthorizationWrapperImpl& operator=(
const MediaAuthorizationWrapperImpl&) = delete;
~MediaAuthorizationWrapperImpl() override = default;
AVAuthorizationStatus AuthorizationStatusForMediaType(
AVMediaType media_type) override {
return [AVCaptureDevice authorizationStatusForMediaType:media_type];
}
void RequestAccessForMediaType(AVMediaType media_type,
base::OnceClosure callback) override {
__block base::OnceClosure block_callback = std::move(callback);
__block scoped_refptr<base::SequencedTaskRunner> requesting_thread =
base::SequencedTaskRunner::GetCurrentDefault();
[AVCaptureDevice requestAccessForMediaType:media_type
completionHandler:^(BOOL granted) {
requesting_thread->PostTask(
FROM_HERE, std::move(block_callback));
}];
}
};
MediaAuthorizationWrapper& GetMediaAuthorizationWrapper() {
if (g_media_authorization_wrapper_for_tests) {
return *g_media_authorization_wrapper_for_tests;
}
static base::NoDestructor<MediaAuthorizationWrapperImpl>
media_authorization_wrapper;
return *media_authorization_wrapper;
}
AVAuthorizationStatus MediaAuthorizationStatus(AVMediaType media_type) {
return GetMediaAuthorizationWrapper().AuthorizationStatusForMediaType(
media_type);
}
SystemPermission CheckSystemMediaCapturePermission(AVMediaType media_type) {
if (UsingFakeMediaDevices()) {
return SystemPermission::kAllowed;
}
AVAuthorizationStatus auth_status = MediaAuthorizationStatus(media_type);
switch (auth_status) {
case AVAuthorizationStatusNotDetermined:
return SystemPermission::kNotDetermined;
case AVAuthorizationStatusRestricted:
return SystemPermission::kRestricted;
case AVAuthorizationStatusDenied:
return SystemPermission::kDenied;
case AVAuthorizationStatusAuthorized:
return SystemPermission::kAllowed;
default:
NOTREACHED();
}
}
void RequestSystemMediaCapturePermission(AVMediaType media_type,
base::OnceClosure callback) {
if (UsingFakeMediaDevices()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
GetMediaAuthorizationWrapper().RequestAccessForMediaType(media_type,
std::move(callback));
}
bool IsScreenCaptureAllowed() {
if (g_is_screen_capture_allowed_for_testing) {
return g_is_screen_capture_allowed_for_testing.value();
}
return ui::IsScreenCaptureAllowed();
}
} // namespace
SystemPermission CheckSystemAudioCapturePermission() {
return CheckSystemMediaCapturePermission(AVMediaTypeAudio);
}
SystemPermission CheckSystemVideoCapturePermission() {
return CheckSystemMediaCapturePermission(AVMediaTypeVideo);
}
SystemPermission CheckSystemScreenCapturePermission() {
return IsScreenCaptureAllowed() ? SystemPermission::kAllowed
: SystemPermission::kDenied;
}
SystemPermission CheckSystemClipboardPermission() {
// Check macOS system privacy settings for programmatic clipboard access using
// the accessBehavior property available in macOS 15.4+. These settings only
// affect programmatic access - direct user actions like ⌘V always work.
if (@available(macOS 15.4, *)) {
NSPasteboardAccessBehavior access_behavior =
[NSPasteboard generalPasteboard].accessBehavior;
switch (access_behavior) {
case NSPasteboardAccessBehaviorAlwaysAllow:
return SystemPermission::kAllowed;
case NSPasteboardAccessBehaviorAlwaysDeny:
return SystemPermission::kDenied;
case NSPasteboardAccessBehaviorAsk:
return SystemPermission::kNotDetermined;
case NSPasteboardAccessBehaviorDefault:
// Default behavior for the General pasteboard is to ask upon
// programmatic access
return SystemPermission::kNotDetermined;
}
} else {
// The behavior of older macOS versions is effectively kAllowed.
return SystemPermission::kAllowed;
}
}
void RequestSystemAudioCapturePermission(base::OnceClosure callback) {
RequestSystemMediaCapturePermission(AVMediaTypeAudio, std::move(callback));
}
void RequestSystemVideoCapturePermission(base::OnceClosure callback) {
RequestSystemMediaCapturePermission(AVMediaTypeVideo, std::move(callback));
}
void SetMediaAuthorizationWrapperForTesting(
MediaAuthorizationWrapper* wrapper) {
CHECK(!g_media_authorization_wrapper_for_tests);
g_media_authorization_wrapper_for_tests = wrapper;
}
void SetIsScreenCaptureAllowedForTesting(bool is_screen_capture_allowed) {
g_is_screen_capture_allowed_for_testing = is_screen_capture_allowed;
}
} // namespace system_permission_settings