12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/views/message_popup_collection.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <set>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/i18n/rtl.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/logging.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/weak_ptr.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/run_loop.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/timer/timer.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/accessibility/ax_enums.h"
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/animation_delegate.h"
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/slide_animation.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/screen.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/message_center.h"
2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/message_center/message_center_style.h"
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/message_center/message_center_tray.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/notification.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/notification_list.h"
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/message_center/views/message_view_context_menu_controller.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/message_center/views/notification_view.h"
27116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/message_center/views/popup_alignment_delegate.h"
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/message_center/views/toast_contents_view.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/background.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/layout/fill_layout.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/view.h"
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "ui/views/views_delegate.h"
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/widget/widget.h"
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/widget/widget_delegate.h"
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace message_center {
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Timeout between the last user-initiated close of the toast and the moment
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// when normal layout/update of the toast stack continues. If the last toast was
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// just closed, the timeout is shorter.
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kMouseExitedDeferTimeoutMs = 200;
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
44bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// The margin between messages (and between the anchor unless
45bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// first_item_has_no_margin was specified).
46558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochconst int kToastMarginY = kMarginBetweenItems;
47bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace.
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
50116680a4aac90f2aa7413d9095a592090648e557Ben MurdochMessagePopupCollection::MessagePopupCollection(
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    gfx::NativeView parent,
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    MessageCenter* message_center,
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    MessageCenterTray* tray,
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    PopupAlignmentDelegate* alignment_delegate)
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : parent_(parent),
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      message_center_(message_center),
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      tray_(tray),
58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      alignment_delegate_(alignment_delegate),
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      defer_counter_(0),
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      latest_toast_entered_(NULL),
61bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch      user_is_closing_toasts_by_clicking_(false),
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      context_menu_controller_(new MessageViewContextMenuController(this)),
63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      weak_factory_(this) {
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(message_center_);
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  defer_timer_.reset(new base::OneShotTimer<MessagePopupCollection>);
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  message_center_->AddObserver(this);
67116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  alignment_delegate_->set_collection(this);
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)MessagePopupCollection::~MessagePopupCollection() {
71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  weak_factory_.InvalidateWeakPtrs();
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  message_center_->RemoveObserver(this);
7458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  CloseAllWidgets();
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MessagePopupCollection::ClickOnNotification(
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& notification_id) {
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  message_center_->ClickOnNotification(notification_id);
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MessagePopupCollection::RemoveNotification(
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& notification_id,
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    bool by_user) {
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  message_center_->RemoveNotification(notification_id, by_user);
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)scoped_ptr<ui::MenuModel> MessagePopupCollection::CreateMenuModel(
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const NotifierId& notifier_id,
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& display_source) {
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return tray_->CreateNotificationMenuModel(notifier_id, display_source);
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool MessagePopupCollection::HasClickedListener(
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& notification_id) {
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return message_center_->HasClickedListener(notification_id);
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MessagePopupCollection::ClickOnNotificationButton(
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& notification_id,
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int button_index) {
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  message_center_->ClickOnNotificationButton(notification_id, button_index);
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void MessagePopupCollection::MarkAllPopupsShown() {
107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  std::set<std::string> closed_ids = CloseAllWidgets();
108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for (std::set<std::string>::iterator iter = closed_ids.begin();
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)       iter != closed_ids.end(); iter++) {
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    message_center_->MarkSinglePopupAsShown(*iter, false);
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::UpdateWidgets() {
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  NotificationList::PopupNotifications popups =
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      message_center_->GetPopupNotifications();
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (popups.empty()) {
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    CloseAllWidgets();
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool top_down = alignment_delegate_->IsTopDown();
124a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  int base = GetBaseLine(toasts_.empty() ? NULL : toasts_.back());
125bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Iterate in the reverse order to keep the oldest toasts on screen. Newer
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // items may be ignored if there are no room to place them.
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (NotificationList::PopupNotifications::const_reverse_iterator iter =
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)           popups.rbegin(); iter != popups.rend(); ++iter) {
130868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (FindToast((*iter)->id()))
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      continue;
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    NotificationView* view =
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        NotificationView::Create(NULL,
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 *(*iter),
1367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                 true); // Create top-level notification.
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    view->set_context_menu_controller(context_menu_controller_.get());
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int view_height = ToastContentsView::GetToastSizeForView(view).height();
139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    int height_available =
140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        top_down ? alignment_delegate_->GetWorkAreaBottom() - base : base;
141a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
142a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (height_available - view_height - kToastMarginY < 0) {
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      delete view;
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    ToastContentsView* toast =
148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        new ToastContentsView((*iter)->id(), weak_factory_.GetWeakPtr());
149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // There will be no contents already since this is a new ToastContentsView.
150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    toast->SetContents(view, /*a11y_feedback_for_updates=*/false);
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    toasts_.push_back(toast);
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    view->set_controller(toast);
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    gfx::Size preferred_size = toast->GetPreferredSize();
155116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    gfx::Point origin(
156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        alignment_delegate_->GetToastOriginX(gfx::Rect(preferred_size)), base);
157d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // The toast slides in from the edge of the screen horizontally.
158116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (alignment_delegate_->IsFromLeft())
159d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      origin.set_x(origin.x() - preferred_size.width());
160d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    else
161d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      origin.set_x(origin.x() + preferred_size.width());
162d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (top_down)
163d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      origin.set_y(origin.y() + view_height);
164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    toast->RevealWithAnimation(origin);
166a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
167a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // Shift the base line to be a few pixels above the last added toast or (few
168a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // pixels below last added toast if top-aligned).
169a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (top_down)
170a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      base += view_height + kToastMarginY;
171a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    else
172a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      base -= view_height + kToastMarginY;
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
174868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (views::ViewsDelegate::views_delegate) {
175868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      views::ViewsDelegate::views_delegate->NotifyAccessibilityEvent(
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          toast, ui::AX_EVENT_ALERT);
177868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
179010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    message_center_->DisplayedNotification(
180010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        (*iter)->id(), message_center::DISPLAY_SOURCE_POPUP);
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::OnMouseEntered(ToastContentsView* toast_entered) {
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Sometimes we can get two MouseEntered/MouseExited in a row when animating
186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // toasts.  So we need to keep track of which one is the currently active one.
187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  latest_toast_entered_ = toast_entered;
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
189868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  message_center_->PausePopupTimers();
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (user_is_closing_toasts_by_clicking_)
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    defer_timer_->Stop();
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::OnMouseExited(ToastContentsView* toast_exited) {
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If we're exiting a toast after entering a different toast, then ignore
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // this mouse event.
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (toast_exited != latest_toast_entered_)
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  latest_toast_entered_ = NULL;
201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (user_is_closing_toasts_by_clicking_) {
203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    defer_timer_->Start(
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        FROM_HERE,
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::TimeDelta::FromMilliseconds(kMouseExitedDeferTimeoutMs),
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        this,
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        &MessagePopupCollection::OnDeferTimerExpired);
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
209868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    message_center_->RestartPopupTimers();
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)std::set<std::string> MessagePopupCollection::CloseAllWidgets() {
214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  std::set<std::string> closed_toast_ids;
215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  while (!toasts_.empty()) {
217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    ToastContentsView* toast = toasts_.front();
218f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    toasts_.pop_front();
219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    closed_toast_ids.insert(toast->id());
220f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
221f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    OnMouseExited(toast);
222f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // CloseWithAnimation will cause the toast to forget about |this| so it is
224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // required when we forget a toast.
225f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    toast->CloseWithAnimation();
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
228f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return closed_toast_ids;
229f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
230f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MessagePopupCollection::ForgetToast(ToastContentsView* toast) {
232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  toasts_.remove(toast);
233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  OnMouseExited(toast);
234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MessagePopupCollection::RemoveToast(ToastContentsView* toast,
237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                         bool mark_as_shown) {
238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ForgetToast(toast);
239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  toast->CloseWithAnimation();
241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (mark_as_shown)
243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    message_center_->MarkSinglePopupAsShown(toast->id(), false);
244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
245f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::RepositionWidgets() {
247116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool top_down = alignment_delegate_->IsTopDown();
248a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  int base = GetBaseLine(NULL);  // We don't want to position relative to last
249a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                                 // toast - we want re-position.
250bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch
251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for (Toasts::const_iterator iter = toasts_.begin(); iter != toasts_.end();) {
252f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    Toasts::const_iterator curr = iter++;
253c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    gfx::Rect bounds((*curr)->bounds());
254116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    bounds.set_x(alignment_delegate_->GetToastOriginX(bounds));
255116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    bounds.set_y(top_down ? base : base - bounds.height());
256a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
257a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // The notification may scrolls the boundary of the screen due to image
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // load and such notifications should disappear. Do not call
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // CloseWithAnimation, we don't want to show the closing animation, and we
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // don't want to mark such notifications as shown. See crbug.com/233424
261116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if ((top_down ? alignment_delegate_->GetWorkAreaBottom() - bounds.bottom()
262116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                  : bounds.y()) >= 0)
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      (*curr)->SetBoundsWithAnimation(bounds);
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    else
265f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      RemoveToast(*curr, /*mark_as_shown=*/false);
266a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
267a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // Shift the base line to be a few pixels above the last added toast or (few
268a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // pixels below last added toast if top-aligned).
269a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (top_down)
270a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      base += bounds.height() + kToastMarginY;
271a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    else
272a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      base -= bounds.height() + kToastMarginY;
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::RepositionWidgetsWithTarget() {
277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (toasts_.empty())
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
280116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool top_down = alignment_delegate_->IsTopDown();
281a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
282a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Nothing to do if there are no widgets above target if bottom-aligned or no
283a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // widgets below target if top-aligned.
284a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (top_down ? toasts_.back()->origin().y() < target_top_edge_
285a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)               : toasts_.back()->origin().y() > target_top_edge_)
286868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return;
287868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
288868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  Toasts::reverse_iterator iter = toasts_.rbegin();
289868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  for (; iter != toasts_.rend(); ++iter) {
290a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // We only reposition widgets above target if bottom-aligned or widgets
291a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // below target if top-aligned.
292a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (top_down ? (*iter)->origin().y() < target_top_edge_
293a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                 : (*iter)->origin().y() > target_top_edge_)
294868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      break;
295868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
296868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  --iter;
297a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
298a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Slide length is the number of pixels the widgets should move so that their
299a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // bottom edge (top-edge if top-aligned) touches the target.
300a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  int slide_length = std::abs(target_top_edge_ - (*iter)->origin().y());
301a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  for (;; --iter) {
302868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    gfx::Rect bounds((*iter)->bounds());
303a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
304a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // If top-aligned, shift widgets upwards by slide_length. If bottom-aligned,
305a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // shift them downwards by slide_length.
306a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (top_down)
307a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      bounds.set_y(bounds.y() - slide_length);
308a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    else
309a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      bounds.set_y(bounds.y() + slide_length);
310868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    (*iter)->SetBoundsWithAnimation(bounds);
311868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
312868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (iter == toasts_.rbegin())
313868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      break;
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
317f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)int MessagePopupCollection::GetBaseLine(ToastContentsView* last_toast) const {
318116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!last_toast) {
319116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return alignment_delegate_->GetBaseLine();
320116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  } else if (alignment_delegate_->IsTopDown()) {
321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return toasts_.back()->bounds().bottom() + kToastMarginY;
322a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  } else {
323116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return toasts_.back()->origin().y() - kToastMarginY;
324a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
325a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
326a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
327c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::OnNotificationAdded(
328c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& notification_id) {
329c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DoUpdateIfPossible();
330c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::OnNotificationRemoved(
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& notification_id,
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    bool by_user) {
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Find a toast.
336f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Toasts::const_iterator iter = toasts_.begin();
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (; iter != toasts_.end(); ++iter) {
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if ((*iter)->id() == notification_id)
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (iter == toasts_.end())
342c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
343c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
344c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  target_top_edge_ = (*iter)->bounds().y();
3454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (by_user && !user_is_closing_toasts_by_clicking_) {
346c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // [Re] start a timeout after which the toasts re-position to their
347c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // normal locations after tracking the mouse pointer for easy deletion.
348c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // This provides a period of time when toasts are easy to remove because
349c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // they re-position themselves to have Close button right under the mouse
350c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // pointer. If the user continue to remove the toasts, the delay is reset.
351c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Once user stopped removing the toasts, the toasts re-populate/rearrange
352c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // after the specified delay.
3534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    user_is_closing_toasts_by_clicking_ = true;
3544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    IncrementDeferCounter();
355c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
3564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // CloseWithAnimation ultimately causes a call to RemoveToast, which calls
3584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // OnMouseExited.  This means that |user_is_closing_toasts_by_clicking_| must
3594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // have been set before this call, otherwise it will remain true even after
3604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // the toast is closed, since the defer timer won't be started.
361f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  RemoveToast(*iter, /*mark_as_shown=*/true);
3624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (by_user)
3644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    RepositionWidgetsWithTarget();
365c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
366c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
367c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::OnDeferTimerExpired() {
368c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  user_is_closing_toasts_by_clicking_ = false;
369c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DecrementDeferCounter();
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
371868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  message_center_->RestartPopupTimers();
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
374c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::OnNotificationUpdated(
375c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& notification_id) {
376c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Find a toast.
377f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Toasts::const_iterator toast_iter = toasts_.begin();
378c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (; toast_iter != toasts_.end(); ++toast_iter) {
379c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if ((*toast_iter)->id() == notification_id)
380c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
382c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (toast_iter == toasts_.end())
383c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
384c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
385c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  NotificationList::PopupNotifications notifications =
386c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      message_center_->GetPopupNotifications();
387c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool updated = false;
388c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (NotificationList::PopupNotifications::iterator iter =
390c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)           notifications.begin(); iter != notifications.end(); ++iter) {
3910de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)    Notification* notification = *iter;
3920de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)    DCHECK(notification);
3930de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)    ToastContentsView* toast_contents_view = *toast_iter;
3940de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)    DCHECK(toast_contents_view);
3950de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)
3960de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)    if (notification->id() != notification_id)
397c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      continue;
398c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
399f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const RichNotificationData& optional_fields =
4000de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)        notification->rich_notification_data();
401f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bool a11y_feedback_for_updates =
402f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        optional_fields.should_make_spoken_feedback_for_popup_updates;
403f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
4040de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)    toast_contents_view->UpdateContents(*notification,
4050de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)                                        a11y_feedback_for_updates);
4060de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)
407c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    updated = true;
408c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
409c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
410c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // OnNotificationUpdated() can be called when a notification is excluded from
411c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // the popup notification list but still remains in the full notification
412c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // list. In that case the widget for the notification has to be closed here.
413c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!updated)
414f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    RemoveToast(*toast_iter, /*mark_as_shown=*/true);
415c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
416c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (user_is_closing_toasts_by_clicking_)
417c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    RepositionWidgetsWithTarget();
418c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  else
419c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DoUpdateIfPossible();
420c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
421c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
422868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)ToastContentsView* MessagePopupCollection::FindToast(
423f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const std::string& notification_id) const {
424f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for (Toasts::const_iterator iter = toasts_.begin(); iter != toasts_.end();
425f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)       ++iter) {
426c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if ((*iter)->id() == notification_id)
427868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return *iter;
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
429868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return NULL;
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
432c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::IncrementDeferCounter() {
433c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  defer_counter_++;
434c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
435c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
436c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::DecrementDeferCounter() {
437c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  defer_counter_--;
438c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(defer_counter_ >= 0);
439c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DoUpdateIfPossible();
440c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
442c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// This is the main sequencer of tasks. It does a step, then waits for
443c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// all started transitions to play out before doing the next step.
444c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// First, remove all expired toasts.
445c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Then, reposition widgets (the reposition on close happens before all
446c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// deferred tasks are even able to run)
447c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Then, see if there is vacant space for new toasts.
448c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void MessagePopupCollection::DoUpdateIfPossible() {
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (defer_counter_ > 0)
450c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
451c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
452c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  RepositionWidgets();
453c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
454c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (defer_counter_ > 0)
455c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
456c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
457c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Reposition could create extra space which allows additional widgets.
458c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  UpdateWidgets();
459c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
460c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (defer_counter_ > 0)
461c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
462c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
463c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Test support. Quit the test run loop when no more updates are deferred,
464c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // meaining th echeck for updates did not cause anything to change so no new
465c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // transition animations were started.
466c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (run_loop_for_test_.get())
467c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    run_loop_for_test_->Quit();
468c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
469c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
470cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void MessagePopupCollection::OnDisplayMetricsChanged(
471116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const gfx::Display& display) {
472116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  alignment_delegate_->RecomputeAlignment(display);
473cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
474cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
475f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)views::Widget* MessagePopupCollection::GetWidgetForTest(const std::string& id)
476f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const {
477f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for (Toasts::const_iterator iter = toasts_.begin(); iter != toasts_.end();
478f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)       ++iter) {
479c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if ((*iter)->id() == id)
480c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return (*iter)->GetWidget();
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
482c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return NULL;
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
485f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MessagePopupCollection::CreateRunLoopForTest() {
486c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  run_loop_for_test_.reset(new base::RunLoop());
487f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
488f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
489f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MessagePopupCollection::WaitForTest() {
490c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  run_loop_for_test_->Run();
491c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  run_loop_for_test_.reset();
492c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
493c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
494f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)gfx::Rect MessagePopupCollection::GetToastRectAt(size_t index) const {
495c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(defer_counter_ == 0) << "Fetching the bounds with animations active.";
496c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  size_t i = 0;
497f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for (Toasts::const_iterator iter = toasts_.begin(); iter != toasts_.end();
498f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)       ++iter) {
499c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (i++ == index) {
500c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      views::Widget* widget = (*iter)->GetWidget();
501c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (widget)
502c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return widget->GetWindowBoundsInScreen();
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
506c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return gfx::Rect();
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace message_center
510