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.
4
5 #include "build/build_config.h"
6
7 #include "chrome/browser/infobars/infobar_container.h"
8
9 #include <algorithm>
10
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"
19
~Delegate()20 InfoBarContainer::Delegate::~Delegate() {
21 }
22
InfoBarContainer(Delegate * delegate)23 InfoBarContainer::InfoBarContainer(Delegate* delegate)
24 : delegate_(delegate),
25 infobar_service_(NULL),
26 top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight) {
27 }
28
~InfoBarContainer()29 InfoBarContainer::~InfoBarContainer() {
30 // RemoveAllInfoBarsForDestruction() should have already cleared our infobars.
31 DCHECK(infobars_.empty());
32 }
33
ChangeInfoBarService(InfoBarService * infobar_service)34 void InfoBarContainer::ChangeInfoBarService(InfoBarService* infobar_service) {
35 HideAllInfoBars();
36
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,
41 source);
42 registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
43 source);
44 registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED,
45 source);
46
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);
51 }
52 }
53
54 // Now that everything is up to date, signal the delegate to re-layout.
55 OnInfoBarStateChanged(false);
56 }
57
GetVerticalOverlap(int * total_height)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;
63
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();
69 }
70
71 if (total_height)
72 *total_height = next_infobar_y + vertical_overlap;
73 return vertical_overlap;
74 }
75
SetMaxTopArrowHeight(int height)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();
83 }
84
OnInfoBarStateChanged(bool is_animating)85 void InfoBarContainer::OnInfoBarStateChanged(bool is_animating) {
86 if (delegate_)
87 delegate_->InfoBarContainerStateChanged(is_animating);
88 UpdateInfoBarArrowTargetHeights();
89 PlatformSpecificInfoBarStateChanged(is_animating);
90 }
91
RemoveInfoBar(InfoBar * infobar)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);
97 infobars_.erase(i);
98 }
99
RemoveAllInfoBarsForDestruction()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.
106 delegate_ = NULL;
107 ChangeInfoBarService(NULL);
108 }
109
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)110 void InfoBarContainer::Observe(int type,
111 const content::NotificationSource& source,
112 const content::NotificationDetails& details) {
113 switch (type) {
114 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED:
115 AddInfoBar(content::Details<InfoBar::AddedDetails>(details).ptr(),
116 infobars_.size(), true, WANT_CALLBACK);
117 break;
118
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();
124 break;
125 }
126
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(),
134 old_infobar));
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);
139 break;
140 }
141
142 default:
143 NOTREACHED();
144 break;
145 }
146 }
147
HideAllInfoBars()148 void InfoBarContainer::HideAllInfoBars() {
149 registrar_.RemoveAll();
150
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);
157 }
158 }
159
AddInfoBar(InfoBar * infobar,size_t position,bool animate,CallbackStatus callback_status)160 void InfoBarContainer::AddInfoBar(InfoBar* infobar,
161 size_t position,
162 bool animate,
163 CallbackStatus callback_status) {
164 DCHECK(std::find(infobars_.begin(), infobars_.end(), infobar) ==
165 infobars_.end());
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);
175 }
176
UpdateInfoBarArrowTargetHeights()177 void InfoBarContainer::UpdateInfoBarArrowTargetHeights() {
178 for (size_t i = 0; i < infobars_.size(); ++i)
179 infobars_[i]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i));
180 }
181
ArrowTargetHeightForInfoBar(size_t infobar_index) const182 int InfoBarContainer::ArrowTargetHeightForInfoBar(size_t infobar_index) const {
183 if (!delegate_ || !delegate_->DrawInfoBarArrows(NULL))
184 return 0;
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());
197 }
198