1 // Copyright 2017 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/notification_display_service_impl.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/feature_list.h"
12 #include "base/logging.h"
13 #include "build/build_config.h"
14 #include "build/buildflag.h"
15 #include "chrome/browser/browser_features.h"
16 #include "chrome/browser/notifications/non_persistent_notification_handler.h"
17 #include "chrome/browser/notifications/notification_display_service_factory.h"
18 #include "chrome/browser/notifications/persistent_notification_handler.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/updates/announcement_notification/announcement_notification_handler.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/pref_registry/pref_registry_syncable.h"
23 #include "components/prefs/pref_service.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "extensions/buildflags/buildflags.h"
26 #include "ui/message_center/public/cpp/notification.h"
27
28 #if BUILDFLAG(ENABLE_EXTENSIONS)
29 #include "chrome/browser/extensions/api/notifications/extension_notification_handler.h"
30 #endif
31
32 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD) || \
33 defined(OS_WIN)
34 #include "chrome/browser/send_tab_to_self/desktop_notification_handler.h"
35 #include "chrome/browser/sharing/sharing_notification_handler.h"
36 #endif
37
38 #if defined(OS_CHROMEOS)
39 #include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
40 #include "chrome/browser/nearby_sharing/nearby_notification_handler.h"
41 #endif
42
43 #if !defined(OS_ANDROID)
44 #include "chrome/browser/notifications/muted_notification_handler.h"
45 #include "chrome/browser/notifications/screen_capture_notification_blocker.h"
46 #endif
47
48 namespace {
49
OperationCompleted()50 void OperationCompleted() {
51 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
52 }
53
54 } // namespace
55
56 // static
GetForProfile(Profile * profile)57 NotificationDisplayServiceImpl* NotificationDisplayServiceImpl::GetForProfile(
58 Profile* profile) {
59 return static_cast<NotificationDisplayServiceImpl*>(
60 NotificationDisplayServiceFactory::GetForProfile(profile));
61 }
62
63 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)64 void NotificationDisplayServiceImpl::RegisterProfilePrefs(
65 user_prefs::PrefRegistrySyncable* registry) {
66 #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_BSD)
67 registry->RegisterBooleanPref(prefs::kAllowNativeNotifications, true);
68 #endif
69 }
70
NotificationDisplayServiceImpl(Profile * profile)71 NotificationDisplayServiceImpl::NotificationDisplayServiceImpl(Profile* profile)
72 : profile_(profile) {
73 // TODO(peter): Move these to the NotificationDisplayServiceFactory.
74 if (profile_) {
75 AddNotificationHandler(
76 NotificationHandler::Type::WEB_NON_PERSISTENT,
77 std::make_unique<NonPersistentNotificationHandler>());
78 AddNotificationHandler(NotificationHandler::Type::WEB_PERSISTENT,
79 std::make_unique<PersistentNotificationHandler>());
80
81 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_BSD) || \
82 defined(OS_WIN)
83 AddNotificationHandler(
84 NotificationHandler::Type::SEND_TAB_TO_SELF,
85 std::make_unique<send_tab_to_self::DesktopNotificationHandler>(
86 profile_));
87 #endif
88
89 #if BUILDFLAG(ENABLE_EXTENSIONS)
90 AddNotificationHandler(
91 NotificationHandler::Type::EXTENSION,
92 std::make_unique<extensions::ExtensionNotificationHandler>());
93 #endif
94
95 #if !defined(OS_ANDROID)
96 AddNotificationHandler(NotificationHandler::Type::SHARING,
97 std::make_unique<SharingNotificationHandler>());
98 AddNotificationHandler(NotificationHandler::Type::ANNOUNCEMENT,
99 std::make_unique<AnnouncementNotificationHandler>());
100
101 if (base::FeatureList::IsEnabled(
102 features::kMuteNotificationsDuringScreenShare)) {
103 auto screen_capture_blocker =
104 std::make_unique<ScreenCaptureNotificationBlocker>(this);
105 AddNotificationHandler(NotificationHandler::Type::NOTIFICATIONS_MUTED,
106 std::make_unique<MutedNotificationHandler>(
107 screen_capture_blocker.get()));
108 notification_queue_.AddNotificationBlocker(
109 std::move(screen_capture_blocker));
110 }
111
112 #endif
113
114 #if defined(OS_CHROMEOS)
115 if (base::FeatureList::IsEnabled(features::kNearbySharing)) {
116 AddNotificationHandler(NotificationHandler::Type::NEARBY_SHARE,
117 std::make_unique<NearbyNotificationHandler>());
118 }
119 #endif
120 }
121
122 bridge_delegator_ = std::make_unique<NotificationPlatformBridgeDelegator>(
123 profile_,
124 base::BindOnce(
125 &NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady,
126 weak_factory_.GetWeakPtr()));
127 }
128
~NotificationDisplayServiceImpl()129 NotificationDisplayServiceImpl::~NotificationDisplayServiceImpl() {
130 for (auto& obs : observers_)
131 obs.OnNotificationDisplayServiceDestroyed(this);
132 }
133
ProcessNotificationOperation(NotificationCommon::Operation operation,NotificationHandler::Type notification_type,const GURL & origin,const std::string & notification_id,const base::Optional<int> & action_index,const base::Optional<base::string16> & reply,const base::Optional<bool> & by_user)134 void NotificationDisplayServiceImpl::ProcessNotificationOperation(
135 NotificationCommon::Operation operation,
136 NotificationHandler::Type notification_type,
137 const GURL& origin,
138 const std::string& notification_id,
139 const base::Optional<int>& action_index,
140 const base::Optional<base::string16>& reply,
141 const base::Optional<bool>& by_user) {
142 NotificationHandler* handler = GetNotificationHandler(notification_type);
143 DCHECK(handler);
144 if (!handler) {
145 LOG(ERROR) << "Unable to find a handler for "
146 << static_cast<int>(notification_type);
147 return;
148 }
149
150 // TODO(crbug.com/766854): Plumb this through from the notification platform
151 // bridges so they can report completion of the operation as needed.
152 base::OnceClosure completed_closure = base::BindOnce(&OperationCompleted);
153
154 switch (operation) {
155 case NotificationCommon::OPERATION_CLICK:
156 handler->OnClick(profile_, origin, notification_id, action_index, reply,
157 std::move(completed_closure));
158 break;
159 case NotificationCommon::OPERATION_CLOSE:
160 DCHECK(by_user.has_value());
161 handler->OnClose(profile_, origin, notification_id, by_user.value(),
162 std::move(completed_closure));
163 for (auto& observer : observers_)
164 observer.OnNotificationClosed(notification_id);
165 break;
166 case NotificationCommon::OPERATION_DISABLE_PERMISSION:
167 handler->DisableNotifications(profile_, origin);
168 break;
169 case NotificationCommon::OPERATION_SETTINGS:
170 handler->OpenSettings(profile_, origin);
171 break;
172 }
173 }
174
AddNotificationHandler(NotificationHandler::Type notification_type,std::unique_ptr<NotificationHandler> handler)175 void NotificationDisplayServiceImpl::AddNotificationHandler(
176 NotificationHandler::Type notification_type,
177 std::unique_ptr<NotificationHandler> handler) {
178 DCHECK(handler);
179 DCHECK_EQ(notification_handlers_.count(notification_type), 0u);
180 notification_handlers_[notification_type] = std::move(handler);
181 }
182
GetNotificationHandler(NotificationHandler::Type notification_type)183 NotificationHandler* NotificationDisplayServiceImpl::GetNotificationHandler(
184 NotificationHandler::Type notification_type) {
185 auto found = notification_handlers_.find(notification_type);
186 if (found != notification_handlers_.end())
187 return found->second.get();
188 return nullptr;
189 }
190
Shutdown()191 void NotificationDisplayServiceImpl::Shutdown() {
192 bridge_delegator_->DisplayServiceShutDown();
193 }
194
Display(NotificationHandler::Type notification_type,const message_center::Notification & notification,std::unique_ptr<NotificationCommon::Metadata> metadata)195 void NotificationDisplayServiceImpl::Display(
196 NotificationHandler::Type notification_type,
197 const message_center::Notification& notification,
198 std::unique_ptr<NotificationCommon::Metadata> metadata) {
199 // TODO(estade): in the future, the reverse should also be true: a
200 // non-TRANSIENT type implies no delegate.
201 if (notification_type == NotificationHandler::Type::TRANSIENT)
202 DCHECK(notification.delegate());
203
204 CHECK(profile_ || notification_type == NotificationHandler::Type::TRANSIENT);
205
206 if (!bridge_delegator_initialized_) {
207 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Display,
208 weak_factory_.GetWeakPtr(), notification_type,
209 notification, std::move(metadata)));
210 return;
211 }
212
213 for (auto& observer : observers_)
214 observer.OnNotificationDisplayed(notification, metadata.get());
215
216 if (notification_queue_.ShouldEnqueueNotification(notification_type,
217 notification)) {
218 notification_queue_.EnqueueNotification(notification_type, notification,
219 std::move(metadata));
220 } else {
221 bridge_delegator_->Display(notification_type, notification,
222 std::move(metadata));
223 }
224
225 NotificationHandler* handler = GetNotificationHandler(notification_type);
226 if (handler)
227 handler->OnShow(profile_, notification.id());
228 }
229
Close(NotificationHandler::Type notification_type,const std::string & notification_id)230 void NotificationDisplayServiceImpl::Close(
231 NotificationHandler::Type notification_type,
232 const std::string& notification_id) {
233 CHECK(profile_ || notification_type == NotificationHandler::Type::TRANSIENT);
234
235 if (!bridge_delegator_initialized_) {
236 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Close,
237 weak_factory_.GetWeakPtr(), notification_type,
238 notification_id));
239 return;
240 }
241
242 notification_queue_.RemoveQueuedNotification(notification_id);
243
244 bridge_delegator_->Close(notification_type, notification_id);
245 }
246
GetDisplayed(DisplayedNotificationsCallback callback)247 void NotificationDisplayServiceImpl::GetDisplayed(
248 DisplayedNotificationsCallback callback) {
249 if (!bridge_delegator_initialized_) {
250 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::GetDisplayed,
251 weak_factory_.GetWeakPtr(),
252 std::move(callback)));
253 return;
254 }
255
256 bridge_delegator_->GetDisplayed(
257 base::BindOnce(&NotificationDisplayServiceImpl::OnGetDisplayed,
258 weak_factory_.GetWeakPtr(), std::move(callback)));
259 }
260
AddObserver(Observer * observer)261 void NotificationDisplayServiceImpl::AddObserver(Observer* observer) {
262 observers_.AddObserver(observer);
263 }
264
RemoveObserver(Observer * observer)265 void NotificationDisplayServiceImpl::RemoveObserver(Observer* observer) {
266 observers_.RemoveObserver(observer);
267 }
268
269 // Callback to run once the profile has been loaded in order to perform a
270 // given |operation| in a notification.
ProfileLoadedCallback(NotificationCommon::Operation operation,NotificationHandler::Type notification_type,const GURL & origin,const std::string & notification_id,const base::Optional<int> & action_index,const base::Optional<base::string16> & reply,const base::Optional<bool> & by_user,Profile * profile)271 void NotificationDisplayServiceImpl::ProfileLoadedCallback(
272 NotificationCommon::Operation operation,
273 NotificationHandler::Type notification_type,
274 const GURL& origin,
275 const std::string& notification_id,
276 const base::Optional<int>& action_index,
277 const base::Optional<base::string16>& reply,
278 const base::Optional<bool>& by_user,
279 Profile* profile) {
280 if (!profile) {
281 // TODO(miguelg): Add UMA for this condition.
282 // Perhaps propagate this through PersistentNotificationStatus.
283 LOG(WARNING) << "Profile not loaded correctly";
284 return;
285 }
286
287 NotificationDisplayServiceImpl* display_service =
288 NotificationDisplayServiceImpl::GetForProfile(profile);
289 display_service->ProcessNotificationOperation(operation, notification_type,
290 origin, notification_id,
291 action_index, reply, by_user);
292 }
293
SetBlockersForTesting(NotificationDisplayQueue::NotificationBlockers blockers)294 void NotificationDisplayServiceImpl::SetBlockersForTesting(
295 NotificationDisplayQueue::NotificationBlockers blockers) {
296 notification_queue_.SetNotificationBlockers(std::move(blockers));
297 }
298
299 void NotificationDisplayServiceImpl::
SetNotificationPlatformBridgeDelegatorForTesting(std::unique_ptr<NotificationPlatformBridgeDelegator> bridge_delegator)300 SetNotificationPlatformBridgeDelegatorForTesting(
301 std::unique_ptr<NotificationPlatformBridgeDelegator> bridge_delegator) {
302 bridge_delegator_ = std::move(bridge_delegator);
303 OnNotificationPlatformBridgeReady();
304 }
305
OverrideNotificationHandlerForTesting(NotificationHandler::Type notification_type,std::unique_ptr<NotificationHandler> handler)306 void NotificationDisplayServiceImpl::OverrideNotificationHandlerForTesting(
307 NotificationHandler::Type notification_type,
308 std::unique_ptr<NotificationHandler> handler) {
309 DCHECK(handler);
310 DCHECK_EQ(1u, notification_handlers_.count(notification_type));
311 notification_handlers_[notification_type] = std::move(handler);
312 }
313
OnNotificationPlatformBridgeReady()314 void NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady() {
315 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
316 bridge_delegator_initialized_ = true;
317
318 // Flush any pending actions that have yet to execute.
319 while (!actions_.empty()) {
320 std::move(actions_.front()).Run();
321 actions_.pop();
322 }
323 }
324
OnGetDisplayed(DisplayedNotificationsCallback callback,std::set<std::string> notification_ids,bool supports_synchronization)325 void NotificationDisplayServiceImpl::OnGetDisplayed(
326 DisplayedNotificationsCallback callback,
327 std::set<std::string> notification_ids,
328 bool supports_synchronization) {
329 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
330
331 std::set<std::string> queued = notification_queue_.GetQueuedNotificationIds();
332 notification_ids.insert(queued.begin(), queued.end());
333
334 std::move(callback).Run(std::move(notification_ids),
335 supports_synchronization);
336 }
337