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