blob: c39217ffa8c8cd4f0018d92b966f873108648a7f [file] [log] [blame]
// Copyright 2025 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/enterprise/data_controls/chrome_clipboard_context.h"
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_info.h"
#include "chrome/browser/enterprise/data_protection/content_area_user_provider.h"
#include "chrome/browser/profiles/profile.h"
#include "components/enterprise/data_controls/core/browser/prefs.h"
#include "components/policy/core/common/policy_types.h"
#include "components/prefs/pref_service.h"
namespace data_controls {
namespace {
GURL GetURL(const content::ClipboardEndpoint& endpoint) {
if (!endpoint.data_transfer_endpoint() ||
!endpoint.data_transfer_endpoint()->IsUrlType() ||
!endpoint.data_transfer_endpoint()->GetURL()) {
return GURL();
}
return *endpoint.data_transfer_endpoint()->GetURL();
}
bool PolicyAppliedAtUserScope(content::BrowserContext* browser_context,
const char* scope_pref) {
CHECK(browser_context);
CHECK(scope_pref);
return Profile::FromBrowserContext(browser_context)
->GetPrefs()
->GetInteger(scope_pref) == policy::POLICY_SCOPE_USER;
}
} // namespace
ChromeClipboardContext::ChromeClipboardContext(
content::ClipboardEndpoint source,
content::ClipboardEndpoint destination,
ui::ClipboardMetadata metadata)
: source_(std::move(source)),
destination_(std::move(destination)),
metadata_(std::move(metadata)) {}
ChromeClipboardContext::ChromeClipboardContext(
content::ClipboardEndpoint source,
ui::ClipboardMetadata metadata)
: source_(std::move(source)),
destination_(std::nullopt),
metadata_(std::move(metadata)) {}
ChromeClipboardContext::~ChromeClipboardContext() = default;
// static
enterprise_connectors::ContentMetaData::CopiedTextSource
ChromeClipboardContext::GetClipboardSource(
const content::ClipboardEndpoint& source,
const content::ClipboardEndpoint& destination,
const char* scope_pref) {
CHECK(destination.browser_context());
using SourceType = enterprise_connectors::ContentMetaData::CopiedTextSource;
SourceType copied_text_source;
if (!source.browser_context()) {
// This off the record check will also include guest profile sources, but
// since there is no way to disambiguate them with a null BrowserContext
// INCOGNITO is selected instead of CLIPBOARD to not share the source URL in
// such cases.
if (source.data_transfer_endpoint() &&
source.data_transfer_endpoint()->off_the_record()) {
copied_text_source.set_context(SourceType::INCOGNITO);
} else {
copied_text_source.set_context(SourceType::CLIPBOARD);
}
} else if (Profile::FromBrowserContext(source.browser_context())
->IsIncognitoProfile()) {
copied_text_source.set_context(SourceType::INCOGNITO);
} else if (source.browser_context() == destination.browser_context()) {
copied_text_source.set_context(SourceType::SAME_PROFILE);
} else {
copied_text_source.set_context(SourceType::OTHER_PROFILE);
}
switch (copied_text_source.context()) {
case SourceType::UNSPECIFIED:
case SourceType::INCOGNITO:
break;
case SourceType::CLIPBOARD:
// If the user does something like closing the browser between the time
// they copy and then paste, the DTE might have a URL even though the lack
// of browser context will make it impossible to know if the `SourceType`
// is `SAME_PROFILE` or `OTHER_PROFILE`.
//
// In that case, we can be conservative and perform the same check as
// `OTHER_PROFILE`. Note that this code path is unreachable in the case of
// an incognito source URL as that is handled in the `set_context`
// conditions above.
[[fallthrough]];
case SourceType::OTHER_PROFILE:
// Only add a source URL if the other profile is getting the policy
// applied at the machine scope, not the user scope.
if (PolicyAppliedAtUserScope(destination.browser_context(), scope_pref)) {
break;
}
[[fallthrough]];
case SourceType::SAME_PROFILE:
if (source.data_transfer_endpoint() &&
source.data_transfer_endpoint()->IsUrlType() &&
source.data_transfer_endpoint()->GetURL()) {
copied_text_source.set_url(
source.data_transfer_endpoint()->GetURL()->spec());
}
break;
}
return copied_text_source;
}
GURL ChromeClipboardContext::source_url() const {
return GetURL(source_);
}
GURL ChromeClipboardContext::destination_url() const {
return GetURL(destination_);
}
enterprise_connectors::ContentMetaData::CopiedTextSource
ChromeClipboardContext::data_controls_copied_text_source() const {
return GetClipboardSource(source_, destination_, kDataControlsRulesScopePref);
}
ui::ClipboardFormatType ChromeClipboardContext::format_type() const {
return metadata_.format_type;
}
std::optional<size_t> ChromeClipboardContext::size() const {
return metadata_.size;
}
std::string ChromeClipboardContext::source_active_user() const {
auto* profile = Profile::FromBrowserContext(source_.browser_context());
if (!profile) {
return "";
}
return enterprise_connectors::ContentAreaUserProvider::GetUser(
profile, source_.web_contents(), source_url());
}
std::string ChromeClipboardContext::destination_active_user() const {
auto* profile = Profile::FromBrowserContext(destination_.browser_context());
if (!profile) {
return "";
}
return enterprise_connectors::ContentAreaUserProvider::GetUser(
profile, destination_.web_contents(), destination_url());
}
} // namespace data_controls