172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/infobars/infobar_container.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/tab_contents/infobar_delegate.h"
8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/views/infobars/infobar.h"
9dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h"
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h"
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/animation/slide_animation.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
14dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenInfoBarContainer::Delegate::~Delegate() {
15dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
16dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochInfoBarContainer::InfoBarContainer(Delegate* delegate)
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : delegate_(delegate),
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      tab_contents_(NULL),
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight) {
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochInfoBarContainer::~InfoBarContainer() {
24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // RemoveAllInfoBarsForDestruction() should have already cleared our infobars.
25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(infobars_.empty());
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InfoBarContainer::ChangeTabContents(TabContents* contents) {
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.RemoveAll();
30dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
31dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  while (!infobars_.empty()) {
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    InfoBar* infobar = infobars_.front();
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // NULL the container pointer first so that if the infobar is currently
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // animating, OnInfoBarAnimated() won't get called; we'll manually trigger
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // this once for the whole set of changes below.  This also prevents
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // InfoBar::MaybeDelete() from calling RemoveInfoBar() a second time if the
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // infobar happens to be at zero height (dunno if this can actually happen).
38dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    infobar->set_container(NULL);
39dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    RemoveInfoBar(infobar);
40dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
41dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_contents_ = contents;
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_contents_) {
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Source<TabContents> tc_source(tab_contents_);
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED,
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   tc_source);
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED,
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   tc_source);
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REPLACED,
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   tc_source);
51dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
52dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    for (size_t i = 0; i < tab_contents_->infobar_count(); ++i) {
53dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // As when we removed the infobars above, we prevent callbacks to
54dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // OnInfoBarAnimated() for each infobar.
55dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      AddInfoBar(tab_contents_->GetInfoBarDelegateAt(i)->CreateInfoBar(), false,
56dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                 NO_CALLBACK);
57dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
59dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
60dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Now that everything is up to date, signal the delegate to re-layout.
61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  OnInfoBarStateChanged(false);
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenint InfoBarContainer::GetVerticalOverlap(int* total_height) {
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Our |total_height| is the sum of the preferred heights of the InfoBars
66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // contained within us plus the |vertical_overlap|.
67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int vertical_overlap = 0;
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int next_infobar_y = 0;
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (InfoBars::iterator i(infobars_.begin()); i != infobars_.end(); ++i) {
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    InfoBar* infobar = *i;
72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    next_infobar_y -= infobar->arrow_height();
73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    vertical_overlap = std::max(vertical_overlap, -next_infobar_y);
74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    next_infobar_y += infobar->total_height();
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (total_height)
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    *total_height = next_infobar_y + vertical_overlap;
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return vertical_overlap;
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::SetMaxTopArrowHeight(int height) {
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Decrease the height by the arrow stroke thickness, which is the separator
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // line height, because the infobar arrow target heights are without-stroke.
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  top_arrow_target_height_ = std::min(
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      std::max(height - InfoBar::kSeparatorLineHeight, 0),
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      InfoBar::kMaximumArrowTargetHeight);
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  UpdateInfoBarArrowTargetHeights();
89dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
90dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::OnInfoBarStateChanged(bool is_animating) {
92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (delegate_)
93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    delegate_->InfoBarContainerStateChanged(is_animating);
9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::RemoveDelegate(InfoBarDelegate* delegate) {
97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  tab_contents_->RemoveInfoBar(delegate);
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::RemoveInfoBar(InfoBar* infobar) {
101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  InfoBars::iterator infobar_iterator(std::find(infobars_.begin(),
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                infobars_.end(), infobar));
103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(infobar_iterator != infobars_.end());
104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  PlatformSpecificRemoveInfoBar(infobar);
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  infobars_.erase(infobar_iterator);
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::RemoveAllInfoBarsForDestruction() {
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Before we remove any children, we reset |delegate_|, so that no removals
110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // will result in us trying to call
111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // delegate_->InfoBarContainerStateChanged().  This is important because at
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // this point |delegate_| may be shutting down, and it's at best unimportant
113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // and at worst disastrous to call that.
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  delegate_ = NULL;
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ChangeTabContents(NULL);
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InfoBarContainer::Observe(NotificationType type,
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               const NotificationSource& source,
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               const NotificationDetails& details) {
121dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  switch (type.value) {
122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    case NotificationType::TAB_CONTENTS_INFOBAR_ADDED:
123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      AddInfoBar(Details<InfoBarDelegate>(details)->CreateInfoBar(), true,
124dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                 WANT_CALLBACK);
125dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      break;
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    case NotificationType::TAB_CONTENTS_INFOBAR_REMOVED:
128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      RemoveInfoBar(Details<InfoBarDelegate>(details).ptr(), true);
129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      break;
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    case NotificationType::TAB_CONTENTS_INFOBAR_REPLACED: {
132dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      typedef std::pair<InfoBarDelegate*, InfoBarDelegate*> InfoBarPair;
133dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      InfoBarPair* infobar_pair = Details<InfoBarPair>(details).ptr();
134dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      RemoveInfoBar(infobar_pair->first, false);
135dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      AddInfoBar(infobar_pair->second->CreateInfoBar(), false, WANT_CALLBACK);
136dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      break;
137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    default:
140dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      NOTREACHED();
141dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      break;
142dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InfoBarContainer::RemoveInfoBar(InfoBarDelegate* delegate,
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     bool use_animation) {
147dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Search for the infobar associated with |delegate|.  We cannot search for
148dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // |delegate| in |tab_contents_|, because an InfoBar remains alive until its
149dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // close animation completes, while the delegate is removed from the tab
150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // immediately.
151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (InfoBars::iterator i(infobars_.begin()); i != infobars_.end(); ++i) {
152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    InfoBar* infobar = *i;
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (infobar->delegate() == delegate) {
154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // We merely need hide the infobar; it will call back to RemoveInfoBar()
155dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      // itself once it's hidden.
156dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      infobar->Hide(use_animation);
157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      UpdateInfoBarArrowTargetHeights();
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
163dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid InfoBarContainer::AddInfoBar(InfoBar* infobar,
164dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                  bool animate,
165dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                  CallbackStatus callback_status) {
166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(std::find(infobars_.begin(), infobars_.end(), infobar) ==
167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      infobars_.end());
168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  infobars_.push_back(infobar);
169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  UpdateInfoBarArrowTargetHeights();
170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  PlatformSpecificAddInfoBar(infobar);
171dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (callback_status == WANT_CALLBACK)
172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    infobar->set_container(this);
173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  infobar->Show(animate);
174dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (callback_status == NO_CALLBACK)
175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    infobar->set_container(this);
176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::UpdateInfoBarArrowTargetHeights() {
179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (size_t i = 0; i < infobars_.size(); ++i)
180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    infobars_[i]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i));
181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenint InfoBarContainer::ArrowTargetHeightForInfoBar(size_t infobar_index) const {
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!delegate_->DrawInfoBarArrows(NULL))
185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return 0;
186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (infobar_index == 0)
187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return top_arrow_target_height_;
188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const ui::SlideAnimation* first_infobar_animation =
189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      const_cast<const InfoBar*>(infobars_.front())->animation();
190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if ((infobar_index > 1) || first_infobar_animation->IsShowing())
191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return InfoBar::kDefaultArrowTargetHeight;
192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // When the first infobar is animating closed, we animate the second infobar's
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // arrow target height from the default to the top target height.  Note that
194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // the animation values here are going from 1.0 -> 0.0 as the top bar closes.
195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return top_arrow_target_height_ + static_cast<int>(
196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      (InfoBar::kDefaultArrowTargetHeight - top_arrow_target_height_) *
197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          first_infobar_animation->GetCurrentValue());
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
199