// Copyright 2021 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/chromeos/policy/dlp/dlp_content_manager_lacros.h"
#include <vector>
#include "chrome/browser/ui/lacros/window_utility.h"
#include "chromeos/crosapi/mojom/dlp.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h"
#include "ui/aura/window.h"
#include "ui/platform_window/platform_window.h"
namespace policy {
namespace {
crosapi::mojom::DlpRestrictionLevel ConvertLevelToMojo(
DlpRulesManager::Level level) {
switch (level) {
case DlpRulesManager::Level::kReport:
return crosapi::mojom::DlpRestrictionLevel::kReport;
case DlpRulesManager::Level::kWarn:
return crosapi::mojom::DlpRestrictionLevel::kWarn;
case DlpRulesManager::Level::kBlock:
return crosapi::mojom::DlpRestrictionLevel::kBlock;
case DlpRulesManager::Level::kAllow:
return crosapi::mojom::DlpRestrictionLevel::kAllow;
case DlpRulesManager::Level::kNotSet:
return crosapi::mojom::DlpRestrictionLevel::kNotSet;
}
}
crosapi::mojom::DlpRestrictionLevelAndUrlPtr ConvertLevelAndUrlToMojo(
RestrictionLevelAndUrl level_and_url) {
auto result = crosapi::mojom::DlpRestrictionLevelAndUrl::New();
result->level = ConvertLevelToMojo(level_and_url.level);
result->url = level_and_url.url;
return result;
}
crosapi::mojom::DlpRestrictionSetPtr ConvertRestrictionSetToMojo(
const DlpContentRestrictionSet& restriction_set) {
auto result = crosapi::mojom::DlpRestrictionSet::New();
result->screenshot =
ConvertLevelAndUrlToMojo(restriction_set.GetRestrictionLevelAndUrl(
DlpContentRestriction::kScreenshot));
result->privacy_screen =
ConvertLevelAndUrlToMojo(restriction_set.GetRestrictionLevelAndUrl(
DlpContentRestriction::kPrivacyScreen));
result->print = ConvertLevelAndUrlToMojo(
restriction_set.GetRestrictionLevelAndUrl(DlpContentRestriction::kPrint));
result->screen_share =
ConvertLevelAndUrlToMojo(restriction_set.GetRestrictionLevelAndUrl(
DlpContentRestriction::kScreenShare));
return result;
}
crosapi::mojom::ScreenShareAreaPtr ConvertToScreenShareArea(
const content::DesktopMediaID& media_id) {
auto result = crosapi::mojom::ScreenShareArea::New();
if (media_id.type == content::DesktopMediaID::Type::TYPE_SCREEN) {
return result;
}
DCHECK_EQ(media_id.type, content::DesktopMediaID::Type::TYPE_WINDOW);
aura::Window* window = content::DesktopMediaID::GetNativeWindowById(media_id);
if (window) {
result->window_id = lacros_window_utility::GetRootWindowUniqueId(window);
}
result->snapshot_source_id = media_id.id;
return result;
}
} // namespace
// static
DlpContentManagerLacros* DlpContentManagerLacros::Get() {
return static_cast<DlpContentManagerLacros*>(DlpContentObserver::Get());
}
void DlpContentManagerLacros::CheckScreenShareRestriction(
const content::DesktopMediaID& media_id,
const std::u16string& application_title,
WarningCallback callback) {
if (media_id.type == content::DesktopMediaID::Type::TYPE_WEB_CONTENTS) {
ProcessScreenShareRestriction(
application_title,
GetScreenShareConfidentialContentsInfoForWebContents(
GetWebContentsFromMediaId(media_id)),
std::move(callback));
return;
}
chromeos::LacrosService* lacros_service = chromeos::LacrosService::Get();
if (!lacros_service->IsAvailable<crosapi::mojom::Dlp>()) {
LOG(WARNING) << "DLP mojo service not available";
std::move(callback).Run(true);
return;
}
int dlp_mojo_version =
lacros_service->GetInterfaceVersion<crosapi::mojom::Dlp>();
if (dlp_mojo_version < int{crosapi::mojom::Dlp::MethodMinVersions::
kCheckScreenShareRestrictionMinVersion}) {
LOG(WARNING) << "DLP mojo service version does not support screen share "
"restrictions";
std::move(callback).Run(true);
return;
}
lacros_service->GetRemote<crosapi::mojom::Dlp>()->CheckScreenShareRestriction(
ConvertToScreenShareArea(media_id), application_title,
std::move(callback));
}
void DlpContentManagerLacros::OnScreenShareStarted(
const std::string& label,
std::vector<content::DesktopMediaID> screen_share_ids,
const std::u16string& application_title,
base::RepeatingClosure stop_callback,
content::MediaStreamUI::StateChangeCallback state_change_callback,
content::MediaStreamUI::SourceCallback source_callback) {
for (const content::DesktopMediaID& media_id : screen_share_ids) {
if (media_id.type == content::DesktopMediaID::Type::TYPE_WEB_CONTENTS) {
AddOrUpdateScreenShare(label, media_id, application_title, stop_callback,
state_change_callback, source_callback);
} else {
chromeos::LacrosService* lacros_service = chromeos::LacrosService::Get();
auto delegate = std::make_unique<ScreenShareStateChangeDelegate>(
label, media_id, state_change_callback, stop_callback);
if (lacros_service->IsAvailable<crosapi::mojom::Dlp>()) {
lacros_service->GetRemote<crosapi::mojom::Dlp>()->OnScreenShareStarted(
label, ConvertToScreenShareArea(media_id), application_title,
delegate->BindDelegate());
running_remote_screen_shares_.push_back(std::move(delegate));
}
}
}
CheckRunningScreenShares();
}
void DlpContentManagerLacros::OnScreenShareStopped(
const std::string& label,
const content::DesktopMediaID& media_id) {
if (media_id.type == content::DesktopMediaID::Type::TYPE_WEB_CONTENTS) {
RemoveScreenShare(label, media_id);
} else {
chromeos::LacrosService* lacros_service = chromeos::LacrosService::Get();
if (lacros_service->IsAvailable<crosapi::mojom::Dlp>()) {
lacros_service->GetRemote<crosapi::mojom::Dlp>()->OnScreenShareStopped(
label, ConvertToScreenShareArea(media_id));
}
std::erase_if(
running_remote_screen_shares_,
[=](const std::unique_ptr<
DlpContentManagerLacros::ScreenShareStateChangeDelegate>& delegate)
-> bool {
return delegate->label() == label && delegate->media_id() == media_id;
});
}
}
void DlpContentManagerLacros::TabLocationMaybeChanged(
content::WebContents* web_contents) {
UpdateRestrictions(web_contents->GetNativeView());
}
DlpContentManagerLacros::ScreenShareStateChangeDelegate::
ScreenShareStateChangeDelegate(
const std::string& label,
const content::DesktopMediaID& media_id,
content::MediaStreamUI::StateChangeCallback state_change_callback,
base::OnceClosure stop_callback)
: label_(label),
media_id_(media_id),
state_change_callback_(std::move(state_change_callback)),
stop_callback_(std::move(stop_callback)) {}
DlpContentManagerLacros::ScreenShareStateChangeDelegate::
~ScreenShareStateChangeDelegate() = default;
bool DlpContentManagerLacros::ScreenShareStateChangeDelegate::operator==(
const DlpContentManagerLacros::ScreenShareStateChangeDelegate& other)
const {
return label_ == other.label_ && media_id_ == other.media_id_;
}
bool DlpContentManagerLacros::ScreenShareStateChangeDelegate::operator!=(
const DlpContentManagerLacros::ScreenShareStateChangeDelegate& other)
const {
return !(*this == other);
}
mojo::PendingRemote<crosapi::mojom::StateChangeDelegate>
DlpContentManagerLacros::ScreenShareStateChangeDelegate::BindDelegate() {
return receiver_.BindNewPipeAndPassRemoteWithVersion();
}
void DlpContentManagerLacros::ScreenShareStateChangeDelegate::OnPause() {
state_change_callback_.Run(media_id_,
blink::mojom::MediaStreamStateChange::PAUSE);
}
void DlpContentManagerLacros::ScreenShareStateChangeDelegate::OnResume() {
state_change_callback_.Run(media_id_,
blink::mojom::MediaStreamStateChange::PLAY);
}
void DlpContentManagerLacros::ScreenShareStateChangeDelegate::OnStop() {
DCHECK(stop_callback_);
if (stop_callback_) {
std::move(stop_callback_).Run();
}
}
DlpContentManagerLacros::DlpContentManagerLacros() = default;
DlpContentManagerLacros::~DlpContentManagerLacros() {
// Clean up still observed windows.
for (const auto& window_pair : window_webcontents_) {
window_pair.first->RemoveObserver(this);
}
}
void DlpContentManagerLacros::OnConfidentialityChanged(
content::WebContents* web_contents,
const DlpContentRestrictionSet& restriction_set) {
DlpContentManager::OnConfidentialityChanged(web_contents, restriction_set);
aura::Window* window = web_contents->GetNativeView();
if (!window_webcontents_.contains(window)) {
window_webcontents_[window] = {};
window->AddObserver(this);
}
window_webcontents_[window].insert(web_contents);
UpdateRestrictions(window);
CheckRunningScreenShares();
}
void DlpContentManagerLacros::OnWebContentsDestroyed(
content::WebContents* web_contents) {
DlpContentManager::OnWebContentsDestroyed(web_contents);
aura::Window* window = web_contents->GetNativeView();
if (window_webcontents_.contains(window)) {
window_webcontents_[window].erase(web_contents);
UpdateRestrictions(window);
}
}
void DlpContentManagerLacros::OnVisibilityChanged(
content::WebContents* web_contents) {
aura::Window* window = web_contents->GetNativeView();
UpdateRestrictions(window);
}
void DlpContentManagerLacros::OnWindowDestroying(aura::Window* window) {
window->RemoveObserver(this);
window_webcontents_.erase(window);
confidential_windows_.erase(window);
}
void DlpContentManagerLacros::UpdateRestrictions(aura::Window* window) {
DlpContentRestrictionSet new_restrictions;
for (content::WebContents* web_contents : window_webcontents_[window]) {
if (web_contents->GetNativeView()->IsVisible()) {
new_restrictions.UnionWith(confidential_web_contents_[web_contents]);
}
}
if (new_restrictions != confidential_windows_[window]) {
confidential_windows_[window] = new_restrictions;
chromeos::LacrosService* lacros_service = chromeos::LacrosService::Get();
if (lacros_service->IsAvailable<crosapi::mojom::Dlp>()) {
lacros_service->GetRemote<crosapi::mojom::Dlp>()->DlpRestrictionsUpdated(
lacros_window_utility::GetRootWindowUniqueId(window),
ConvertRestrictionSetToMojo(new_restrictions));
}
}
}
DlpContentManager::ConfidentialContentsInfo
DlpContentManagerLacros::GetScreenShareConfidentialContentsInfo(
const content::DesktopMediaID& media_id,
content::WebContents* web_contents) const {
if (media_id.type == content::DesktopMediaID::Type::TYPE_WEB_CONTENTS) {
return GetScreenShareConfidentialContentsInfoForWebContents(web_contents);
}
NOTREACHED_IN_MIGRATION();
return ConfidentialContentsInfo();
}
} // namespace policy