• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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