message_center_impl.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. // Copyright (c) 2013 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. #include "ui/message_center/message_center_impl.h"
  5. #include <algorithm>
  6. #include <iterator>
  7. #include <memory>
  8. #include <utility>
  9. #include <vector>
  10. #include "base/auto_reset.h"
  11. #include "base/bind.h"
  12. #include "base/command_line.h"
  13. #include "base/containers/contains.h"
  14. #include "base/observer_list.h"
  15. #include "base/strings/string_util.h"
  16. #include "build/build_config.h"
  17. #include "build/chromeos_buildflags.h"
  18. #include "ui/message_center/lock_screen/lock_screen_controller.h"
  19. #include "ui/message_center/message_center_types.h"
  20. #include "ui/message_center/notification_blocker.h"
  21. #include "ui/message_center/notification_list.h"
  22. #include "ui/message_center/popup_timers_controller.h"
  23. #include "ui/message_center/public/cpp/message_center_constants.h"
  24. #include "ui/message_center/public/cpp/notification.h"
  25. #include "ui/message_center/public/cpp/notification_types.h"
  26. #include "ui/message_center/public/cpp/notifier_id.h"
  27. #if BUILDFLAG(IS_CHROMEOS_ASH)
  28. #include "ash/constants/ash_features.h"
  29. #endif
  30. namespace message_center {
  31. ////////////////////////////////////////////////////////////////////////////////
  32. // MessageCenterImpl
  33. MessageCenterImpl::MessageCenterImpl(
  34. std::unique_ptr<LockScreenController> lock_screen_controller)
  35. : lock_screen_controller_(std::move(lock_screen_controller)),
  36. popup_timers_controller_(std::make_unique<PopupTimersController>(this)),
  37. stats_collector_(this) {
  38. notification_list_ = std::make_unique<NotificationList>(this);
  39. #if BUILDFLAG(IS_CHROMEOS_ASH)
  40. notifications_grouping_enabled_ =
  41. ash::features::IsNotificationsRefreshEnabled();
  42. #endif
  43. }
  44. MessageCenterImpl::~MessageCenterImpl() = default;
  45. void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) {
  46. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  47. observer_list_.AddObserver(observer);
  48. }
  49. void MessageCenterImpl::RemoveObserver(MessageCenterObserver* observer) {
  50. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  51. observer_list_.RemoveObserver(observer);
  52. }
  53. void MessageCenterImpl::AddNotificationBlocker(NotificationBlocker* blocker) {
  54. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  55. if (base::Contains(blockers_, blocker))
  56. return;
  57. blocker->AddObserver(this);
  58. blockers_.push_back(blocker);
  59. }
  60. void MessageCenterImpl::RemoveNotificationBlocker(
  61. NotificationBlocker* blocker) {
  62. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  63. auto iter = std::find(blockers_.begin(), blockers_.end(), blocker);
  64. if (iter == blockers_.end())
  65. return;
  66. blocker->RemoveObserver(this);
  67. blockers_.erase(iter);
  68. }
  69. void MessageCenterImpl::OnBlockingStateChanged(NotificationBlocker* blocker) {
  70. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  71. std::list<std::string> blocked;
  72. NotificationList::PopupNotifications popups =
  73. notification_list_->GetPopupNotifications(blockers_, &blocked);
  74. visible_notifications_ =
  75. notification_list_->GetVisibleNotifications(blockers_);
  76. for (const std::string& notification_id : blocked) {
  77. for (MessageCenterObserver& observer : observer_list_)
  78. observer.OnNotificationUpdated(notification_id);
  79. }
  80. for (MessageCenterObserver& observer : observer_list_)
  81. observer.OnBlockingStateChanged(blocker);
  82. }
  83. void MessageCenterImpl::SetVisibility(Visibility visibility) {
  84. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  85. visible_ = (visibility == VISIBILITY_MESSAGE_CENTER);
  86. if (visible_) {
  87. std::set<std::string> updated_ids;
  88. notification_list_->SetNotificationsShown(blockers_, &updated_ids);
  89. for (const auto& id : updated_ids) {
  90. for (MessageCenterObserver& observer : observer_list_)
  91. observer.OnNotificationUpdated(id);
  92. }
  93. for (Notification* notification : GetPopupNotifications())
  94. MarkSinglePopupAsShown(notification->id(), false);
  95. }
  96. for (MessageCenterObserver& observer : observer_list_)
  97. observer.OnCenterVisibilityChanged(visibility);
  98. }
  99. bool MessageCenterImpl::IsMessageCenterVisible() const {
  100. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  101. return visible_;
  102. }
  103. void MessageCenterImpl::SetHasMessageCenterView(bool has_message_center_view) {
  104. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  105. has_message_center_view_ = has_message_center_view;
  106. }
  107. bool MessageCenterImpl::HasMessageCenterView() const {
  108. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  109. return has_message_center_view_;
  110. }
  111. size_t MessageCenterImpl::NotificationCount() const {
  112. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  113. return visible_notifications_.size();
  114. }
  115. bool MessageCenterImpl::HasPopupNotifications() const {
  116. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  117. return !IsMessageCenterVisible() &&
  118. notification_list_->HasPopupNotifications(blockers_);
  119. }
  120. bool MessageCenterImpl::IsQuietMode() const {
  121. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  122. return notification_list_->quiet_mode();
  123. }
  124. bool MessageCenterImpl::IsSpokenFeedbackEnabled() const {
  125. return spoken_feedback_enabled_;
  126. }
  127. Notification* MessageCenterImpl::FindNotificationById(const std::string& id) {
  128. return notification_list_->GetNotificationById(id);
  129. }
  130. Notification* MessageCenterImpl::FindParentNotification(
  131. Notification* notification) {
  132. // For a notification to have a parent notification, they must have identical
  133. // notifier_ids. To make sure that the notifications come from
  134. // the same website for the same user. Also make sure to only group
  135. // notifications from web pages with valid origin urls.
  136. if (notification->origin_url().is_empty() ||
  137. notification->notifier_id().type != NotifierType::WEB_PAGE) {
  138. return nullptr;
  139. }
  140. NotificationList::Notifications notifications =
  141. notification_list_->GetNotificationsByNotifierId(
  142. notification->notifier_id());
  143. // `notifications` keeps notifications ordered with the most recent one in the
  144. // front. If we have notifications for this notifier_id we return the last
  145. // notification..
  146. return notifications.size() ? *notifications.rbegin() : nullptr;
  147. }
  148. Notification* MessageCenterImpl::FindPopupNotificationById(
  149. const std::string& id) {
  150. auto id_match = [&id](Notification* notification) {
  151. return id == notification->id();
  152. };
  153. auto notifications = GetPopupNotifications();
  154. auto notification =
  155. std::find_if(notifications.begin(), notifications.end(), id_match);
  156. return notification == notifications.end() ? nullptr : *notification;
  157. }
  158. Notification* MessageCenterImpl::FindVisibleNotificationById(
  159. const std::string& id) {
  160. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  161. const auto& notifications = GetVisibleNotifications();
  162. for (Notification* notification : notifications) {
  163. if (notification->id() == id)
  164. return notification;
  165. }
  166. return nullptr;
  167. }
  168. NotificationList::Notifications MessageCenterImpl::FindNotificationsByAppId(
  169. const std::string& app_id) {
  170. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  171. return notification_list_->GetNotificationsByAppId(app_id);
  172. }
  173. NotificationList::Notifications MessageCenterImpl::GetNotifications() {
  174. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  175. return notification_list_->GetNotifications();
  176. }
  177. const NotificationList::Notifications&
  178. MessageCenterImpl::GetVisibleNotifications() {
  179. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  180. return visible_notifications_;
  181. }
  182. NotificationList::PopupNotifications
  183. MessageCenterImpl::GetPopupNotifications() {
  184. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  185. return notification_list_->GetPopupNotifications(blockers_, nullptr);
  186. }
  187. NotificationList::PopupNotifications
  188. MessageCenterImpl::GetPopupNotificationsWithoutBlocker(
  189. const NotificationBlocker& blocker) const {
  190. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  191. return notification_list_->GetPopupNotificationsWithoutBlocker(blockers_,
  192. blocker);
  193. }
  194. //------------------------------------------------------------------------------
  195. // Client code interface.
  196. void MessageCenterImpl::AddNotification(
  197. std::unique_ptr<Notification> notification) {
  198. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  199. DCHECK(notification);
  200. notification->set_allow_group(notifications_grouping_enabled_);
  201. const std::string id = notification->id();
  202. for (NotificationBlocker* blocker : blockers_)
  203. blocker->CheckState();
  204. auto* parent = FindParentNotification(notification.get());
  205. if (notification->allow_group() && parent && !notification->group_parent()) {
  206. parent->SetGroupParent();
  207. notification->SetGroupChild();
  208. }
  209. // Sometimes the notification can be added with the same id and the
  210. // |notification_list| will replace the notification instead of adding new.
  211. // This is essentially an update rather than addition.
  212. bool already_exists = notification_list_->GetNotificationById(id) != nullptr;
  213. if (already_exists) {
  214. UpdateNotification(id, std::move(notification));
  215. return;
  216. }
  217. notification_list_->AddNotification(std::move(notification));
  218. visible_notifications_ =
  219. notification_list_->GetVisibleNotifications(blockers_);
  220. for (MessageCenterObserver& observer : observer_list_)
  221. observer.OnNotificationAdded(id);
  222. }
  223. void MessageCenterImpl::UpdateNotification(
  224. const std::string& old_id,
  225. std::unique_ptr<Notification> new_notification) {
  226. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  227. for (NotificationBlocker* blocker : blockers_)
  228. blocker->CheckState();
  229. std::string new_id = new_notification->id();
  230. notification_list_->UpdateNotificationMessage(old_id,
  231. std::move(new_notification));
  232. visible_notifications_ =
  233. notification_list_->GetVisibleNotifications(blockers_);
  234. for (MessageCenterObserver& observer : observer_list_) {
  235. if (old_id == new_id) {
  236. observer.OnNotificationUpdated(new_id);
  237. } else {
  238. observer.OnNotificationRemoved(old_id, false);
  239. observer.OnNotificationAdded(new_id);
  240. }
  241. }
  242. }
  243. void MessageCenterImpl::RemoveNotification(const std::string& id,
  244. bool by_user) {
  245. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  246. Notification* notification = notification_list_->GetNotificationById(id);
  247. if (!notification)
  248. return;
  249. if (by_user && notification->pinned()) {
  250. // When pinned, a popup will not be removed completely but moved into the
  251. // message center bubble.
  252. MarkSinglePopupAsShown(id, true);
  253. return;
  254. }
  255. // In many cases |id| is a reference to an existing notification instance
  256. // but the instance can be destructed in this method. Hence copies the id
  257. // explicitly here.
  258. std::string copied_id(id);
  259. scoped_refptr<NotificationDelegate> delegate =
  260. notification_list_->GetNotificationDelegate(copied_id);
  261. // Remove notification before calling the Close method in case it calls
  262. // RemoveNotification reentrantly.
  263. notification_list_->RemoveNotification(copied_id);
  264. if (delegate.get())
  265. delegate->Close(by_user);
  266. visible_notifications_ =
  267. notification_list_->GetVisibleNotifications(blockers_);
  268. for (MessageCenterObserver& observer : observer_list_)
  269. observer.OnNotificationRemoved(copied_id, by_user);
  270. }
  271. void MessageCenterImpl::RemoveNotificationsForNotifierId(
  272. const NotifierId& notifier_id) {
  273. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  274. NotificationList::Notifications notifications =
  275. notification_list_->GetNotificationsByNotifierId(notifier_id);
  276. for (Notification* notification : notifications)
  277. RemoveNotification(notification->id(), false);
  278. if (!notifications.empty()) {
  279. visible_notifications_ =
  280. notification_list_->GetVisibleNotifications(blockers_);
  281. }
  282. }
  283. void MessageCenterImpl::RemoveAllNotifications(bool by_user, RemoveType type) {
  284. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  285. bool remove_pinned = (type == RemoveType::ALL);
  286. const NotificationBlockers& blockers =
  287. remove_pinned ? NotificationBlockers() /* empty blockers */
  288. : blockers_; /* use default blockers */
  289. const NotificationList::Notifications notifications =
  290. notification_list_->GetVisibleNotifications(blockers);
  291. std::set<std::string> ids;
  292. for (Notification* notification : notifications) {
  293. if (!remove_pinned && notification->pinned())
  294. continue;
  295. ids.insert(notification->id());
  296. scoped_refptr<NotificationDelegate> delegate = notification->delegate();
  297. // Remove notification before calling the Close method in case it calls
  298. // RemoveNotification reentrantly.
  299. notification_list_->RemoveNotification(notification->id());
  300. if (delegate.get())
  301. delegate->Close(by_user);
  302. }
  303. if (!ids.empty()) {
  304. visible_notifications_ =
  305. notification_list_->GetVisibleNotifications(blockers_);
  306. }
  307. for (const auto& id : ids) {
  308. for (MessageCenterObserver& observer : observer_list_)
  309. observer.OnNotificationRemoved(id, by_user);
  310. }
  311. }
  312. void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id,
  313. const ui::ImageModel& image) {
  314. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  315. if (notification_list_->SetNotificationIcon(notification_id, image)) {
  316. for (MessageCenterObserver& observer : observer_list_)
  317. observer.OnNotificationUpdated(notification_id);
  318. }
  319. }
  320. void MessageCenterImpl::SetNotificationImage(const std::string& notification_id,
  321. const gfx::Image& image) {
  322. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  323. if (notification_list_->SetNotificationImage(notification_id, image)) {
  324. for (MessageCenterObserver& observer : observer_list_)
  325. observer.OnNotificationUpdated(notification_id);
  326. }
  327. }
  328. void MessageCenterImpl::ClickOnNotification(const std::string& id) {
  329. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  330. if (!FindVisibleNotificationById(id))
  331. return;
  332. lock_screen_controller_->DismissLockScreenThenExecute(
  333. base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
  334. base::Unretained(this), id, absl::nullopt, absl::nullopt),
  335. base::OnceClosure());
  336. }
  337. void MessageCenterImpl::ClickOnNotificationButton(const std::string& id,
  338. int button_index) {
  339. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  340. if (!FindVisibleNotificationById(id))
  341. return;
  342. lock_screen_controller_->DismissLockScreenThenExecute(
  343. base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
  344. base::Unretained(this), id, button_index, absl::nullopt),
  345. base::OnceClosure());
  346. }
  347. void MessageCenterImpl::ClickOnNotificationButtonWithReply(
  348. const std::string& id,
  349. int button_index,
  350. const std::u16string& reply) {
  351. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  352. if (!FindVisibleNotificationById(id))
  353. return;
  354. lock_screen_controller_->DismissLockScreenThenExecute(
  355. base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
  356. base::Unretained(this), id, button_index, reply),
  357. base::OnceClosure());
  358. }
  359. void MessageCenterImpl::ClickOnNotificationUnlocked(
  360. const std::string& id,
  361. const absl::optional<int>& button_index,
  362. const absl::optional<std::u16string>& reply) {
  363. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  364. // This method must be called under unlocked screen.
  365. DCHECK(!lock_screen_controller_->IsScreenLocked());
  366. // Ensure the notification is still visible.
  367. if (!FindVisibleNotificationById(id))
  368. return;
  369. if (HasMessageCenterView() && HasPopupNotifications())
  370. MarkSinglePopupAsShown(id, true);
  371. for (MessageCenterObserver& observer : observer_list_)
  372. observer.OnNotificationClicked(id, button_index, reply);
  373. scoped_refptr<NotificationDelegate> delegate =
  374. notification_list_->GetNotificationDelegate(id);
  375. if (delegate)
  376. delegate->Click(button_index, reply);
  377. }
  378. void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) {
  379. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  380. Notification* notification = notification_list_->GetNotificationById(id);
  381. bool handled_by_delegate =
  382. notification &&
  383. (notification->rich_notification_data().settings_button_handler ==
  384. SettingsButtonHandler::DELEGATE);
  385. if (handled_by_delegate)
  386. notification->delegate()->SettingsClick();
  387. for (MessageCenterObserver& observer : observer_list_)
  388. observer.OnNotificationSettingsClicked(handled_by_delegate);
  389. }
  390. void MessageCenterImpl::DisableNotification(const std::string& id) {
  391. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  392. Notification* notification = notification_list_->GetNotificationById(id);
  393. if (notification && notification->delegate()) {
  394. notification->delegate()->DisableNotification();
  395. RemoveNotificationsForNotifierId(notification->notifier_id());
  396. }
  397. }
  398. void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id,
  399. bool mark_notification_as_read) {
  400. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  401. if (!FindVisibleNotificationById(id))
  402. return;
  403. if (HasMessageCenterView()) {
  404. notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read);
  405. for (MessageCenterObserver& observer : observer_list_) {
  406. observer.OnNotificationUpdated(id);
  407. observer.OnNotificationPopupShown(id, mark_notification_as_read);
  408. }
  409. } else {
  410. RemoveNotification(id, false);
  411. }
  412. }
  413. void MessageCenterImpl::ResetPopupTimer(const std::string& id) {
  414. DCHECK(FindPopupNotificationById(id));
  415. popup_timers_controller_->CancelTimer(id);
  416. popup_timers_controller_->StartTimer(
  417. id, popup_timers_controller_->GetTimeoutForNotification(
  418. FindPopupNotificationById(id)));
  419. }
  420. void MessageCenterImpl::ResetSinglePopup(const std::string& id) {
  421. notification_list_->ResetSinglePopup(id);
  422. for (MessageCenterObserver& observer : observer_list_) {
  423. observer.OnNotificationUpdated(id);
  424. }
  425. }
  426. void MessageCenterImpl::DisplayedNotification(const std::string& id,
  427. const DisplaySource source) {
  428. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  429. // This method may be called from the handlers, so we shouldn't manipulate
  430. // notifications in this method.
  431. if (!FindVisibleNotificationById(id))
  432. return;
  433. if (HasPopupNotifications())
  434. notification_list_->MarkSinglePopupAsDisplayed(id);
  435. scoped_refptr<NotificationDelegate> delegate =
  436. notification_list_->GetNotificationDelegate(id);
  437. for (MessageCenterObserver& observer : observer_list_)
  438. observer.OnNotificationDisplayed(id, source);
  439. }
  440. void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) {
  441. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  442. if (in_quiet_mode != notification_list_->quiet_mode()) {
  443. notification_list_->SetQuietMode(in_quiet_mode);
  444. for (MessageCenterObserver& observer : observer_list_)
  445. observer.OnQuietModeChanged(in_quiet_mode);
  446. }
  447. quiet_mode_timer_.Stop();
  448. }
  449. void MessageCenterImpl::SetSpokenFeedbackEnabled(bool enabled) {
  450. spoken_feedback_enabled_ = enabled;
  451. }
  452. void MessageCenterImpl::EnterQuietModeWithExpire(
  453. const base::TimeDelta& expires_in) {
  454. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  455. if (!quiet_mode_timer_.IsRunning()) {
  456. notification_list_->SetQuietMode(true);
  457. for (MessageCenterObserver& observer : observer_list_)
  458. observer.OnQuietModeChanged(true);
  459. }
  460. // This will restart the timer if it is already running.
  461. quiet_mode_timer_.Start(FROM_HERE, expires_in,
  462. base::BindOnce(&MessageCenterImpl::SetQuietMode,
  463. base::Unretained(this), false));
  464. }
  465. void MessageCenterImpl::RestartPopupTimers() {
  466. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  467. if (popup_timers_controller_)
  468. popup_timers_controller_->StartAll();
  469. }
  470. void MessageCenterImpl::PausePopupTimers() {
  471. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  472. if (popup_timers_controller_)
  473. popup_timers_controller_->PauseAll();
  474. }
  475. const std::u16string& MessageCenterImpl::GetSystemNotificationAppName() const {
  476. return system_notification_app_name_;
  477. }
  478. void MessageCenterImpl::SetSystemNotificationAppName(
  479. const std::u16string& name) {
  480. system_notification_app_name_ = name;
  481. }
  482. void MessageCenterImpl::OnMessageViewHovered(
  483. const std::string& notification_id) {
  484. for (MessageCenterObserver& observer : observer_list_)
  485. observer.OnMessageViewHovered(notification_id);
  486. }
  487. void MessageCenterImpl::DisableTimersForTest() {
  488. popup_timers_controller_.reset();
  489. }
  490. } // namespace message_center