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       [&notification](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