1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/notifications/screen_capture_notification_blocker.h"
6
7 #include <algorithm>
8
9 #include "base/metrics/histogram_functions.h"
10 #include "base/strings/strcat.h"
11 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
12 #include "chrome/browser/notifications/notification_display_service.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "content/public/browser/web_contents.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/message_center/public/cpp/notification.h"
17 #include "url/gurl.h"
18 #include "url/origin.h"
19
20 namespace {
21
22 const char kMuteNotificationId[] = "notifications_muted";
23
24 // Suffix for a mute notification action. Should match suffix
25 // NotificationMuteAction in histogram_suffixes_list.xml.
MutedActionSuffix(MutedNotificationHandler::Action action)26 std::string MutedActionSuffix(MutedNotificationHandler::Action action) {
27 switch (action) {
28 case MutedNotificationHandler::Action::kUserClose:
29 return "Close";
30 case MutedNotificationHandler::Action::kBodyClick:
31 return "Body";
32 case MutedNotificationHandler::Action::kShowClick:
33 return "Show";
34 }
35 }
36
RecordScreenCaptureCount(const std::string & suffix,int count)37 void RecordScreenCaptureCount(const std::string& suffix, int count) {
38 base::UmaHistogramCounts100(
39 base::StrCat({"Notifications.Blocker.ScreenCapture.", suffix}), count);
40 }
41
42 } // namespace
43
ScreenCaptureNotificationBlocker(NotificationDisplayService * notification_display_service)44 ScreenCaptureNotificationBlocker::ScreenCaptureNotificationBlocker(
45 NotificationDisplayService* notification_display_service)
46 : notification_display_service_(notification_display_service) {
47 DCHECK(notification_display_service_);
48 observer_.Add(MediaCaptureDevicesDispatcher::GetInstance()
49 ->GetMediaStreamCaptureIndicator()
50 .get());
51 }
52
53 ScreenCaptureNotificationBlocker::~ScreenCaptureNotificationBlocker() = default;
54
ShouldBlockNotification(const message_center::Notification & notification)55 bool ScreenCaptureNotificationBlocker::ShouldBlockNotification(
56 const message_center::Notification& notification) {
57 // Don't block if the user clicked on "Show" for the current session.
58 if (state_ == NotifyState::kShowAll)
59 return false;
60
61 // Don't block if no WebContents currently captures the screen.
62 if (capturing_web_contents_.empty())
63 return false;
64
65 // Otherwise block all notifications that belong to non-capturing origins.
66 return std::none_of(
67 capturing_web_contents_.begin(), capturing_web_contents_.end(),
68 [¬ification](content::WebContents* web_contents) {
69 return url::IsSameOriginWith(notification.origin_url(),
70 web_contents->GetLastCommittedURL());
71 });
72 }
73
OnBlockedNotification(const message_center::Notification & notification,bool replaced)74 void ScreenCaptureNotificationBlocker::OnBlockedNotification(
75 const message_center::Notification& notification,
76 bool replaced) {
77 if (replaced)
78 ++replaced_notification_count_;
79 else
80 ++muted_notification_count_;
81
82 if (state_ == NotifyState::kNotifyMuted)
83 DisplayMuteNotification();
84 }
85
OnClosedNotification(const message_center::Notification & notification)86 void ScreenCaptureNotificationBlocker::OnClosedNotification(
87 const message_center::Notification& notification) {
88 ++closed_notification_count_;
89 }
90
OnAction(MutedNotificationHandler::Action action)91 void ScreenCaptureNotificationBlocker::OnAction(
92 MutedNotificationHandler::Action action) {
93 DCHECK(state_ == NotifyState::kNotifyMuted);
94 CloseMuteNotification();
95 ReportMuteNotificationAction(action);
96
97 switch (action) {
98 case MutedNotificationHandler::Action::kUserClose:
99 case MutedNotificationHandler::Action::kBodyClick:
100 // Nothing to do here.
101 break;
102 case MutedNotificationHandler::Action::kShowClick:
103 state_ = NotifyState::kShowAll;
104 NotifyBlockingStateChanged();
105 ReportSessionMetrics();
106 break;
107 }
108 }
109
OnIsCapturingDisplayChanged(content::WebContents * web_contents,bool is_capturing_display)110 void ScreenCaptureNotificationBlocker::OnIsCapturingDisplayChanged(
111 content::WebContents* web_contents,
112 bool is_capturing_display) {
113 if (is_capturing_display)
114 capturing_web_contents_.insert(web_contents);
115 else
116 capturing_web_contents_.erase(web_contents);
117
118 if (capturing_web_contents_.empty()) {
119 ReportSessionMetrics();
120 muted_notification_count_ = 0;
121 replaced_notification_count_ = 0;
122 closed_notification_count_ = 0;
123 reported_session_metrics_ = false;
124 state_ = NotifyState::kNotifyMuted;
125 CloseMuteNotification();
126 }
127
128 NotifyBlockingStateChanged();
129 }
130
ReportSessionMetrics()131 void ScreenCaptureNotificationBlocker::ReportSessionMetrics() {
132 if (reported_session_metrics_)
133 return;
134
135 RecordScreenCaptureCount("MutedCount", muted_notification_count_);
136 RecordScreenCaptureCount("ReplacedCount", replaced_notification_count_);
137 RecordScreenCaptureCount("ClosedCount", closed_notification_count_);
138
139 reported_session_metrics_ = true;
140 }
141
ReportMuteNotificationAction(MutedNotificationHandler::Action action)142 void ScreenCaptureNotificationBlocker::ReportMuteNotificationAction(
143 MutedNotificationHandler::Action action) {
144 RecordScreenCaptureCount(
145 base::StrCat({"Action.", MutedActionSuffix(action)}),
146 muted_notification_count_ + replaced_notification_count_);
147 }
148
DisplayMuteNotification()149 void ScreenCaptureNotificationBlocker::DisplayMuteNotification() {
150 int total_notification_count =
151 muted_notification_count_ + replaced_notification_count_;
152
153 message_center::RichNotificationData rich_notification_data;
154 rich_notification_data.renotify = true;
155 rich_notification_data.buttons.emplace_back(l10n_util::GetPluralStringFUTF16(
156 IDS_NOTIFICATION_MUTED_ACTION_SHOW, total_notification_count));
157
158 message_center::Notification notification(
159 message_center::NOTIFICATION_TYPE_SIMPLE, kMuteNotificationId,
160 l10n_util::GetPluralStringFUTF16(IDS_NOTIFICATION_MUTED_TITLE,
161 total_notification_count),
162 l10n_util::GetStringUTF16(IDS_NOTIFICATION_MUTED_MESSAGE),
163 /*icon=*/gfx::Image(),
164 /*display_source=*/base::string16(),
165 /*origin_url=*/GURL(), message_center::NotifierId(),
166 rich_notification_data,
167 /*delegate=*/nullptr);
168
169 notification_display_service_->Display(
170 NotificationHandler::Type::NOTIFICATIONS_MUTED, notification,
171 /*metadata=*/nullptr);
172 }
173
CloseMuteNotification()174 void ScreenCaptureNotificationBlocker::CloseMuteNotification() {
175 notification_display_service_->Close(
176 NotificationHandler::Type::NOTIFICATIONS_MUTED, kMuteNotificationId);
177 }
178