Commit 95dd490a authored by Sergey Poromov's avatar Sergey Poromov Committed by Commit Bot

DLP: Add check to video capture mode and interrupt it if needed.

Before capturing video in Capture Mode,
it should be checked whether the captured area is allowed
according to the currently set Data Leak Prevention rules.
Also, when video is being captured and confidential content
appears in the area, the capture should be stopped via a callback.

Bug: 1133324
Change-Id: I28ddd46b4c10760d3631d5b934ea06facec31df6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2438411
Commit-Queue: Sergey Poromov <poromov@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarNikita Podguzov <nikitapodguzov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#817695}
parent 3e5176bb
......@@ -284,6 +284,7 @@ void CaptureModeController::EndVideoRecording() {
// with all the frames.
is_recording_in_progress_ = false;
Shell::Get()->UpdateCursorCompositingEnabled();
delegate_->StopObservingRestrictedContent();
}
bool CaptureModeController::IsCaptureAllowed() const {
......@@ -521,6 +522,11 @@ void CaptureModeController::OnVideoRecordCountDownFinished() {
// TODO(afakhry): Call into the recording service.
delegate_->StartObservingRestrictedContent(
capture_params->window, capture_params->bounds,
base::BindOnce(&CaptureModeController::EndVideoRecording,
weak_ptr_factory_.GetWeakPtr()));
ShowStopRecordingButton(capture_params->window->GetRootWindow());
}
......
......@@ -29,4 +29,11 @@ bool TestCaptureModeDelegate::IsCaptureAllowed(const aura::Window* window,
return true;
}
void TestCaptureModeDelegate::StartObservingRestrictedContent(
const aura::Window* window,
const gfx::Rect& bounds,
base::OnceClosure stop_callback) {}
void TestCaptureModeDelegate::StopObservingRestrictedContent() {}
} // namespace ash
......@@ -6,6 +6,7 @@
#define ASH_CAPTURE_MODE_TEST_CAPTURE_MODE_DELEGATE_H_
#include "ash/public/cpp/capture_mode_delegate.h"
#include "base/callback.h"
namespace ash {
......@@ -24,6 +25,11 @@ class TestCaptureModeDelegate : public CaptureModeDelegate {
bool IsCaptureAllowed(const aura::Window* window,
const gfx::Rect& bounds,
bool for_video) const override;
void StartObservingRestrictedContent(
const aura::Window* window,
const gfx::Rect& bounds,
base::OnceClosure stop_callback) override;
void StopObservingRestrictedContent() override;
};
} // namespace ash
......
......@@ -6,6 +6,7 @@
#define ASH_PUBLIC_CPP_CAPTURE_MODE_DELEGATE_H_
#include "ash/public/cpp/ash_public_export.h"
#include "base/callback.h"
namespace aura {
class Window;
......@@ -49,6 +50,18 @@ class ASH_PUBLIC_EXPORT CaptureModeDelegate {
virtual bool IsCaptureAllowed(const aura::Window* window,
const gfx::Rect& bounds,
bool for_video) const = 0;
// Called when a video capture for |window| and |bounds| area is started, so
// that Data Leak Prevention can start observing the area.
// |on_area_restricted_callback| will be called when the area becomes
// restricted so that the capture should be interrupted.
virtual void StartObservingRestrictedContent(
const aura::Window* window,
const gfx::Rect& bounds,
base::OnceClosure on_area_restricted_callback) = 0;
// Called when the running video capture is stopped.
virtual void StopObservingRestrictedContent() = 0;
};
} // namespace ash
......
......@@ -9,7 +9,6 @@
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/ui/ash/screenshot_area.h"
#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
......@@ -51,53 +50,12 @@ DlpContentRestrictionSet DlpContentManager::GetOnScreenPresentRestrictions()
bool DlpContentManager::IsScreenshotRestricted(
const ScreenshotArea& area) const {
// Fullscreen - restricted if any confidential data is visible.
if (area.type == ScreenshotType::kAllRootWindows) {
return GetOnScreenPresentRestrictions().HasRestriction(
DlpContentRestriction::kScreenshot);
}
// Window - restricted if the window contains confidential data.
if (area.type == ScreenshotType::kWindow) {
DCHECK(area.window);
for (auto& entry : confidential_web_contents_) {
aura::Window* web_contents_window = entry.first->GetNativeView();
if (entry.second.HasRestriction(DlpContentRestriction::kScreenshot) &&
area.window->Contains(web_contents_window)) {
return true;
}
}
return false;
}
DCHECK_EQ(area.type, ScreenshotType::kPartialWindow);
DCHECK(area.rect);
DCHECK(area.window);
// Partial - restricted if any visible confidential WebContents intersects
// with the area.
for (auto& entry : confidential_web_contents_) {
if (entry.first->GetVisibility() != content::Visibility::VISIBLE ||
!entry.second.HasRestriction(DlpContentRestriction::kScreenshot)) {
continue;
}
aura::Window* web_contents_window = entry.first->GetNativeView();
aura::Window* root_window = web_contents_window->GetRootWindow();
// If no root window, then the WebContent shouldn't be visible.
if (!root_window)
continue;
// Not allowing if the area intersects with confidential WebContents,
// but the intersection doesn't belong to occluded area.
gfx::Rect intersection(*area.rect);
aura::Window::ConvertRectToTarget(area.window, root_window, &intersection);
intersection.Intersect(web_contents_window->GetBoundsInRootWindow());
if (!intersection.IsEmpty() &&
!web_contents_window->occluded_region_in_root().contains(
gfx::RectToSkIRect(intersection))) {
return true;
}
}
return IsAreaRestricted(area, DlpContentRestriction::kScreenshot);
}
return false;
bool DlpContentManager::IsVideoCaptureRestricted(
const ScreenshotArea& area) const {
return IsAreaRestricted(area, DlpContentRestriction::kVideoCapture);
}
bool DlpContentManager::IsPrintingRestricted(
......@@ -113,6 +71,21 @@ bool DlpContentManager::IsPrintingRestricted(
.HasRestriction(DlpContentRestriction::kPrint);
}
void DlpContentManager::OnVideoCaptureStarted(const ScreenshotArea& area,
base::OnceClosure stop_callback) {
if (IsVideoCaptureRestricted(area)) {
std::move(stop_callback).Run();
return;
}
DCHECK(!running_video_capture_.has_value());
running_video_capture_.emplace(
std::make_pair(area, std::move(stop_callback)));
}
void DlpContentManager::OnVideoCaptureStopped() {
running_video_capture_.reset();
}
/* static */
void DlpContentManager::SetDlpContentManagerForTesting(
DlpContentManager* dlp_content_manager) {
......@@ -140,6 +113,9 @@ void DlpContentManager::OnConfidentialityChanged(
if (web_contents->GetVisibility() == content::Visibility::VISIBLE) {
MaybeChangeOnScreenRestrictions();
}
// TODO(crbug.com/1133324): Track the corresponding window position for
// video capture. It might appear that some confidential content will become
// visible in the video capture area and it should be stopped.
}
}
......@@ -201,6 +177,7 @@ void DlpContentManager::MaybeChangeOnScreenRestrictions() {
on_screen_restrictions_ = new_restriction_set;
OnScreenRestrictionsChanged(added_restrictions, removed_restrictions);
}
CheckRunningVideoCapture();
}
void DlpContentManager::OnScreenRestrictionsChanged(
......@@ -232,6 +209,68 @@ void DlpContentManager::MaybeRemovePrivacyScreenEnforcement() const {
}
}
bool DlpContentManager::IsAreaRestricted(
const ScreenshotArea& area,
DlpContentRestriction restriction) const {
// Fullscreen - restricted if any confidential data is visible.
if (area.type == ScreenshotType::kAllRootWindows) {
return GetOnScreenPresentRestrictions().HasRestriction(restriction);
}
// Window - restricted if the window contains confidential data.
if (area.type == ScreenshotType::kWindow) {
DCHECK(area.window);
for (auto& entry : confidential_web_contents_) {
aura::Window* web_contents_window = entry.first->GetNativeView();
if (entry.second.HasRestriction(restriction) &&
area.window->Contains(web_contents_window)) {
return true;
}
}
return false;
}
DCHECK_EQ(area.type, ScreenshotType::kPartialWindow);
DCHECK(area.rect);
DCHECK(area.window);
// Partial - restricted if any visible confidential WebContents intersects
// with the area.
for (auto& entry : confidential_web_contents_) {
if (entry.first->GetVisibility() != content::Visibility::VISIBLE ||
!entry.second.HasRestriction(restriction)) {
continue;
}
aura::Window* web_contents_window = entry.first->GetNativeView();
aura::Window* root_window = web_contents_window->GetRootWindow();
// If no root window, then the WebContent shouldn't be visible.
if (!root_window)
continue;
// Not allowing if the area intersects with confidential WebContents,
// but the intersection doesn't belong to occluded area.
gfx::Rect intersection(*area.rect);
aura::Window::ConvertRectToTarget(area.window, root_window, &intersection);
intersection.Intersect(web_contents_window->GetBoundsInRootWindow());
if (!intersection.IsEmpty() &&
!web_contents_window->occluded_region_in_root().contains(
gfx::RectToSkIRect(intersection))) {
return true;
}
}
return false;
}
void DlpContentManager::CheckRunningVideoCapture() {
if (!running_video_capture_.has_value())
return;
const auto& area = running_video_capture_->first;
auto& stop_callback = running_video_capture_->second;
if (IsAreaRestricted(area, DlpContentRestriction::kVideoCapture)) {
std::move(stop_callback).Run();
running_video_capture_.reset();
}
}
// static
base::TimeDelta DlpContentManager::GetPrivacyScreenOffDelayForTesting() {
return kPrivacyScreenOffDelay;
......
......@@ -5,10 +5,13 @@
#ifndef CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_H_
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
#include "chrome/browser/ui/ash/screenshot_area.h"
class GURL;
struct ScreenshotArea;
......@@ -41,9 +44,20 @@ class DlpContentManager {
// Returns whether screenshots should be restricted.
virtual bool IsScreenshotRestricted(const ScreenshotArea& area) const;
// Returns whether video capture should be restricted.
bool IsVideoCaptureRestricted(const ScreenshotArea& area) const;
// Returns whether printing should be restricted.
bool IsPrintingRestricted(content::WebContents* web_contents) const;
// Called when video capturing for |area| is started.
// |stop_callback| will be called when restricted content will appear there.
void OnVideoCaptureStarted(const ScreenshotArea& area,
base::OnceClosure stop_callback);
// Called when video capturing is stopped.
void OnVideoCaptureStopped();
// The caller (test) should manage |dlp_content_manager| lifetime.
// Reset doesn't delete the object.
static void SetDlpContentManagerForTesting(
......@@ -91,6 +105,14 @@ class DlpContentManager {
// Removes PrivacyScreen enforcement after delay if it's still not enforced.
void MaybeRemovePrivacyScreenEnforcement() const;
// Returns whether |restriction| is currently enforced for |area|.
bool IsAreaRestricted(const ScreenshotArea& area,
DlpContentRestriction restriction) const;
// Checks and stops the running video capture if restricted content appeared
// in the corresponding areas.
void CheckRunningVideoCapture();
// Get the delay before switching privacy screen off.
static base::TimeDelta GetPrivacyScreenOffDelayForTesting();
......@@ -100,6 +122,10 @@ class DlpContentManager {
// Set of restriction applied to the currently visible content.
DlpContentRestrictionSet on_screen_restrictions_;
// The currently running video capture are and callback to stop, if any.
base::Optional<std::pair<ScreenshotArea, base::OnceClosure>>
running_video_capture_;
};
} // namespace policy
......
......@@ -19,6 +19,8 @@ enum DlpContentRestriction {
kPrivacyScreen = 1 << 1,
// Do not allow printing.
kPrint = 1 << 2,
// Do not allow video capturing of the content.
kVideoCapture = 1 << 3,
};
// Represents set of restrictions applied to on-screen content.
......
......@@ -85,6 +85,18 @@ bool ChromeCaptureModeDelegate::IsCaptureAllowed(const aura::Window* window,
policy::DlpContentManager* dlp_content_manager =
policy::DlpContentManager::Get();
const ScreenshotArea area = ConvertToScreenshotArea(window, bounds);
// TODO(poromov): Implement check for video capture.
return for_video ? true : !dlp_content_manager->IsScreenshotRestricted(area);
return for_video ? !dlp_content_manager->IsVideoCaptureRestricted(area)
: !dlp_content_manager->IsScreenshotRestricted(area);
}
void ChromeCaptureModeDelegate::StartObservingRestrictedContent(
const aura::Window* window,
const gfx::Rect& bounds,
base::OnceClosure stop_callback) {
policy::DlpContentManager::Get()->OnVideoCaptureStarted(
ConvertToScreenshotArea(window, bounds), std::move(stop_callback));
}
void ChromeCaptureModeDelegate::StopObservingRestrictedContent() {
policy::DlpContentManager::Get()->OnVideoCaptureStopped();
}
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_
#include "ash/public/cpp/capture_mode_delegate.h"
#include "base/callback.h"
// Implements the interface needed for the delegate of the Capture Mode feature
// in Chrome.
......@@ -25,6 +26,11 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate {
bool IsCaptureAllowed(const aura::Window* window,
const gfx::Rect& bounds,
bool for_video) const override;
void StartObservingRestrictedContent(
const aura::Window* window,
const gfx::Rect& bounds,
base::OnceClosure stop_callback) override;
void StopObservingRestrictedContent() override;
};
#endif // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment