1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "build/build_config.h"
7 #include "chrome/browser/infobars/infobar_container.h"
11 #include "base/logging.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/infobars/infobar.h"
14 #include "chrome/browser/infobars/infobar_delegate.h"
15 #include "chrome/browser/infobars/infobar_service.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_source.h"
18 #include "ui/gfx/animation/slide_animation.h"
20 InfoBarContainer::Delegate::~Delegate() {
23 InfoBarContainer::InfoBarContainer(Delegate
* delegate
)
24 : delegate_(delegate
),
25 infobar_service_(NULL
),
26 top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight
) {
29 InfoBarContainer::~InfoBarContainer() {
30 // RemoveAllInfoBarsForDestruction() should have already cleared our infobars.
31 DCHECK(infobars_
.empty());
34 void InfoBarContainer::ChangeInfoBarService(InfoBarService
* infobar_service
) {
37 infobar_service_
= infobar_service
;
38 if (infobar_service_
) {
39 content::Source
<InfoBarService
> source(infobar_service_
);
40 registrar_
.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED
,
42 registrar_
.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED
,
44 registrar_
.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED
,
47 for (size_t i
= 0; i
< infobar_service_
->infobar_count(); ++i
) {
48 // As when we removed the infobars above, we prevent callbacks to
49 // OnInfoBarAnimated() for each infobar.
50 AddInfoBar(infobar_service_
->infobar_at(i
), i
, false, NO_CALLBACK
);
54 // Now that everything is up to date, signal the delegate to re-layout.
55 OnInfoBarStateChanged(false);
58 int InfoBarContainer::GetVerticalOverlap(int* total_height
) {
59 // Our |total_height| is the sum of the preferred heights of the InfoBars
60 // contained within us plus the |vertical_overlap|.
61 int vertical_overlap
= 0;
62 int next_infobar_y
= 0;
64 for (InfoBars::iterator
i(infobars_
.begin()); i
!= infobars_
.end(); ++i
) {
65 InfoBar
* infobar
= *i
;
66 next_infobar_y
-= infobar
->arrow_height();
67 vertical_overlap
= std::max(vertical_overlap
, -next_infobar_y
);
68 next_infobar_y
+= infobar
->total_height();
72 *total_height
= next_infobar_y
+ vertical_overlap
;
73 return vertical_overlap
;
76 void InfoBarContainer::SetMaxTopArrowHeight(int height
) {
77 // Decrease the height by the arrow stroke thickness, which is the separator
78 // line height, because the infobar arrow target heights are without-stroke.
79 top_arrow_target_height_
= std::min(
80 std::max(height
- InfoBar::kSeparatorLineHeight
, 0),
81 InfoBar::kMaximumArrowTargetHeight
);
82 UpdateInfoBarArrowTargetHeights();
85 void InfoBarContainer::OnInfoBarStateChanged(bool is_animating
) {
87 delegate_
->InfoBarContainerStateChanged(is_animating
);
88 UpdateInfoBarArrowTargetHeights();
89 PlatformSpecificInfoBarStateChanged(is_animating
);
92 void InfoBarContainer::RemoveInfoBar(InfoBar
* infobar
) {
93 infobar
->set_container(NULL
);
94 InfoBars::iterator
i(std::find(infobars_
.begin(), infobars_
.end(), infobar
));
95 DCHECK(i
!= infobars_
.end());
96 PlatformSpecificRemoveInfoBar(infobar
);
100 void InfoBarContainer::RemoveAllInfoBarsForDestruction() {
101 // Before we remove any children, we reset |delegate_|, so that no removals
102 // will result in us trying to call
103 // delegate_->InfoBarContainerStateChanged(). This is important because at
104 // this point |delegate_| may be shutting down, and it's at best unimportant
105 // and at worst disastrous to call that.
107 ChangeInfoBarService(NULL
);
110 void InfoBarContainer::Observe(int type
,
111 const content::NotificationSource
& source
,
112 const content::NotificationDetails
& details
) {
114 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED
:
115 AddInfoBar(content::Details
<InfoBar::AddedDetails
>(details
).ptr(),
116 infobars_
.size(), true, WANT_CALLBACK
);
119 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED
: {
120 InfoBar::RemovedDetails
* removed_details
=
121 content::Details
<InfoBar::RemovedDetails
>(details
).ptr();
122 removed_details
->first
->Hide(removed_details
->second
);
123 UpdateInfoBarArrowTargetHeights();
127 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED
: {
128 InfoBar::ReplacedDetails
* replaced_details
=
129 content::Details
<InfoBar::ReplacedDetails
>(details
).ptr();
130 InfoBar
* old_infobar
= replaced_details
->first
;
131 InfoBar
* new_infobar
= replaced_details
->second
;
132 PlatformSpecificReplaceInfoBar(old_infobar
, new_infobar
);
133 InfoBars::const_iterator
i(std::find(infobars_
.begin(), infobars_
.end(),
135 DCHECK(i
!= infobars_
.end());
136 size_t position
= i
- infobars_
.begin();
137 old_infobar
->Hide(false);
138 AddInfoBar(new_infobar
, position
, false, WANT_CALLBACK
);
148 void InfoBarContainer::HideAllInfoBars() {
149 registrar_
.RemoveAll();
151 while (!infobars_
.empty()) {
152 InfoBar
* infobar
= infobars_
.front();
153 // Inform the infobar that it's hidden. If it was already closing, this
154 // deletes it. Otherwise, this ensures the infobar will be deleted if it's
155 // closed while it's not in an InfoBarContainer.
156 infobar
->Hide(false);
160 void InfoBarContainer::AddInfoBar(InfoBar
* infobar
,
163 CallbackStatus callback_status
) {
164 DCHECK(std::find(infobars_
.begin(), infobars_
.end(), infobar
) ==
166 DCHECK_LE(position
, infobars_
.size());
167 infobars_
.insert(infobars_
.begin() + position
, infobar
);
168 UpdateInfoBarArrowTargetHeights();
169 PlatformSpecificAddInfoBar(infobar
, position
);
170 if (callback_status
== WANT_CALLBACK
)
171 infobar
->set_container(this);
172 infobar
->Show(animate
);
173 if (callback_status
== NO_CALLBACK
)
174 infobar
->set_container(this);
177 void InfoBarContainer::UpdateInfoBarArrowTargetHeights() {
178 for (size_t i
= 0; i
< infobars_
.size(); ++i
)
179 infobars_
[i
]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i
));
182 int InfoBarContainer::ArrowTargetHeightForInfoBar(size_t infobar_index
) const {
183 if (!delegate_
|| !delegate_
->DrawInfoBarArrows(NULL
))
185 if (infobar_index
== 0)
186 return top_arrow_target_height_
;
187 const gfx::SlideAnimation
& first_infobar_animation
=
188 const_cast<const InfoBar
*>(infobars_
.front())->animation();
189 if ((infobar_index
> 1) || first_infobar_animation
.IsShowing())
190 return InfoBar::kDefaultArrowTargetHeight
;
191 // When the first infobar is animating closed, we animate the second infobar's
192 // arrow target height from the default to the top target height. Note that
193 // the animation values here are going from 1.0 -> 0.0 as the top bar closes.
194 return top_arrow_target_height_
+ static_cast<int>(
195 (InfoBar::kDefaultArrowTargetHeight
- top_arrow_target_height_
) *
196 first_infobar_animation
.GetCurrentValue());