Commit 40d807ac authored by Ahmed Fakhry's avatar Ahmed Fakhry Committed by Commit Bot

[Reland] capture_mode: Hook to policy and locked fullscreen mode

This is a reland of https://crrev.com/c/2548880 which was reverted
due to a flaky test. The flakiness root cause was resolved in
https://crrev.com/c/2552921. Therefore, there're no changes in this
CL from the original.

TBR=jamescook@chromium.org, poromov@chromium.org, isandrk@chromium.org, bmalcolm@chromium.org

Original CL description:
Capture Mode should be disabled when the `kDisableScreenshots` policy
is disabled.
It also should be disabled when a tab requests to be in a locked
fullscreen mode, and any on going recording should be interrupted.

BUG=1150585
TEST=Existing, and added a new test.

Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2548880Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Reviewed-by: default avatarIvan Šandrk <isandrk@chromium.org>
Reviewed-by: default avatarBrian Malcolm <bmalcolm@chromium.org>
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#829390}
Change-Id: If427589bcaf9837d0def703dbeff6fc03b8167cc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2552215Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#829927}
parent 13e14812
......@@ -6,12 +6,14 @@
#include <vector>
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/privacy_screen_dlp_helper.h"
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_notification_helper.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.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"
......@@ -113,19 +115,18 @@ bool DlpContentManager::IsScreenCaptureRestricted(
return false;
}
void DlpContentManager::OnVideoCaptureStarted(const ScreenshotArea& area,
base::OnceClosure stop_callback) {
void DlpContentManager::OnVideoCaptureStarted(const ScreenshotArea& area) {
if (IsVideoCaptureRestricted(area)) {
std::move(stop_callback).Run();
if (ash::features::IsCaptureModeEnabled())
ChromeCaptureModeDelegate::Get()->InterruptVideoRecordingIfAny();
return;
}
DCHECK(!running_video_capture_.has_value());
running_video_capture_.emplace(
std::make_pair(area, std::move(stop_callback)));
DCHECK(!running_video_capture_area_.has_value());
running_video_capture_area_.emplace(area);
}
void DlpContentManager::OnVideoCaptureStopped() {
running_video_capture_.reset();
running_video_capture_area_.reset();
}
bool DlpContentManager::IsCaptureModeInitRestricted() const {
......@@ -367,13 +368,13 @@ bool DlpContentManager::IsAreaRestricted(
}
void DlpContentManager::CheckRunningVideoCapture() {
if (!running_video_capture_.has_value())
if (!running_video_capture_area_.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();
if (IsAreaRestricted(*running_video_capture_area_,
DlpContentRestriction::kVideoCapture)) {
if (ash::features::IsCaptureModeEnabled())
ChromeCaptureModeDelegate::Get()->InterruptVideoRecordingIfAny();
running_video_capture_area_.reset();
}
}
......
......@@ -70,9 +70,7 @@ class DlpContentManager : public DlpWindowObserver::Delegate {
const content::DesktopMediaID& media_id) 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);
void OnVideoCaptureStarted(const ScreenshotArea& area);
// Called when video capturing is stopped.
void OnVideoCaptureStopped();
......@@ -203,9 +201,8 @@ class DlpContentManager : public DlpWindowObserver::Delegate {
// 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_;
// The currently running video capture area if any.
base::Optional<ScreenshotArea> running_video_capture_area_;
// List of the currently running screen captures.
std::vector<ScreenCaptureInfo> running_screen_captures_;
......
......@@ -4,11 +4,14 @@
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
#include "ash/public/cpp/ash_features.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_test_utils.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
#include "chrome/browser/ui/ash/screenshot_area.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
......@@ -45,7 +48,17 @@ constexpr char kScreenCaptureResumedNotificationId[] =
class DlpContentManagerBrowserTest : public InProcessBrowserTest {
public:
DlpContentManagerBrowserTest() {}
DlpContentManagerBrowserTest() = default;
~DlpContentManagerBrowserTest() override = default;
// InProcessBrowserTest:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(ash::features::kCaptureMode);
InProcessBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest, ScreenshotsRestricted) {
......@@ -104,8 +117,6 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
DlpContentManager* manager = DlpContentManager::Get();
aura::Window* root_window =
browser()->window()->GetNativeWindow()->GetRootWindow();
ScreenshotArea fullscreen = ScreenshotArea::CreateForPartialWindow(
root_window, root_window->bounds());
// Open first browser window.
Browser* browser1 = browser();
......@@ -130,7 +141,9 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
// Start capture of the whole screen.
base::RunLoop run_loop;
manager->OnVideoCaptureStarted(fullscreen, run_loop.QuitClosure());
auto* capture_mode_delegate = ChromeCaptureModeDelegate::Get();
capture_mode_delegate->StartObservingRestrictedContent(
root_window, root_window->bounds(), run_loop.QuitClosure());
// Move first window with confidential content to make it visible.
browser1->window()->SetBounds(gfx::Rect(100, 100, 700, 700));
......@@ -138,7 +151,7 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
// Check that capture was requested to be stopped via callback.
run_loop.Run();
manager->OnVideoCaptureStopped();
capture_mode_delegate->StopObservingRestrictedContent();
browser2->window()->Close();
}
......@@ -147,8 +160,6 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
DlpContentManager* manager = DlpContentManager::Get();
aura::Window* root_window =
browser()->window()->GetNativeWindow()->GetRootWindow();
ScreenshotArea fullscreen = ScreenshotArea::CreateForPartialWindow(
root_window, root_window->bounds());
// Open first browser window.
Browser* browser1 = browser();
......@@ -173,7 +184,9 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
// Start capture of the whole screen.
base::RunLoop run_loop;
manager->OnVideoCaptureStarted(fullscreen, run_loop.QuitClosure());
auto* capture_mode_delegate = ChromeCaptureModeDelegate::Get();
capture_mode_delegate->StartObservingRestrictedContent(
root_window, root_window->bounds(), run_loop.QuitClosure());
// Move second window to make first window with confidential content visible.
browser2->window()->SetBounds(gfx::Rect(150, 150, 700, 700));
......@@ -181,7 +194,7 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
// Check that capture was requested to be stopped via callback.
run_loop.Run();
manager->OnVideoCaptureStopped();
capture_mode_delegate->StopObservingRestrictedContent();
browser2->window()->Close();
}
......@@ -190,8 +203,6 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
DlpContentManager* manager = DlpContentManager::Get();
aura::Window* root_window =
browser()->window()->GetNativeWindow()->GetRootWindow();
ScreenshotArea fullscreen = ScreenshotArea::CreateForPartialWindow(
root_window, root_window->bounds());
// Open first browser window.
Browser* browser1 = browser();
......@@ -216,8 +227,9 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
// Start capture of the whole screen.
base::RunLoop run_loop;
manager->OnVideoCaptureStarted(
fullscreen, base::BindOnce([] {
auto* capture_mode_delegate = ChromeCaptureModeDelegate::Get();
capture_mode_delegate->StartObservingRestrictedContent(
root_window, root_window->bounds(), base::BindOnce([] {
FAIL() << "Video capture stop callback shouldn't be called";
}));
......@@ -225,8 +237,8 @@ IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest,
browser1->window()->SetBounds(gfx::Rect(150, 150, 500, 500));
// Check that capture was not requested to be stopped via callback.
manager->OnVideoCaptureStopped();
run_loop.RunUntilIdle();
capture_mode_delegate->StopObservingRestrictedContent();
browser2->window()->Close();
}
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/api/tabs/tabs_util.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/assistant/assistant_state.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
......@@ -11,6 +12,7 @@
#include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
#include "chrome/browser/chromeos/assistant/assistant_util.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
#include "chrome/browser/ui/ash/chrome_screenshot_grabber.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_command_controller.h"
......@@ -42,6 +44,11 @@ void SetLockedFullscreenState(Browser* browser, bool locked) {
// Disallow screenshots in locked fullscreen mode.
ChromeScreenshotGrabber::Get()->set_screenshots_allowed(!locked);
// Disable both screenshots and video screen captures via the capture mode
// feature.
if (ash::features::IsCaptureModeEnabled())
ChromeCaptureModeDelegate::Get()->SetIsScreenCaptureLocked(locked);
// Reset the clipboard and kill dev tools when entering or exiting locked
// fullscreen (security concerns).
ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
......
......@@ -11,6 +11,7 @@
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/launch_utils.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/platform_util.h"
......@@ -29,6 +30,8 @@
namespace {
ChromeCaptureModeDelegate* g_instance = nullptr;
ScreenshotArea ConvertToScreenshotArea(const aura::Window* window,
const gfx::Rect& bounds) {
return window->IsRootWindow()
......@@ -36,11 +39,39 @@ ScreenshotArea ConvertToScreenshotArea(const aura::Window* window,
: ScreenshotArea::CreateForWindow(window);
}
bool IsScreenCaptureDisabledByPolicy() {
return g_browser_process->local_state()->GetBoolean(
prefs::kDisableScreenshots);
}
} // namespace
ChromeCaptureModeDelegate::ChromeCaptureModeDelegate() = default;
ChromeCaptureModeDelegate::ChromeCaptureModeDelegate() {
DCHECK_EQ(g_instance, nullptr);
g_instance = this;
}
ChromeCaptureModeDelegate::~ChromeCaptureModeDelegate() = default;
ChromeCaptureModeDelegate::~ChromeCaptureModeDelegate() {
DCHECK_EQ(g_instance, this);
g_instance = nullptr;
}
// static
ChromeCaptureModeDelegate* ChromeCaptureModeDelegate::Get() {
DCHECK(g_instance);
return g_instance;
}
void ChromeCaptureModeDelegate::SetIsScreenCaptureLocked(bool locked) {
is_screen_capture_locked_ = locked;
if (is_screen_capture_locked_)
InterruptVideoRecordingIfAny();
}
void ChromeCaptureModeDelegate::InterruptVideoRecordingIfAny() {
if (interrupt_video_recording_callback_)
std::move(interrupt_video_recording_callback_).Run();
}
base::FilePath ChromeCaptureModeDelegate::GetActiveUserDownloadsDir() const {
DCHECK(chromeos::LoginState::Get()->IsUserLoggedIn());
......@@ -86,12 +117,19 @@ bool ChromeCaptureModeDelegate::Uses24HourFormat() const {
}
bool ChromeCaptureModeDelegate::IsCaptureModeInitRestricted() const {
return policy::DlpContentManager::Get()->IsCaptureModeInitRestricted();
return is_screen_capture_locked_ || IsScreenCaptureDisabledByPolicy() ||
policy::DlpContentManager::Get()->IsCaptureModeInitRestricted();
}
bool ChromeCaptureModeDelegate::IsCaptureAllowed(const aura::Window* window,
const gfx::Rect& bounds,
bool for_video) const {
if (is_screen_capture_locked_)
return false;
if (IsScreenCaptureDisabledByPolicy())
return false;
policy::DlpContentManager* dlp_content_manager =
policy::DlpContentManager::Get();
const ScreenshotArea area = ConvertToScreenshotArea(window, bounds);
......@@ -103,11 +141,16 @@ void ChromeCaptureModeDelegate::StartObservingRestrictedContent(
const aura::Window* window,
const gfx::Rect& bounds,
base::OnceClosure stop_callback) {
// The order here matters, since DlpContentManager::OnVideoCaptureStarted()
// may call InterruptVideoRecordingIfAny() right away, so the callback must be
// set first.
interrupt_video_recording_callback_ = std::move(stop_callback);
policy::DlpContentManager::Get()->OnVideoCaptureStarted(
ConvertToScreenshotArea(window, bounds), std::move(stop_callback));
ConvertToScreenshotArea(window, bounds));
}
void ChromeCaptureModeDelegate::StopObservingRestrictedContent() {
interrupt_video_recording_callback_.Reset();
policy::DlpContentManager::Get()->OnVideoCaptureStopped();
}
......
......@@ -18,6 +18,16 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate {
delete;
~ChromeCaptureModeDelegate() override;
static ChromeCaptureModeDelegate* Get();
// Sets |is_screen_capture_locked_| to the given |locked|, and interrupts any
// on going video capture.
void SetIsScreenCaptureLocked(bool locked);
// Interrupts an on going video recording if any, due to some restricted
// content showing up on the screen, or if screen capture becomes locked.
void InterruptVideoRecordingIfAny();
// ash::CaptureModeDelegate:
base::FilePath GetActiveUserDownloadsDir() const override;
void ShowScreenCaptureItemInFolder(const base::FilePath& file_path) override;
......@@ -37,6 +47,19 @@ class ChromeCaptureModeDelegate : public ash::CaptureModeDelegate {
override;
void BindAudioStreamFactory(
mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override;
private:
// Used to temporarily disable capture mode in certain cases for which neither
// a device policy, nor DLP will be triggered. For example, Some extension
// APIs can request that a tab operate in a locked fullscreen mode, and in
// that, capturing the screen is disabled.
bool is_screen_capture_locked_ = false;
// A callback to terminate an on going video recording on ash side due to a
// restricted content showing up on the screen, or screen capture becoming
// locked.
// This is only non-null during recording.
base::OnceClosure interrupt_video_recording_callback_;
};
#endif // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_
......@@ -213,3 +213,15 @@ IN_PROC_BROWSER_TEST_F(RecordingServiceBrowserTest, SuccessiveRecording) {
test_api.SetUserSelectedRegion(gfx::Rect(50, 200));
FinishVideoRecordingTest(&test_api);
}
// Tests that recording will be interrupted once screen capture becomes locked.
IN_PROC_BROWSER_TEST_F(RecordingServiceBrowserTest,
RecordingInterruptedOnCaptureLocked) {
ash::CaptureModeTestApi test_api;
test_api.StartForFullscreen(/*for_video=*/true);
test_api.PerformCapture();
WaitForMilliseconds(1000);
ChromeCaptureModeDelegate::Get()->SetIsScreenCaptureLocked(true);
const base::FilePath video_path = WaitForVideoFileToBeSaved();
VerifyVideoFileAndDelete(video_path);
}
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