Home | History | Annotate | Download | only in infobars
      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 
     20 InfoBarContainer::Delegate::~Delegate() {
     21 }
     22 
     23 InfoBarContainer::InfoBarContainer(Delegate* delegate)
     24     : delegate_(delegate),
     25       infobar_service_(NULL),
     26       top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight) {
     27 }
     28 
     29 InfoBarContainer::~InfoBarContainer() {
     30   // RemoveAllInfoBarsForDestruction() should have already cleared our infobars.
     31   DCHECK(infobars_.empty());
     32 }
     33 
     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 
     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 
     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 
     85 void InfoBarContainer::OnInfoBarStateChanged(bool is_animating) {
     86   if (delegate_)
     87     delegate_->InfoBarContainerStateChanged(is_animating);
     88   UpdateInfoBarArrowTargetHeights();
     89   PlatformSpecificInfoBarStateChanged(is_animating);
     90 }
     91 
     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 
    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 
    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 
    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 
    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 
    177 void InfoBarContainer::UpdateInfoBarArrowTargetHeights() {
    178   for (size_t i = 0; i < infobars_.size(); ++i)
    179     infobars_[i]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i));
    180 }
    181 
    182 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