Skip to content

Commit

Permalink
capture_mode: Support DLP warning mode when performing capture
Browse files Browse the repository at this point in the history
DLP should be checked again at the time when the capture
operation is attempted. This is because screen content
may change between the time we checked at initialization,
and the actual time we attempt capture.

This also applies on the 3-second countdown animation we
perform when we initiate video recording, since screen
content may also change.

Demos:
- https://bugs.chromium.org/p/chromium/issues/detail?id=1253550#c12
- https://bugs.chromium.org/p/chromium/issues/detail?id=1253550#c13

BUG=1253550
TEST=Manually, Added new browser tests.

Change-Id: Ia07012e252dac25014d830f477d9504d34f6539e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3291521
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Reviewed-by: Sergey Poromov <poromov@chromium.org>
Reviewed-by: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/main@{#946188}
  • Loading branch information
Ahmed Fakhry authored and Chromium LUCI CQ committed Nov 29, 2021
1 parent c025f54 commit dcadca7
Show file tree
Hide file tree
Showing 13 changed files with 474 additions and 114 deletions.
198 changes: 127 additions & 71 deletions ash/capture_mode/capture_mode_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -497,15 +497,15 @@ void CaptureModeController::EnableAudioRecording(bool enable_audio_recording) {
}

void CaptureModeController::Start(CaptureModeEntryType entry_type) {
if (capture_mode_session_ || pending_dlp_check_on_session_init_)
if (capture_mode_session_ || pending_dlp_check_)
return;

if (!delegate_->IsCaptureAllowedByPolicy()) {
ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
return;
}

pending_dlp_check_on_session_init_ = true;
pending_dlp_check_ = true;
delegate_->CheckCaptureModeInitRestrictionByDlp(base::BindOnce(
&CaptureModeController::OnDlpRestrictionCheckedAtSessionInit,
weak_ptr_factory_.GetWeakPtr(), entry_type));
Expand Down Expand Up @@ -614,15 +614,15 @@ CaptureModeController::GetCurrentCaptureFolder() const {
}

void CaptureModeController::CaptureScreenshotsOfAllDisplays() {
if (pending_dlp_check_on_session_init_)
if (pending_dlp_check_)
return;

if (!delegate_->IsCaptureAllowedByPolicy()) {
ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
return;
}

pending_dlp_check_on_session_init_ = true;
pending_dlp_check_ = true;
delegate_->CheckCaptureModeInitRestrictionByDlp(base::BindOnce(
&CaptureModeController::
OnDlpRestrictionCheckedAtCaptureScreenshotsOfAllDisplays,
Expand All @@ -631,30 +631,22 @@ void CaptureModeController::CaptureScreenshotsOfAllDisplays() {

void CaptureModeController::PerformCapture() {
DCHECK(IsActive());
const absl::optional<CaptureParams> capture_params = GetCaptureParams();
if (!capture_params)
return;

const CaptureAllowance allowance =
IsCaptureAllowedByEnterprisePolicies(*capture_params);
if (allowance != CaptureAllowance::kAllowed) {
ShowDisabledNotification(allowance);
Stop();
if (pending_dlp_check_)
return;
}

if (type_ == CaptureModeType::kImage) {
CaptureImage(*capture_params, BuildImagePath());
} else {
// HDCP affects only video recording.
if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
ShowDisabledNotification(CaptureAllowance::kDisallowedByHdcp);
Stop();
return;
}
const absl::optional<CaptureParams> capture_params = GetCaptureParams();
if (!capture_params)
return;

CaptureVideo(*capture_params);
}
DCHECK(!pending_dlp_check_);
pending_dlp_check_ = true;
capture_mode_session_->OnWaitingForDlpConfirmationStarted();
delegate_->CheckCaptureOperationRestrictionByDlp(
capture_params->window, capture_params->bounds,
base::BindOnce(
&CaptureModeController::OnDlpRestrictionCheckedAtPerformingCapture,
weak_ptr_factory_.GetWeakPtr()));
}

void CaptureModeController::EndVideoRecording(EndRecordingReason reason) {
Expand Down Expand Up @@ -1298,51 +1290,16 @@ void CaptureModeController::OnVideoRecordCountDownFinished() {
return;
}

// During the 3-second count down, screen content might have changed such that
// admin-restricted or HDCP content became present. We must check again.
const CaptureAllowance allowance =
IsCaptureAllowedByEnterprisePolicies(*capture_params);
if (allowance != CaptureAllowance::kAllowed) {
Stop();
ShowDisabledNotification(allowance);
return;
}

if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
Stop();
ShowDisabledNotification(CaptureAllowance::kDisallowedByHdcp);
return;
}

// In Projector mode, the creation of the DriveFS folder that will host the
// video is asynchronous. We don't want the user to be able to bail out of the
// session at this point, since we don't want to create that folder in vain.
capture_mode_session_->set_can_exit_on_escape(false);

if (capture_mode_session_->is_in_projector_mode()) {
ProjectorControllerImpl::Get()->CreateScreencastContainerFolder(
base::BindOnce(
&CaptureModeController::OnProjectorContainerFolderCreated,
weak_ptr_factory_.GetWeakPtr(), *capture_params));
return;
}
const base::FilePath current_path = BuildVideoPath();

// If the current capture folder is not the default `Downloads` folder, we
// need to validate the current folder first before starting the video
// recording.
if (!GetCurrentCaptureFolder().is_default_downloads_folder) {
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&SelectFilePathForCapturedFile, current_path,
GetFallbackFilePathFromFile(current_path)),
base::BindOnce(&CaptureModeController::BeginVideoRecording,
weak_ptr_factory_.GetWeakPtr(), *capture_params,
/*for_projector=*/false));
return;
}

BeginVideoRecording(*capture_params, /*for_projector=*/false, current_path);
// During the 3-second count down, screen content might have changed. We must
// check again the DLP restrictions.
DCHECK(!pending_dlp_check_);
pending_dlp_check_ = true;
capture_mode_session_->OnWaitingForDlpConfirmationStarted();
delegate_->CheckCaptureOperationRestrictionByDlp(
capture_params->window, capture_params->bounds,
base::BindOnce(
&CaptureModeController::OnDlpRestrictionCheckedAtCountDownFinished,
weak_ptr_factory_.GetWeakPtr()));
}

void CaptureModeController::OnProjectorContainerFolderCreated(
Expand Down Expand Up @@ -1420,10 +1377,109 @@ void CaptureModeController::InterruptVideoRecording() {
EndVideoRecording(EndRecordingReason::kDlpInterruption);
}

void CaptureModeController::OnDlpRestrictionCheckedAtPerformingCapture(
bool proceed) {
DCHECK(IsActive());

pending_dlp_check_ = false;
capture_mode_session_->OnWaitingForDlpConfirmationEnded(proceed);

if (!proceed) {
Stop();
return;
}

const absl::optional<CaptureParams> capture_params = GetCaptureParams();
DCHECK(capture_params);

if (!delegate_->IsCaptureAllowedByPolicy()) {
ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
Stop();
return;
}

if (type_ == CaptureModeType::kImage) {
CaptureImage(*capture_params, BuildImagePath());
} else {
// HDCP affects only video recording.
if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
ShowDisabledNotification(CaptureAllowance::kDisallowedByHdcp);
Stop();
return;
}

CaptureVideo(*capture_params);
}
}

void CaptureModeController::OnDlpRestrictionCheckedAtCountDownFinished(
bool proceed) {
DCHECK(IsActive());

pending_dlp_check_ = false;
capture_mode_session_->OnWaitingForDlpConfirmationEnded(proceed);

if (!proceed) {
Stop();
return;
}

const absl::optional<CaptureParams> capture_params = GetCaptureParams();
if (!capture_params) {
Stop();
return;
}

// Now that we're done with DLP restrictions checks, we can perform the policy
// and HDCP checks, which may have changed during the 3-second count down and
// during the time the DLP warning dialog was shown.
if (!delegate_->IsCaptureAllowedByPolicy()) {
ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
Stop();
return;
}

if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
Stop();
ShowDisabledNotification(CaptureAllowance::kDisallowedByHdcp);
return;
}

// In Projector mode, the creation of the DriveFS folder that will host the
// video is asynchronous. We don't want the user to be able to bail out of the
// session at this point, since we don't want to create that folder in vain.
capture_mode_session_->set_can_exit_on_escape(false);

if (capture_mode_session_->is_in_projector_mode()) {
ProjectorControllerImpl::Get()->CreateScreencastContainerFolder(
base::BindOnce(
&CaptureModeController::OnProjectorContainerFolderCreated,
weak_ptr_factory_.GetWeakPtr(), *capture_params));
return;
}
const base::FilePath current_path = BuildVideoPath();

// If the current capture folder is not the default `Downloads` folder, we
// need to validate the current folder first before starting the video
// recording.
if (!GetCurrentCaptureFolder().is_default_downloads_folder) {
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&SelectFilePathForCapturedFile, current_path,
GetFallbackFilePathFromFile(current_path)),
base::BindOnce(&CaptureModeController::BeginVideoRecording,
weak_ptr_factory_.GetWeakPtr(), *capture_params,
/*for_projector=*/false));
return;
}

