blob: c9bec05a7b24975f1d5ba3a9d764f1f40bfcde9d [file] [log] [blame]
// Copyright 2020 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/notifications/screen_capture_notification_blocker.h"
#include <algorithm>
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/notifications/notification_display_service.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/public/cpp/notification.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace {
void RecordScreenCaptureCount(const std::string& suffix, int count) {
base::UmaHistogramCounts100(
base::StrCat({"Notifications.Blocker.ScreenCapture.", suffix}), count);
}
} // namespace
const char kMuteNotificationId[] = "notifications_muted";
ScreenCaptureNotificationBlocker::ScreenCaptureNotificationBlocker(
NotificationDisplayService* notification_display_service)
: notification_display_service_(notification_display_service) {
DCHECK(notification_display_service_);
observation_.Observe(MediaCaptureDevicesDispatcher::GetInstance()
->GetMediaStreamCaptureIndicator()
.get());
}
ScreenCaptureNotificationBlocker::~ScreenCaptureNotificationBlocker() = default;
bool ScreenCaptureNotificationBlocker::ShouldBlockNotification(
const message_center::Notification& notification) {
// Don't block if the user clicked on "Show" for the current session.
if (state_ == NotifyState::kShowAll)
return false;
// Don't block if no WebContents currently captures the screen.
if (capturing_web_contents_.empty())
return false;
// Otherwise block all notifications that belong to non-capturing origins.
return std::ranges::none_of(
capturing_web_contents_,
[&notification](content::WebContents* web_contents) {
return url::IsSameOriginWith(notification.origin_url(),
web_contents->GetLastCommittedURL());
});
}
void ScreenCaptureNotificationBlocker::OnBlockedNotification(
const message_center::Notification& notification,
bool replaced) {
if (replaced)
++replaced_notification_count_;
else
++muted_notification_count_;
if (state_ == NotifyState::kSnooze)
++snoozed_notification_count_;
if (state_ == NotifyState::kNotifyMuted)
DisplayMuteNotification();
}
void ScreenCaptureNotificationBlocker::OnClosedNotification(
const message_center::Notification& notification) {
++closed_notification_count_;
}
void ScreenCaptureNotificationBlocker::OnAction(
MutedNotificationHandler::Action action) {
DCHECK(state_ == NotifyState::kNotifyMuted);
CloseMuteNotification();
switch (action) {
case MutedNotificationHandler::Action::kUserClose:
case MutedNotificationHandler::Action::kBodyClick:
// Nothing to do here.
break;
case MutedNotificationHandler::Action::kShowClick:
state_ = NotifyState::kShowAll;
NotifyBlockingStateChanged();
ReportSessionMetrics(/*revealed=*/true);
break;
case MutedNotificationHandler::Action::kSnoozeClick:
state_ = NotifyState::kSnooze;
break;
}
}
void ScreenCaptureNotificationBlocker::OnIsCapturingDisplayChanged(
content::WebContents* web_contents,
bool is_capturing_display) {
if (is_capturing_display)
capturing_web_contents_.insert(web_contents);
else
capturing_web_contents_.erase(web_contents);
if (capturing_web_contents_.empty()) {
ReportSessionMetrics(/*revealed=*/false);
muted_notification_count_ = 0;
replaced_notification_count_ = 0;
closed_notification_count_ = 0;
snoozed_notification_count_ = 0;
reported_session_metrics_ = false;
state_ = NotifyState::kNotifyMuted;
last_screen_capture_session_start_time_ = base::TimeTicks();
CloseMuteNotification();
} else if (last_screen_capture_session_start_time_.is_null()) {
last_screen_capture_session_start_time_ = base::TimeTicks::Now();
}
NotifyBlockingStateChanged();
}
void ScreenCaptureNotificationBlocker::ReportSessionMetrics(bool revealed) {
auto elapsed_time =
base::TimeTicks::Now() - last_screen_capture_session_start_time_;
base::UmaHistogramLongTimes(
base::StrCat({"Notifications.Blocker.ScreenCapture.",
revealed ? "RevealDuration" : "SessionDuration"}),
elapsed_time);
if (reported_session_metrics_)
return;
RecordScreenCaptureCount("MutedCount", muted_notification_count_);
RecordScreenCaptureCount("ReplacedCount", replaced_notification_count_);
RecordScreenCaptureCount("ClosedCount", closed_notification_count_);
RecordScreenCaptureCount("SnoozedCount", snoozed_notification_count_);
reported_session_metrics_ = true;
}
void ScreenCaptureNotificationBlocker::DisplayMuteNotification() {
int total_notification_count =
muted_notification_count_ + replaced_notification_count_;
message_center::RichNotificationData rich_notification_data;
rich_notification_data.renotify = true;
if (base::FeatureList::IsEnabled(features::kMuteNotificationSnoozeAction)) {
rich_notification_data.buttons.emplace_back(
l10n_util::GetStringUTF16(IDS_NOTIFICATION_MUTED_ACTION_SNOOZE));
}
rich_notification_data.buttons.emplace_back(l10n_util::GetPluralStringFUTF16(
IDS_NOTIFICATION_MUTED_ACTION_SHOW, total_notification_count));
message_center::Notification notification(
message_center::NOTIFICATION_TYPE_SIMPLE, kMuteNotificationId,
l10n_util::GetPluralStringFUTF16(IDS_NOTIFICATION_MUTED_TITLE,
total_notification_count),
l10n_util::GetStringUTF16(IDS_NOTIFICATION_MUTED_MESSAGE),
/*icon=*/ui::ImageModel(),
/*display_source=*/std::u16string(),
/*origin_url=*/GURL(), message_center::NotifierId(),
rich_notification_data,
/*delegate=*/nullptr);
notification_display_service_->Display(
NotificationHandler::Type::NOTIFICATIONS_MUTED, notification,
/*metadata=*/nullptr);
last_mute_notification_time_ = base::TimeTicks::Now();
}
void ScreenCaptureNotificationBlocker::CloseMuteNotification() {
notification_display_service_->Close(
NotificationHandler::Type::NOTIFICATIONS_MUTED, kMuteNotificationId);
}