// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/message_center/message_center_impl.h" #include #include #include #include "base/command_line.h" #include "base/macros.h" #include "base/memory/scoped_vector.h" #include "base/observer_list.h" #include "base/stl_util.h" #include "build/build_config.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/message_center_switches.h" #include "ui/message_center/message_center_types.h" #include "ui/message_center/notification.h" #include "ui/message_center/notification_blocker.h" #include "ui/message_center/notification_list.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/popup_timer.h" namespace message_center { namespace internal { // ChangeQueue keeps track of all the changes that we need to make to the // notification list once the visibility is set to VISIBILITY_TRANSIENT. class ChangeQueue { public: enum ChangeType { CHANGE_TYPE_ADD = 0, CHANGE_TYPE_UPDATE, CHANGE_TYPE_DELETE }; // Change represents an operation made on a notification. Since it contains // the final state of the notification, we only keep the last change for a // particular notification that is in the notification list around. There are // two ids; |id_| is the newest notification id that has been assigned by an // update, and |notification_list_id_| is the id of the notification it should // be updating as it exists in the notification list. class Change { public: Change(ChangeType type, const std::string& id, scoped_ptr notification); ~Change(); // Used to transfer ownership of the contained notification. scoped_ptr PassNotification(); Notification* notification() const { return notification_.get(); } // Returns the post-update ID. It means: // - ADD event: ID of the notification to be added. // - UPDATE event: ID of the notification after the change. If the change // doesn't update its ID, this value is same as |notification_list_id|. // - DELETE event: ID of the notification to be deleted. const std::string& id() const { return id_; } ChangeType type() const { return type_; } bool by_user() const { return by_user_; } void set_by_user(bool by_user) { by_user_ = by_user; } // Returns the ID which is used in the notification list. In other word, it // means the ID before the change. const std::string& notification_list_id() const { return notification_list_id_; } void set_type(const ChangeType new_type) { type_ = new_type; } void ReplaceNotification(scoped_ptr new_notification); private: ChangeType type_; std::string id_; std::string notification_list_id_; bool by_user_; scoped_ptr notification_; DISALLOW_COPY_AND_ASSIGN(Change); }; ChangeQueue(); ~ChangeQueue(); // Called when the message center has appropriate visibility. Modifies // |message_center| but does not retain it. This also causes the queue to // empty itself. void ApplyChanges(MessageCenterImpl* message_center); // Applies only the changes of the given ID. void ApplyChangesForId(MessageCenterImpl* message_center, const std::string& id); // Causes a TYPE_ADD change to be added to the queue. void AddNotification(scoped_ptr notification); // Causes a TYPE_UPDATE change to be added to the queue. void UpdateNotification(const std::string& old_id, scoped_ptr notification); // Causes a TYPE_DELETE change to be added to the queue. void EraseNotification(const std::string& id, bool by_user); // Returns whether the queue matches an id. The id given will be matched // against the ID of all changes post-update, not the id of the notification // as it stands in the notification list. bool Has(const std::string& id) const; // Returns a Change that can be modified by the caller. ChangeQueue retains // ownership of the Change; pointers should not be retained. Notification* GetLatestNotification(const std::string& id) const; private: void ApplyChangeInternal(MessageCenterImpl* message_center, scoped_ptr change); ScopedVector changes_; DISALLOW_COPY_AND_ASSIGN(ChangeQueue); }; //////////////////////////////////////////////////////////////////////////////// // ChangeFinder struct ChangeFinder { explicit ChangeFinder(const std::string& id) : id(id) {} bool operator()(ChangeQueue::Change* change) { return change->id() == id; } std::string id; }; //////////////////////////////////////////////////////////////////////////////// // ChangeQueue::Change ChangeQueue::Change::Change(ChangeType type, const std::string& id, scoped_ptr notification) : type_(type), notification_list_id_(id), by_user_(false), notification_(std::move(notification)) { DCHECK(!id.empty()); DCHECK(type != CHANGE_TYPE_DELETE || notification_.get() == NULL); id_ = notification_ ? notification_->id() : notification_list_id_; } ChangeQueue::Change::~Change() {} scoped_ptr ChangeQueue::Change::PassNotification() { return std::move(notification_); } void ChangeQueue::Change::ReplaceNotification( scoped_ptr new_notification) { id_ = new_notification ? new_notification->id() : notification_list_id_; notification_.swap(new_notification); } //////////////////////////////////////////////////////////////////////////////// // ChangeQueue ChangeQueue::ChangeQueue() {} ChangeQueue::~ChangeQueue() {} void ChangeQueue::ApplyChanges(MessageCenterImpl* message_center) { // This method is re-entrant. while (!changes_.empty()) { ScopedVector::iterator iter = changes_.begin(); scoped_ptr change(*iter); // TODO(dewittj): Replace changes_ with a deque. changes_.weak_erase(iter); ApplyChangeInternal(message_center, std::move(change)); } } void ChangeQueue::ApplyChangesForId(MessageCenterImpl* message_center, const std::string& id) { std::deque changes_for_id; std::string interesting_id = id; // Traverses the queue in reverse so shat we can track changes which change // the notification's ID. ScopedVector::iterator iter = changes_.end(); while (iter != changes_.begin()) { --iter; if (interesting_id != (*iter)->id()) continue; scoped_ptr change(*iter); interesting_id = change->notification_list_id(); iter = changes_.weak_erase(iter); changes_for_id.push_back(change.release()); } while (!changes_for_id.empty()) { ApplyChangeInternal( message_center, scoped_ptr(changes_for_id.back())); changes_for_id.pop_back(); } } void ChangeQueue::AddNotification(scoped_ptr notification) { std::string id = notification->id(); changes_.push_back(new Change(CHANGE_TYPE_ADD, id, std::move(notification))); } void ChangeQueue::UpdateNotification(const std::string& old_id, scoped_ptr notification) { ScopedVector::reverse_iterator iter = std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(old_id)); if (iter == changes_.rend()) { changes_.push_back( new Change(CHANGE_TYPE_UPDATE, old_id, std::move(notification))); return; } Change* change = *iter; switch (change->type()) { case CHANGE_TYPE_ADD: { std::string id = notification->id(); // Needs to add the change at the last, because if this change updates // its ID, some previous changes may affect new ID. // (eg. Add A, Update B->C, and This update A->B). changes_.erase(--(iter.base())); changes_.push_back( new Change(CHANGE_TYPE_ADD, id, std::move(notification))); break; } case CHANGE_TYPE_UPDATE: if (notification->id() == old_id) { // Safe to place the change at the previous place. change->ReplaceNotification(std::move(notification)); } else if (change->id() == change->notification_list_id()) { std::string id = notification->id(); // Safe to place the change at the last. changes_.erase(--(iter.base())); changes_.push_back( new Change(CHANGE_TYPE_ADD, id, std::move(notification))); } else { // Complex case: gives up to optimize. changes_.push_back( new Change(CHANGE_TYPE_UPDATE, old_id, std::move(notification))); } break; case CHANGE_TYPE_DELETE: // DELETE -> UPDATE. Something is wrong. Treats the UPDATE as ADD. changes_.push_back( new Change(CHANGE_TYPE_ADD, old_id, std::move(notification))); break; default: NOTREACHED(); } } void ChangeQueue::EraseNotification(const std::string& id, bool by_user) { ScopedVector::reverse_iterator iter = std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(id)); if (iter == changes_.rend()) { scoped_ptr change(new Change(CHANGE_TYPE_DELETE, id, nullptr)); change->set_by_user(by_user); changes_.push_back(std::move(change)); return; } Change* change = *iter; switch (change->type()) { case CHANGE_TYPE_ADD: // ADD -> DELETE. Just removes both. changes_.erase(--(iter.base())); break; case CHANGE_TYPE_UPDATE: // UPDATE -> DELETE. Changes the previous UPDATE to DELETE. change->set_type(CHANGE_TYPE_DELETE); change->set_by_user(by_user); change->ReplaceNotification(nullptr); break; case CHANGE_TYPE_DELETE: // DELETE -> DELETE. Something is wrong. Combines them with overriding // the |by_user| flag. change->set_by_user(!change->by_user() && by_user); break; default: NOTREACHED(); } } bool ChangeQueue::Has(const std::string& id) const { ScopedVector::const_iterator iter = std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id)); return iter != changes_.end(); } Notification* ChangeQueue::GetLatestNotification(const std::string& id) const { ScopedVector::const_iterator iter = std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id)); if (iter == changes_.end()) return NULL; return (*iter)->notification(); } void ChangeQueue::ApplyChangeInternal(MessageCenterImpl* message_center, scoped_ptr change) { switch (change->type()) { case CHANGE_TYPE_ADD: message_center->AddNotificationImmediately(change->PassNotification()); break; case CHANGE_TYPE_UPDATE: message_center->UpdateNotificationImmediately( change->notification_list_id(), change->PassNotification()); break; case CHANGE_TYPE_DELETE: message_center->RemoveNotificationImmediately( change->notification_list_id(), change->by_user()); break; default: NOTREACHED(); } } } // namespace internal //////////////////////////////////////////////////////////////////////////////// // MessageCenterImpl::NotificationCache MessageCenterImpl::NotificationCache::NotificationCache() : unread_count(0) {} MessageCenterImpl::NotificationCache::~NotificationCache() {} void MessageCenterImpl::NotificationCache::Rebuild( const NotificationList::Notifications& notifications) { visible_notifications = notifications; RecountUnread(); } void MessageCenterImpl::NotificationCache::RecountUnread() { unread_count = 0; for (const auto& notification : visible_notifications) { if (!notification->IsRead()) ++unread_count; } } //////////////////////////////////////////////////////////////////////////////// // MessageCenterImpl MessageCenterImpl::MessageCenterImpl() : MessageCenter(), popup_timers_controller_(new PopupTimersController(this)), settings_provider_(NULL) { notification_list_.reset(new NotificationList()); bool enable_message_center_changes_while_open = true; // enable by default std::string arg = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kMessageCenterChangesWhileOpen); if (!arg.empty()) { if (arg == "enabled") enable_message_center_changes_while_open = true; else if (arg == "disabled") enable_message_center_changes_while_open = false; } if (!enable_message_center_changes_while_open) notification_queue_.reset(new internal::ChangeQueue()); } MessageCenterImpl::~MessageCenterImpl() { SetNotifierSettingsProvider(NULL); } void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) { observer_list_.AddObserver(observer); } void MessageCenterImpl::RemoveObserver(MessageCenterObserver* observer) { observer_list_.RemoveObserver(observer); } void MessageCenterImpl::AddNotificationBlocker(NotificationBlocker* blocker) { if (ContainsValue(blockers_, blocker)) return; blocker->AddObserver(this); blockers_.push_back(blocker); } void MessageCenterImpl::RemoveNotificationBlocker( NotificationBlocker* blocker) { std::vector::iterator iter = std::find(blockers_.begin(), blockers_.end(), blocker); if (iter == blockers_.end()) return; blocker->RemoveObserver(this); blockers_.erase(iter); } void MessageCenterImpl::OnBlockingStateChanged(NotificationBlocker* blocker) { std::list blocked_ids; NotificationList::PopupNotifications popups = notification_list_->GetPopupNotifications(blockers_, &blocked_ids); for (const auto& id : blocked_ids) { // Do not call MessageCenterImpl::MarkSinglePopupAsShown() directly here // just for performance reason. MessageCenterImpl::MarkSinglePopupAsShown() // calls NotificationList::MarkSinglePopupAsShown() and then updates the // unread count, but the whole cache will be recreated below. notification_list_->MarkSinglePopupAsShown(id, true); } notification_cache_.Rebuild( notification_list_->GetVisibleNotifications(blockers_)); for (const auto& id : blocked_ids) { FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationUpdated(id)); } FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnBlockingStateChanged(blocker)); } void MessageCenterImpl::UpdateIconImage( const NotifierId& notifier_id, const gfx::Image& icon) {} void MessageCenterImpl::NotifierGroupChanged() {} void MessageCenterImpl::NotifierEnabledChanged( const NotifierId& notifier_id, bool enabled) { if (!enabled) { RemoveNotificationsForNotifierId(notifier_id); } } void MessageCenterImpl::SetVisibility(Visibility visibility) { std::set updated_ids; notification_list_->SetMessageCenterVisible( (visibility == VISIBILITY_MESSAGE_CENTER), &updated_ids); notification_cache_.RecountUnread(); for (const auto& id : updated_ids) { FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationUpdated(id)); } if (notification_queue_ && visibility == VISIBILITY_TRANSIENT) { notification_queue_->ApplyChanges(this); } FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnCenterVisibilityChanged(visibility)); } bool MessageCenterImpl::IsMessageCenterVisible() const { return notification_list_->is_message_center_visible(); } size_t MessageCenterImpl::NotificationCount() const { return notification_cache_.visible_notifications.size(); } size_t MessageCenterImpl::UnreadNotificationCount() const { return notification_cache_.unread_count; } bool MessageCenterImpl::HasPopupNotifications() const { return !IsMessageCenterVisible() && notification_list_->HasPopupNotifications(blockers_); } bool MessageCenterImpl::IsQuietMode() const { return notification_list_->quiet_mode(); } bool MessageCenterImpl::HasClickedListener(const std::string& id) { scoped_refptr delegate = notification_list_->GetNotificationDelegate(id); return delegate.get() && delegate->HasClickedListener(); } message_center::Notification* MessageCenterImpl::FindVisibleNotificationById( const std::string& id) { return notification_list_->GetNotificationById(id); } const NotificationList::Notifications& MessageCenterImpl::GetVisibleNotifications() { return notification_cache_.visible_notifications; } NotificationList::PopupNotifications MessageCenterImpl::GetPopupNotifications() { return notification_list_->GetPopupNotifications(blockers_, NULL); } void MessageCenterImpl::ForceNotificationFlush(const std::string& id) { if (notification_queue_) notification_queue_->ApplyChangesForId(this, id); } //------------------------------------------------------------------------------ // Client code interface. void MessageCenterImpl::AddNotification(scoped_ptr notification) { DCHECK(notification); const std::string id = notification->id(); for (size_t i = 0; i < blockers_.size(); ++i) blockers_[i]->CheckState(); if (notification_queue_ && notification_list_->is_message_center_visible()) { notification_queue_->AddNotification(std::move(notification)); return; } AddNotificationImmediately(std::move(notification)); } void MessageCenterImpl::AddNotificationImmediately( scoped_ptr notification) { const std::string id = notification->id(); // Sometimes the notification can be added with the same id and the // |notification_list| will replace the notification instead of adding new. // This is essentially an update rather than addition. bool already_exists = (notification_list_->GetNotificationById(id) != NULL); notification_list_->AddNotification(std::move(notification)); notification_cache_.Rebuild( notification_list_->GetVisibleNotifications(blockers_)); if (already_exists) { FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationUpdated(id)); } else { FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationAdded(id)); } } void MessageCenterImpl::UpdateNotification( const std::string& old_id, scoped_ptr new_notification) { for (size_t i = 0; i < blockers_.size(); ++i) blockers_[i]->CheckState(); if (notification_queue_ && notification_list_->is_message_center_visible()) { // We will allow notifications that are progress types (and stay progress // types) to be updated even if the message center is open. There are 3 // requirements here: // * Notification of type PROGRESS exists with same ID in the center // * There are no queued updates for this notification (they imply a change // that violates the PROGRESS invariant // * The new notification is type PROGRESS. // TODO(dewittj): Ensure this works when the ID is changed by the caller. // This shouldn't be an issue in practice since only W3C notifications // change the ID on update, and they don't have progress type notifications. bool update_keeps_progress_type = new_notification->type() == NOTIFICATION_TYPE_PROGRESS && !notification_queue_->Has(old_id) && notification_list_->HasNotificationOfType(old_id, NOTIFICATION_TYPE_PROGRESS); if (!update_keeps_progress_type) { // Updates are allowed only for progress notifications. notification_queue_->UpdateNotification(old_id, std::move(new_notification)); return; } } UpdateNotificationImmediately(old_id, std::move(new_notification)); } void MessageCenterImpl::UpdateNotificationImmediately( const std::string& old_id, scoped_ptr new_notification) { std::string new_id = new_notification->id(); notification_list_->UpdateNotificationMessage(old_id, std::move(new_notification)); notification_cache_.Rebuild( notification_list_->GetVisibleNotifications(blockers_)); if (old_id == new_id) { FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationUpdated(new_id)); } else { FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationRemoved(old_id, false)); FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationAdded(new_id)); } } void MessageCenterImpl::RemoveNotification(const std::string& id, bool by_user) { if (notification_queue_ && !by_user && notification_list_->is_message_center_visible()) { notification_queue_->EraseNotification(id, by_user); return; } RemoveNotificationImmediately(id, by_user); } void MessageCenterImpl::RemoveNotificationImmediately( const std::string& id, bool by_user) { if (FindVisibleNotificationById(id) == NULL) return; // In many cases |id| is a reference to an existing notification instance // but the instance can be destructed in this method. Hence copies the id // explicitly here. std::string copied_id(id); scoped_refptr delegate = notification_list_->GetNotificationDelegate(copied_id); if (delegate.get()) delegate->Close(by_user); notification_list_->RemoveNotification(copied_id); notification_cache_.Rebuild( notification_list_->GetVisibleNotifications(blockers_)); FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationRemoved(copied_id, by_user)); } void MessageCenterImpl::RemoveNotificationsForNotifierId( const NotifierId& notifier_id) { NotificationList::Notifications notifications = notification_list_->GetNotificationsByNotifierId(notifier_id); for (const auto& notification : notifications) RemoveNotification(notification->id(), false); if (!notifications.empty()) { notification_cache_.Rebuild( notification_list_->GetVisibleNotifications(blockers_)); } } void MessageCenterImpl::RemoveAllNotifications(bool by_user) { // Using not |blockers_| but an empty list since it wants to remove literally // all notifications. RemoveNotifications(by_user, NotificationBlockers()); } void MessageCenterImpl::RemoveAllVisibleNotifications(bool by_user) { RemoveNotifications(by_user, blockers_); } void MessageCenterImpl::RemoveNotifications( bool by_user, const NotificationBlockers& blockers) { const NotificationList::Notifications notifications = notification_list_->GetVisibleNotifications(blockers); std::set ids; for (const auto& notification : notifications) { ids.insert(notification->id()); scoped_refptr delegate = notification->delegate(); if (delegate.get()) delegate->Close(by_user); notification_list_->RemoveNotification(notification->id()); } if (!ids.empty()) { notification_cache_.Rebuild( notification_list_->GetVisibleNotifications(blockers_)); } for (const auto& id : ids) { FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationRemoved(id, by_user)); } } void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id, const gfx::Image& image) { bool updated = false; Notification* queue_notification = notification_queue_ ? notification_queue_->GetLatestNotification(notification_id) : NULL; if (queue_notification) { queue_notification->set_icon(image); updated = true; } else { updated = notification_list_->SetNotificationIcon(notification_id, image); } if (updated) { FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationUpdated(notification_id)); } } void MessageCenterImpl::SetNotificationImage(const std::string& notification_id, const gfx::Image& image) { bool updated = false; Notification* queue_notification = notification_queue_ ? notification_queue_->GetLatestNotification(notification_id) : NULL; if (queue_notification) { queue_notification->set_image(image); updated = true; } else { updated = notification_list_->SetNotificationImage(notification_id, image); } if (updated) { FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationUpdated(notification_id)); } } void MessageCenterImpl::SetNotificationButtonIcon( const std::string& notification_id, int button_index, const gfx::Image& image) { bool updated = false; Notification* queue_notification = notification_queue_ ? notification_queue_->GetLatestNotification(notification_id) : NULL; if (queue_notification) { queue_notification->SetButtonIcon(button_index, image); updated = true; } else { updated = notification_list_->SetNotificationButtonIcon( notification_id, button_index, image); } if (updated) { FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationUpdated(notification_id)); } } void MessageCenterImpl::DisableNotificationsByNotifier( const NotifierId& notifier_id) { if (settings_provider_) { // TODO(mukai): SetNotifierEnabled can just accept notifier_id? Notifier notifier(notifier_id, base::string16(), true); settings_provider_->SetNotifierEnabled(notifier, false); // The settings provider will call back to remove the notifications // belonging to the notifier id. } else { RemoveNotificationsForNotifierId(notifier_id); } } void MessageCenterImpl::ClickOnNotification(const std::string& id) { if (FindVisibleNotificationById(id) == NULL) return; #if defined(OS_CHROMEOS) if (HasPopupNotifications()) MarkSinglePopupAsShown(id, true); #endif scoped_refptr delegate = notification_list_->GetNotificationDelegate(id); if (delegate.get()) delegate->Click(); FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationClicked(id)); } void MessageCenterImpl::ClickOnNotificationButton(const std::string& id, int button_index) { if (FindVisibleNotificationById(id) == NULL) return; #if defined(OS_CHROMEOS) if (HasPopupNotifications()) MarkSinglePopupAsShown(id, true); #endif scoped_refptr delegate = notification_list_->GetNotificationDelegate(id); if (delegate.get()) delegate->ButtonClick(button_index); FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationButtonClicked( id, button_index)); } void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) { scoped_refptr delegate = notification_list_->GetNotificationDelegate(id); if (delegate.get()) delegate->SettingsClick(); FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnNotificationSettingsClicked()); } void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id, bool mark_notification_as_read) { if (FindVisibleNotificationById(id) == NULL) return; #if !defined(OS_CHROMEOS) return this->RemoveNotification(id, false); #else notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read); notification_cache_.RecountUnread(); FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationUpdated(id)); #endif // defined(OS_CHROMEOS) } void MessageCenterImpl::DisplayedNotification( const std::string& id, const DisplaySource source) { if (FindVisibleNotificationById(id) == NULL) return; if (HasPopupNotifications()) notification_list_->MarkSinglePopupAsDisplayed(id); notification_cache_.RecountUnread(); scoped_refptr delegate = notification_list_->GetNotificationDelegate(id); if (delegate.get()) delegate->Display(); FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnNotificationDisplayed(id, source)); } void MessageCenterImpl::SetNotifierSettingsProvider( NotifierSettingsProvider* provider) { if (settings_provider_) { settings_provider_->RemoveObserver(this); settings_provider_ = NULL; } settings_provider_ = provider; if (settings_provider_) settings_provider_->AddObserver(this); } NotifierSettingsProvider* MessageCenterImpl::GetNotifierSettingsProvider() { return settings_provider_; } void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) { if (in_quiet_mode != notification_list_->quiet_mode()) { notification_list_->SetQuietMode(in_quiet_mode); FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_, OnQuietModeChanged(in_quiet_mode)); } quiet_mode_timer_.reset(); } void MessageCenterImpl::EnterQuietModeWithExpire( const base::TimeDelta& expires_in) { if (quiet_mode_timer_) { // Note that the capital Reset() is the method to restart the timer, not // scoped_ptr::reset(). quiet_mode_timer_->Reset(); } else { notification_list_->SetQuietMode(true); FOR_EACH_OBSERVER( MessageCenterObserver, observer_list_, OnQuietModeChanged(true)); quiet_mode_timer_.reset(new base::OneShotTimer); quiet_mode_timer_->Start( FROM_HERE, expires_in, base::Bind( &MessageCenterImpl::SetQuietMode, base::Unretained(this), false)); } } void MessageCenterImpl::RestartPopupTimers() { if (popup_timers_controller_) popup_timers_controller_->StartAll(); } void MessageCenterImpl::PausePopupTimers() { if (popup_timers_controller_) popup_timers_controller_->PauseAll(); } void MessageCenterImpl::DisableTimersForTest() { popup_timers_controller_.reset(); } void MessageCenterImpl::EnableChangeQueueForTest(bool enable) { if (enable) notification_queue_.reset(new internal::ChangeQueue()); else notification_queue_.reset(); } } // namespace message_center