blob: 0744469f9a471e80a5cddb9bb83773913984ebf2 [file] [log] [blame]
Peter Beverloo7426194d2017-12-11 17:39:381// 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/logging.h"
12#include "base/metrics/histogram_macros.h"
13#include "build/build_config.h"
14#include "build/buildflag.h"
15#include "chrome/browser/browser_process.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/notification_platform_bridge.h"
19#include "chrome/browser/notifications/persistent_notification_handler.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/common/chrome_features.h"
22#include "content/public/browser/browser_thread.h"
Evan Stade196ec042018-03-13 19:44:3623#include "ui/base/ui_base_features.h"
Evan Stade889ce4712018-01-28 15:26:2624#include "ui/message_center/public/cpp/notification.h"
Peter Beverloo7426194d2017-12-11 17:39:3825
26#if BUILDFLAG(ENABLE_EXTENSIONS)
27#include "chrome/browser/extensions/api/notifications/extension_notification_handler.h"
28#endif
29
30#if BUILDFLAG(ENABLE_MESSAGE_CENTER)
31#include "chrome/browser/notifications/notification_platform_bridge_message_center.h"
32#endif
33
34#if defined(OS_WIN)
35#include "base/strings/utf_string_conversions.h"
Xi Cheng23313de2018-02-24 04:15:3936#include "chrome/browser/notifications/notification_platform_bridge_win.h"
Peter Beverloo7426194d2017-12-11 17:39:3837#endif
38
39namespace {
40
41// Returns the NotificationPlatformBridge to use for the current platform.
42// Will return a nullptr for platforms that don't support native notifications.
43//
44// Platforms behave as follows:
45//
46// * Android
47// Always uses native notifications.
48//
49// * Mac OS X, Linux
50// Uses native notifications by default, but can fall back to the message
51// center if base::kNativeNotifications is disabled or initialization fails.
52//
53// * Windows 10 RS1+:
54// Uses the message center by default, but can use native notifications if
55// base::kNativeNotifications is enabled or initialization fails.
56//
57// * Chrome OS:
58// Always uses the message center, either through the message center
59// notification platform bridge when base::kNativeNotifications is disabled,
60// which means the message center runs in-process, or through the Chrome OS
61// specific bridge when the flag is enabled, which displays out-of-process.
62//
63// Please try to keep this comment up to date when changing behaviour on one of
64// the platforms supported by the browser.
65NotificationPlatformBridge* GetNativeNotificationPlatformBridge() {
66#if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
67#if defined(OS_ANDROID)
68 DCHECK(base::FeatureList::IsEnabled(features::kNativeNotifications));
69 return g_browser_process->notification_platform_bridge();
70#elif defined(OS_WIN)
Xi Cheng23313de2018-02-24 04:15:3971 if (NotificationPlatformBridgeWin::NativeNotificationEnabled())
Peter Beverloo7426194d2017-12-11 17:39:3872 return g_browser_process->notification_platform_bridge();
Evan Stade196ec042018-03-13 19:44:3673#elif defined(OS_CHROMEOS)
74 if (base::FeatureList::IsEnabled(features::kNativeNotifications) ||
75 base::FeatureList::IsEnabled(features::kMash)) {
76 return g_browser_process->notification_platform_bridge();
77 }
Peter Beverloo7426194d2017-12-11 17:39:3878#else
79 if (base::FeatureList::IsEnabled(features::kNativeNotifications) &&
80 g_browser_process->notification_platform_bridge()) {
81 return g_browser_process->notification_platform_bridge();
82 }
83#endif
84#endif // BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
85
86 // The platform does not support, or has not enabled, native notifications.
87 return nullptr;
88}
89
90// Returns the NotificationPlatformBridge to use for the message center. May be
91// a nullptr for platforms where the message center is not available.
92std::unique_ptr<NotificationPlatformBridge> CreateMessageCenterBridge(
93 Profile* profile) {
94#if BUILDFLAG(ENABLE_MESSAGE_CENTER)
95 return std::make_unique<NotificationPlatformBridgeMessageCenter>(profile);
96#else
97 return nullptr;
98#endif
99}
100
101std::string GetProfileId(Profile* profile) {
102#if defined(OS_WIN)
103 return base::WideToUTF8(profile->GetPath().BaseName().value());
104#elif defined(OS_POSIX)
105 return profile->GetPath().BaseName().value();
106#else
107#error "Not implemented for !OS_WIN && !OS_POSIX."
108#endif
109}
110
111void OperationCompleted() {
112 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
113}
114
115} // namespace
116
117// static
118NotificationDisplayServiceImpl* NotificationDisplayServiceImpl::GetForProfile(
119 Profile* profile) {
120 return static_cast<NotificationDisplayServiceImpl*>(
121 NotificationDisplayServiceFactory::GetForProfile(profile));
122}
123
124NotificationDisplayServiceImpl::NotificationDisplayServiceImpl(Profile* profile)
125 : profile_(profile),
126 message_center_bridge_(CreateMessageCenterBridge(profile)),
127 bridge_(GetNativeNotificationPlatformBridge()),
128 weak_factory_(this) {
129 // TODO(peter): Move these to the NotificationDisplayServiceFactory.
130 AddNotificationHandler(NotificationHandler::Type::WEB_NON_PERSISTENT,
131 std::make_unique<NonPersistentNotificationHandler>());
132 AddNotificationHandler(NotificationHandler::Type::WEB_PERSISTENT,
133 std::make_unique<PersistentNotificationHandler>());
134#if BUILDFLAG(ENABLE_EXTENSIONS)
135 AddNotificationHandler(
136 NotificationHandler::Type::EXTENSION,
137 std::make_unique<extensions::ExtensionNotificationHandler>());
138#endif
139
140 // Initialize the bridge if native notifications are available, otherwise
141 // signal that the bridge could not be initialized.
142 if (bridge_) {
143 bridge_->SetReadyCallback(base::BindOnce(
144 &NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady,
145 weak_factory_.GetWeakPtr()));
146 } else {
147 OnNotificationPlatformBridgeReady(false /* success */);
148 }
149}
150
151NotificationDisplayServiceImpl::~NotificationDisplayServiceImpl() = default;
152
153void NotificationDisplayServiceImpl::ProcessNotificationOperation(
154 NotificationCommon::Operation operation,
155 NotificationHandler::Type notification_type,
156 const GURL& origin,
157 const std::string& notification_id,
158 const base::Optional<int>& action_index,
159 const base::Optional<base::string16>& reply,
160 const base::Optional<bool>& by_user) {
161 NotificationHandler* handler = GetNotificationHandler(notification_type);
162 DCHECK(handler);
163 if (!handler) {
164 LOG(ERROR) << "Unable to find a handler for "
165 << static_cast<int>(notification_type);
166 return;
167 }
168
169 // TODO(crbug.com/766854): Plumb this through from the notification platform
170 // bridges so they can report completion of the operation as needed.
171 base::OnceClosure completed_closure = base::BindOnce(&OperationCompleted);
172
173 switch (operation) {
174 case NotificationCommon::CLICK:
175 handler->OnClick(profile_, origin, notification_id, action_index, reply,
176 std::move(completed_closure));
177 break;
178 case NotificationCommon::CLOSE:
179 DCHECK(by_user.has_value());
180 handler->OnClose(profile_, origin, notification_id, by_user.value(),
181 std::move(completed_closure));
182 break;
183 case NotificationCommon::DISABLE_PERMISSION:
184 handler->DisableNotifications(profile_, origin);
185 break;
186 case NotificationCommon::SETTINGS:
187 handler->OpenSettings(profile_, origin);
188 break;
189 }
190}
191
192void NotificationDisplayServiceImpl::AddNotificationHandler(
193 NotificationHandler::Type notification_type,
194 std::unique_ptr<NotificationHandler> handler) {
195 DCHECK(handler);
196 DCHECK_EQ(notification_handlers_.count(notification_type), 0u);
197 notification_handlers_[notification_type] = std::move(handler);
198}
199
200NotificationHandler* NotificationDisplayServiceImpl::GetNotificationHandler(
201 NotificationHandler::Type notification_type) {
202 auto found = notification_handlers_.find(notification_type);
203 if (found != notification_handlers_.end())
204 return found->second.get();
205 return nullptr;
206}
207
Peter Beverloo7426194d2017-12-11 17:39:38208void NotificationDisplayServiceImpl::Display(
209 NotificationHandler::Type notification_type,
210 const message_center::Notification& notification,
211 std::unique_ptr<NotificationCommon::Metadata> metadata) {
212 // TODO(estade): in the future, the reverse should also be true: a
213 // non-TRANSIENT type implies no delegate.
214 if (notification_type == NotificationHandler::Type::TRANSIENT)
215 DCHECK(notification.delegate());
216
217 if (!bridge_initialized_) {
218 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Display,
219 weak_factory_.GetWeakPtr(), notification_type,
220 notification, std::move(metadata)));
221 return;
222 }
223
224 NotificationPlatformBridge* bridge =
225 NotificationPlatformBridge::CanHandleType(notification_type)
226 ? bridge_
227 : message_center_bridge_.get();
228 DCHECK(bridge);
229
230 bridge->Display(notification_type, GetProfileId(profile_),
231 profile_->IsOffTheRecord(), notification,
232 std::move(metadata));
233
234 NotificationHandler* handler = GetNotificationHandler(notification_type);
235 if (handler)
236 handler->OnShow(profile_, notification.id());
237}
238
239void NotificationDisplayServiceImpl::Close(
240 NotificationHandler::Type notification_type,
241 const std::string& notification_id) {
242 if (!bridge_initialized_) {
243 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::Close,
244 weak_factory_.GetWeakPtr(), notification_type,
245 notification_id));
246 return;
247 }
248
249 NotificationPlatformBridge* bridge =
250 NotificationPlatformBridge::CanHandleType(notification_type)
251 ? bridge_
252 : message_center_bridge_.get();
253 DCHECK(bridge);
254
255 bridge->Close(GetProfileId(profile_), notification_id);
256}
257
258void NotificationDisplayServiceImpl::GetDisplayed(
259 const DisplayedNotificationsCallback& callback) {
260 if (!bridge_initialized_) {
261 actions_.push(base::BindOnce(&NotificationDisplayServiceImpl::GetDisplayed,
262 weak_factory_.GetWeakPtr(), callback));
263 return;
264 }
265
266 bridge_->GetDisplayed(GetProfileId(profile_), profile_->IsOffTheRecord(),
267 callback);
268}
269
Daniel Bratell1f65ad72018-01-22 16:12:38270// Callback to run once the profile has been loaded in order to perform a
271// given |operation| in a notification.
272void NotificationDisplayServiceImpl::ProfileLoadedCallback(
273 NotificationCommon::Operation operation,
274 NotificationHandler::Type notification_type,
275 const GURL& origin,
276 const std::string& notification_id,
277 const base::Optional<int>& action_index,
278 const base::Optional<base::string16>& reply,
279 const base::Optional<bool>& by_user,
280 Profile* profile) {
281 if (!profile) {
282 // TODO(miguelg): Add UMA for this condition.
283 // Perhaps propagate this through PersistentNotificationStatus.
284 LOG(WARNING) << "Profile not loaded correctly";
285 return;
286 }
287
288 NotificationDisplayServiceImpl* display_service =
289 NotificationDisplayServiceImpl::GetForProfile(profile);
290 display_service->ProcessNotificationOperation(operation, notification_type,
291 origin, notification_id,
292 action_index, reply, by_user);
293}
294
Peter Beverloo7426194d2017-12-11 17:39:38295void NotificationDisplayServiceImpl::OnNotificationPlatformBridgeReady(
296 bool success) {
Finnur Thorarinssonbcd84b822018-03-12 17:44:49297 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Peter Beverloo7426194d2017-12-11 17:39:38298 if (base::FeatureList::IsEnabled(features::kNativeNotifications)) {
299 UMA_HISTOGRAM_BOOLEAN("Notifications.UsingNativeNotificationCenter",
300 success);
301 }
302
303 if (!success) {
304 // Fall back to the message center if initialization failed. Initialization
305 // must always succeed on platforms where the message center is unavailable.
306 DCHECK(message_center_bridge_);
307 bridge_ = message_center_bridge_.get();
308 }
309
310 bridge_initialized_ = true;
311
312 // Flush any pending actions that have yet to execute.
313 while (!actions_.empty()) {
314 std::move(actions_.front()).Run();
315 actions_.pop();
316 }
317}