1 // Copyright (c) 2011 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 "chrome/browser/infobars/infobar.h" 6 7 #include <cmath> 8 9 #include "base/logging.h" 10 #include "build/build_config.h" 11 #include "chrome/browser/infobars/infobar_container.h" 12 #include "chrome/browser/infobars/infobar_service.h" 13 #include "ui/base/animation/slide_animation.h" 14 15 SkColor GetInfoBarTopColor(InfoBarDelegate::Type infobar_type) { 16 static const SkColor kWarningBackgroundColorTop = 17 SkColorSetRGB(255, 242, 183); // Yellow 18 static const SkColor kPageActionBackgroundColorTop = 19 SkColorSetRGB(237, 237, 237); // Gray 20 return (infobar_type == InfoBarDelegate::WARNING_TYPE) ? 21 kWarningBackgroundColorTop : kPageActionBackgroundColorTop; 22 } 23 24 SkColor GetInfoBarBottomColor(InfoBarDelegate::Type infobar_type) { 25 static const SkColor kWarningBackgroundColorBottom = 26 SkColorSetRGB(250, 230, 145); // Yellow 27 static const SkColor kPageActionBackgroundColorBottom = 28 SkColorSetRGB(217, 217, 217); // Gray 29 return (infobar_type == InfoBarDelegate::WARNING_TYPE) ? 30 kWarningBackgroundColorBottom : kPageActionBackgroundColorBottom; 31 } 32 33 // TODO(pkasting): Port Mac to use this. 34 #if defined(TOOLKIT_VIEWS) || defined(TOOLKIT_GTK) || defined(OS_ANDROID) 35 36 InfoBar::InfoBar(InfoBarService* owner, InfoBarDelegate* delegate) 37 : owner_(owner), 38 delegate_(delegate), 39 container_(NULL), 40 animation_(this), 41 arrow_height_(0), 42 arrow_target_height_(kDefaultArrowTargetHeight), 43 arrow_half_width_(0), 44 bar_height_(0), 45 bar_target_height_(kDefaultBarTargetHeight) { 46 DCHECK(owner_ != NULL); 47 DCHECK(delegate_ != NULL); 48 animation_.SetTweenType(ui::Tween::LINEAR); 49 } 50 51 InfoBar::~InfoBar() { 52 } 53 54 void InfoBar::Show(bool animate) { 55 PlatformSpecificShow(animate); 56 if (animate) { 57 animation_.Show(); 58 } else { 59 animation_.Reset(1.0); 60 RecalculateHeights(true); 61 } 62 } 63 64 void InfoBar::Hide(bool animate) { 65 PlatformSpecificHide(animate); 66 if (animate) { 67 animation_.Hide(); 68 } else { 69 animation_.Reset(0.0); 70 // We want to remove ourselves from the container immediately even if we 71 // still have an owner, which MaybeDelete() won't do. 72 DCHECK(container_); 73 container_->RemoveInfoBar(this); 74 MaybeDelete(); // Necessary if the infobar was already closing. 75 } 76 } 77 78 void InfoBar::SetArrowTargetHeight(int height) { 79 DCHECK_LE(height, kMaximumArrowTargetHeight); 80 // Once the closing animation starts, we ignore further requests to change the 81 // target height. 82 if ((arrow_target_height_ != height) && !animation_.IsClosing()) { 83 arrow_target_height_ = height; 84 RecalculateHeights(false); 85 } 86 } 87 88 void InfoBar::CloseSoon() { 89 owner_ = NULL; 90 PlatformSpecificOnCloseSoon(); 91 MaybeDelete(); 92 } 93 94 void InfoBar::AnimationProgressed(const ui::Animation* animation) { 95 RecalculateHeights(false); 96 } 97 98 void InfoBar::RemoveSelf() { 99 // |owner_| should never be NULL here. If it is, then someone violated what 100 // they were supposed to do -- e.g. a ConfirmInfoBarDelegate subclass returned 101 // true from Accept() or Cancel() even though the infobar was already closing. 102 // In the worst case, if we also switched tabs during that process, then 103 // |this| has already been destroyed. But if that's the case, then we're 104 // going to deref a garbage |this| pointer here whether we check |owner_| or 105 // not, and in other cases (where we're still closing and |this| is valid), 106 // checking |owner_| here will avoid a NULL deref. 107 if (owner_) 108 owner_->RemoveInfoBar(delegate_); 109 } 110 111 void InfoBar::SetBarTargetHeight(int height) { 112 if (bar_target_height_ != height) { 113 bar_target_height_ = height; 114 RecalculateHeights(false); 115 } 116 } 117 118 int InfoBar::OffsetY(const gfx::Size& prefsize) const { 119 return arrow_height_ + 120 std::max((bar_target_height_ - prefsize.height()) / 2, 0) - 121 (bar_target_height_ - bar_height_); 122 } 123 124 void InfoBar::AnimationEnded(const ui::Animation* animation) { 125 // When the animation ends, we must ensure the container is notified even if 126 // the heights haven't changed, lest it never get an "animation finished" 127 // notification. (If the browser doesn't get this notification, it will not 128 // bother to re-layout the content area for the new infobar size.) 129 RecalculateHeights(true); 130 MaybeDelete(); 131 } 132 133 void InfoBar::RecalculateHeights(bool force_notify) { 134 int old_arrow_height = arrow_height_; 135 int old_bar_height = bar_height_; 136 137 // Find the desired arrow height/half-width. The arrow area is 138 // |arrow_height_| * |arrow_half_width_|. When the bar is opening or closing, 139 // scaling each of these with the square root of the animation value causes a 140 // linear animation of the area, which matches the perception of the animation 141 // of the bar portion. 142 double scale_factor = sqrt(animation_.GetCurrentValue()); 143 arrow_height_ = static_cast<int>(arrow_target_height_ * scale_factor); 144 if (animation_.is_animating()) { 145 arrow_half_width_ = static_cast<int>(std::min(arrow_target_height_, 146 kMaximumArrowTargetHalfWidth) * scale_factor); 147 } else { 148 // When the infobar is not animating (i.e. fully open), we set the 149 // half-width to be proportionally the same distance between its default and 150 // maximum values as the height is between its. 151 arrow_half_width_ = kDefaultArrowTargetHalfWidth + 152 ((kMaximumArrowTargetHalfWidth - kDefaultArrowTargetHalfWidth) * 153 ((arrow_height_ - kDefaultArrowTargetHeight) / 154 (kMaximumArrowTargetHeight - kDefaultArrowTargetHeight))); 155 } 156 // Add pixels for the stroke, if the arrow is to be visible at all. Without 157 // this, changing the arrow height from 0 to kSeparatorLineHeight would 158 // produce no visible effect, because the stroke would paint atop the divider 159 // line above the infobar. 160 if (arrow_height_) 161 arrow_height_ += kSeparatorLineHeight; 162 163 bar_height_ = animation_.CurrentValueBetween(0, bar_target_height_); 164 165 // Don't re-layout if nothing has changed, e.g. because the animation step was 166 // not large enough to actually change the heights by at least a pixel. 167 bool heights_differ = 168 (old_arrow_height != arrow_height_) || (old_bar_height != bar_height_); 169 if (heights_differ) 170 PlatformSpecificOnHeightsRecalculated(); 171 172 if (container_ && (heights_differ || force_notify)) 173 container_->OnInfoBarStateChanged(animation_.is_animating()); 174 } 175 176 void InfoBar::MaybeDelete() { 177 if (!owner_ && delegate_ && (animation_.GetCurrentValue() == 0.0)) { 178 if (container_) 179 container_->RemoveInfoBar(this); 180 delete delegate_; 181 delegate_ = NULL; 182 } 183 } 184 185 #endif // TOOLKIT_VIEWS || TOOLKIT_GTK || OS_ANDROID 186