BeginVideoRecording(*capture_params, /*for_projector=*/false, current_path);
}

void CaptureModeController::OnDlpRestrictionCheckedAtSessionInit(
CaptureModeEntryType entry_type,
bool proceed) {
pending_dlp_check_on_session_init_ = false;
pending_dlp_check_ = false;

if (!proceed)
return;
Expand Down Expand Up @@ -1496,7 +1552,7 @@ void CaptureModeController::OnDlpRestrictionCheckedAtVideoEnd(

void CaptureModeController::
OnDlpRestrictionCheckedAtCaptureScreenshotsOfAllDisplays(bool proceed) {
pending_dlp_check_on_session_init_ = false;
pending_dlp_check_ = false;
if (!proceed)
return;

Expand Down
25 changes: 22 additions & 3 deletions ash/capture_mode/capture_mode_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ using OnFileDeletedCallback =
base::OnceCallback<void(const base::FilePath& path,
bool delete_successful)>;

// Controls starting and ending a Capture Mode session and its behavior.
// Controls starting and ending a Capture Mode session and its behavior. There
// are various checks that are run when a capture session start is attempted,
// and when a capture operation is performed, to make sure they're allowed. For
// example, checking that policy allows screen capture, and there are no content
// on the screen restricted by DLP (Data Leak Prevention). In the case of video
// recording, HDCP is also checked to ensure no protected content is being
// recorded.
class ASH_EXPORT CaptureModeController
: public recording::mojom::RecordingServiceClient,
public SessionObserver,
Expand Down Expand Up @@ -387,6 +393,18 @@ class ASH_EXPORT CaptureModeController
// allowed to be captured.
void InterruptVideoRecording();

// Called by the DLP manager when it's checked for any on-screen content
// restriction at the time when the capture operation is attempted. `proceed`
// will be set to true if the capture operation should continue, false if it
// should be aborted.
void OnDlpRestrictionCheckedAtPerformingCapture(bool proceed);

// Called by the DLP manager when it's checked again for any on-screen content
// restriction at the time when the video capture 3-second countdown ends.
// `proceed` will be set to true if video recording should begin, or false if
// it should be aborted.
void OnDlpRestrictionCheckedAtCountDownFinished(bool proceed);

// Bound to a callback that will be called by the DLP manager to let us know
// whether a pending session initialization should `proceed` or abort due to
// some restricted contents on the screen.
Expand Down Expand Up @@ -453,8 +471,9 @@ class ASH_EXPORT CaptureModeController
bool low_disk_space_threshold_reached_ = false;

// Set to true when we're waiting for a callback from the DLP manager to check
// content restrictions that may block the session initialization.
bool pending_dlp_check_on_session_init_ = false;
// content restrictions that may block capture mode at any of its stages
// (initialization or performing the capture).
bool pending_dlp_check_ = false;

// Watches events that lead to ending video recording.
std::unique_ptr<VideoRecordingWatcher> video_recording_watcher_;
Expand Down
Loading

0 comments on commit dcadca7

Please sign in to comment.