10529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "components/infobars/core/infobar_container.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "build/build_config.h"
110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "components/infobars/core/infobar.h"
120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "components/infobars/core/infobar_delegate.h"
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/slide_animation.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochnamespace infobars {
160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)InfoBarContainer::Delegate::~Delegate() {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
207dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochInfoBarContainer::InfoBarContainer(Delegate* delegate)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : delegate_(delegate),
22e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      infobar_manager_(NULL),
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight) {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)InfoBarContainer::~InfoBarContainer() {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // RemoveAllInfoBarsForDestruction() should have already cleared our infobars.
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(infobars_.empty());
29e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (infobar_manager_)
30e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    infobar_manager_->RemoveObserver(this);
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochvoid InfoBarContainer::ChangeInfoBarManager(InfoBarManager* infobar_manager) {
34e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (infobar_manager_)
35e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    infobar_manager_->RemoveObserver(this);
36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Hides all infobars in this container without animation.
38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  while (!infobars_.empty()) {
39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    InfoBar* infobar = infobars_.front();
40effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // Inform the infobar that it's hidden.  If it was already closing, this
41effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // deletes it.  Otherwise, this ensures the infobar will be deleted if it's
42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // closed while it's not in an InfoBarContainer.
43effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    infobar->Hide(false);
44effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
46e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  infobar_manager_ = infobar_manager;
47e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (infobar_manager_) {
48e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    infobar_manager_->AddObserver(this);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    for (size_t i = 0; i < infobar_manager_->infobar_count(); ++i) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // As when we removed the infobars above, we prevent callbacks to
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // OnInfoBarStateChanged() for each infobar.
53e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      AddInfoBar(infobar_manager_->infobar_at(i), i, false, NO_CALLBACK);
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now that everything is up to date, signal the delegate to re-layout.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OnInfoBarStateChanged(false);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)int InfoBarContainer::GetVerticalOverlap(int* total_height) const {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Our |total_height| is the sum of the preferred heights of the InfoBars
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // contained within us plus the |vertical_overlap|.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int vertical_overlap = 0;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int next_infobar_y = 0;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (InfoBars::const_iterator i(infobars_.begin()); i != infobars_.end();
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)       ++i) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    InfoBar* infobar = *i;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_infobar_y -= infobar->arrow_height();
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    vertical_overlap = std::max(vertical_overlap, -next_infobar_y);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_infobar_y += infobar->total_height();
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (total_height)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *total_height = next_infobar_y + vertical_overlap;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return vertical_overlap;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InfoBarContainer::SetMaxTopArrowHeight(int height) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Decrease the height by the arrow stroke thickness, which is the separator
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // line height, because the infobar arrow target heights are without-stroke.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  top_arrow_target_height_ = std::min(
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::max(height - InfoBar::kSeparatorLineHeight, 0),
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      InfoBar::kMaximumArrowTargetHeight);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateInfoBarArrowTargetHeights();
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InfoBarContainer::OnInfoBarStateChanged(bool is_animating) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delegate_)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delegate_->InfoBarContainerStateChanged(is_animating);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateInfoBarArrowTargetHeights();
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PlatformSpecificInfoBarStateChanged(is_animating);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InfoBarContainer::RemoveInfoBar(InfoBar* infobar) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  infobar->set_container(NULL);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InfoBars::iterator i(std::find(infobars_.begin(), infobars_.end(), infobar));
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(i != infobars_.end());
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PlatformSpecificRemoveInfoBar(infobar);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  infobars_.erase(i);
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InfoBarContainer::RemoveAllInfoBarsForDestruction() {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Before we remove any children, we reset |delegate_|, so that no removals
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // will result in us trying to call
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // delegate_->InfoBarContainerStateChanged().  This is important because at
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this point |delegate_| may be shutting down, and it's at best unimportant
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and at worst disastrous to call that.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delegate_ = NULL;
111e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  ChangeInfoBarManager(NULL);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid InfoBarContainer::OnInfoBarAdded(InfoBar* infobar) {
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  AddInfoBar(infobar, infobars_.size(), true, WANT_CALLBACK);
116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid InfoBarContainer::OnInfoBarRemoved(InfoBar* infobar, bool animate) {
119effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  infobar->Hide(animate);
120effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  UpdateInfoBarArrowTargetHeights();
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
123effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid InfoBarContainer::OnInfoBarReplaced(InfoBar* old_infobar,
124effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                                         InfoBar* new_infobar) {
125effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  PlatformSpecificReplaceInfoBar(old_infobar, new_infobar);
126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  InfoBars::const_iterator i(std::find(infobars_.begin(), infobars_.end(),
127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                                       old_infobar));
128effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK(i != infobars_.end());
129effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  size_t position = i - infobars_.begin();
130effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  old_infobar->Hide(false);
131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  AddInfoBar(new_infobar, position, false, WANT_CALLBACK);
132effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
134e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochvoid InfoBarContainer::OnManagerShuttingDown(InfoBarManager* manager) {
135e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  DCHECK_EQ(infobar_manager_, manager);
136e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  infobar_manager_->RemoveObserver(this);
137e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  infobar_manager_ = NULL;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InfoBarContainer::AddInfoBar(InfoBar* infobar,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  size_t position,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  bool animate,
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  CallbackStatus callback_status) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(std::find(infobars_.begin(), infobars_.end(), infobar) ==
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      infobars_.end());
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_LE(position, infobars_.size());
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  infobars_.insert(infobars_.begin() + position, infobar);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateInfoBarArrowTargetHeights();
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PlatformSpecificAddInfoBar(infobar, position);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (callback_status == WANT_CALLBACK)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    infobar->set_container(this);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  infobar->Show(animate);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (callback_status == NO_CALLBACK)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    infobar->set_container(this);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InfoBarContainer::UpdateInfoBarArrowTargetHeights() {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < infobars_.size(); ++i)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    infobars_[i]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i));
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int InfoBarContainer::ArrowTargetHeightForInfoBar(size_t infobar_index) const {
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!delegate_ || !delegate_->DrawInfoBarArrows(NULL))
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (infobar_index == 0)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return top_arrow_target_height_;
167d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const gfx::SlideAnimation& first_infobar_animation =
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const_cast<const InfoBar*>(infobars_.front())->animation();
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((infobar_index > 1) || first_infobar_animation.IsShowing())
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return InfoBar::kDefaultArrowTargetHeight;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When the first infobar is animating closed, we animate the second infobar's
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // arrow target height from the default to the top target height.  Note that
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the animation values here are going from 1.0 -> 0.0 as the top bar closes.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return top_arrow_target_height_ + static_cast<int>(
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (InfoBar::kDefaultArrowTargetHeight - top_arrow_target_height_) *
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          first_infobar_animation.GetCurrentValue());
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch}  // namespace infobars
180