172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/infobars/infobar_container.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/tab_contents/infobar_delegate.h" 8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/views/infobars/infobar.h" 9dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h" 10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h" 11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h" 12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/animation/slide_animation.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 14dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenInfoBarContainer::Delegate::~Delegate() { 15dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 16dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochInfoBarContainer::InfoBarContainer(Delegate* delegate) 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : delegate_(delegate), 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tab_contents_(NULL), 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen top_arrow_target_height_(InfoBar::kDefaultArrowTargetHeight) { 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochInfoBarContainer::~InfoBarContainer() { 24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // RemoveAllInfoBarsForDestruction() should have already cleared our infobars. 25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(infobars_.empty()); 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InfoBarContainer::ChangeTabContents(TabContents* contents) { 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.RemoveAll(); 30dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 31dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen while (!infobars_.empty()) { 32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen InfoBar* infobar = infobars_.front(); 33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // NULL the container pointer first so that if the infobar is currently 34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // animating, OnInfoBarAnimated() won't get called; we'll manually trigger 35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // this once for the whole set of changes below. This also prevents 36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // InfoBar::MaybeDelete() from calling RemoveInfoBar() a second time if the 37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // infobar happens to be at zero height (dunno if this can actually happen). 38dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen infobar->set_container(NULL); 39dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen RemoveInfoBar(infobar); 40dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 41dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tab_contents_ = contents; 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_contents_) { 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<TabContents> tc_source(tab_contents_); 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tc_source); 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tc_source); 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REPLACED, 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch tc_source); 51dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 52dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (size_t i = 0; i < tab_contents_->infobar_count(); ++i) { 53dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // As when we removed the infobars above, we prevent callbacks to 54dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // OnInfoBarAnimated() for each infobar. 55dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AddInfoBar(tab_contents_->GetInfoBarDelegateAt(i)->CreateInfoBar(), false, 56dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen NO_CALLBACK); 57dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 59dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 60dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Now that everything is up to date, signal the delegate to re-layout. 61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen OnInfoBarStateChanged(false); 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenint InfoBarContainer::GetVerticalOverlap(int* total_height) { 65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Our |total_height| is the sum of the preferred heights of the InfoBars 66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // contained within us plus the |vertical_overlap|. 67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int vertical_overlap = 0; 68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int next_infobar_y = 0; 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (InfoBars::iterator i(infobars_.begin()); i != infobars_.end(); ++i) { 71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen InfoBar* infobar = *i; 72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen next_infobar_y -= infobar->arrow_height(); 73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen vertical_overlap = std::max(vertical_overlap, -next_infobar_y); 74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen next_infobar_y += infobar->total_height(); 75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (total_height) 78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen *total_height = next_infobar_y + vertical_overlap; 79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return vertical_overlap; 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::SetMaxTopArrowHeight(int height) { 83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Decrease the height by the arrow stroke thickness, which is the separator 84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // line height, because the infobar arrow target heights are without-stroke. 85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen top_arrow_target_height_ = std::min( 86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::max(height - InfoBar::kSeparatorLineHeight, 0), 87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen InfoBar::kMaximumArrowTargetHeight); 88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UpdateInfoBarArrowTargetHeights(); 89dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 90dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::OnInfoBarStateChanged(bool is_animating) { 92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (delegate_) 93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen delegate_->InfoBarContainerStateChanged(is_animating); 9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::RemoveDelegate(InfoBarDelegate* delegate) { 97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen tab_contents_->RemoveInfoBar(delegate); 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::RemoveInfoBar(InfoBar* infobar) { 101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen InfoBars::iterator infobar_iterator(std::find(infobars_.begin(), 102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobars_.end(), infobar)); 103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(infobar_iterator != infobars_.end()); 104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PlatformSpecificRemoveInfoBar(infobar); 105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobars_.erase(infobar_iterator); 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::RemoveAllInfoBarsForDestruction() { 109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Before we remove any children, we reset |delegate_|, so that no removals 110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // will result in us trying to call 111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // delegate_->InfoBarContainerStateChanged(). This is important because at 112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // this point |delegate_| may be shutting down, and it's at best unimportant 113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // and at worst disastrous to call that. 114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen delegate_ = NULL; 115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ChangeTabContents(NULL); 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InfoBarContainer::Observe(NotificationType type, 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationSource& source, 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationDetails& details) { 121dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen switch (type.value) { 122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case NotificationType::TAB_CONTENTS_INFOBAR_ADDED: 123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AddInfoBar(Details<InfoBarDelegate>(details)->CreateInfoBar(), true, 124dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen WANT_CALLBACK); 125dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case NotificationType::TAB_CONTENTS_INFOBAR_REMOVED: 128dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen RemoveInfoBar(Details<InfoBarDelegate>(details).ptr(), true); 129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen case NotificationType::TAB_CONTENTS_INFOBAR_REPLACED: { 132dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen typedef std::pair<InfoBarDelegate*, InfoBarDelegate*> InfoBarPair; 133dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen InfoBarPair* infobar_pair = Details<InfoBarPair>(details).ptr(); 134dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen RemoveInfoBar(infobar_pair->first, false); 135dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen AddInfoBar(infobar_pair->second->CreateInfoBar(), false, WANT_CALLBACK); 136dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; 137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen default: 140dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen NOTREACHED(); 141dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen break; 142dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid InfoBarContainer::RemoveInfoBar(InfoBarDelegate* delegate, 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool use_animation) { 147dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Search for the infobar associated with |delegate|. We cannot search for 148dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // |delegate| in |tab_contents_|, because an InfoBar remains alive until its 149dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // close animation completes, while the delegate is removed from the tab 150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // immediately. 151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen for (InfoBars::iterator i(infobars_.begin()); i != infobars_.end(); ++i) { 152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen InfoBar* infobar = *i; 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (infobar->delegate() == delegate) { 154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // We merely need hide the infobar; it will call back to RemoveInfoBar() 155dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // itself once it's hidden. 156dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen infobar->Hide(use_animation); 157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UpdateInfoBarArrowTargetHeights(); 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 163dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid InfoBarContainer::AddInfoBar(InfoBar* infobar, 164dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bool animate, 165dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen CallbackStatus callback_status) { 166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(std::find(infobars_.begin(), infobars_.end(), infobar) == 167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobars_.end()); 168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobars_.push_back(infobar); 169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UpdateInfoBarArrowTargetHeights(); 170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PlatformSpecificAddInfoBar(infobar); 171dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (callback_status == WANT_CALLBACK) 172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobar->set_container(this); 173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobar->Show(animate); 174dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (callback_status == NO_CALLBACK) 175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobar->set_container(this); 176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid InfoBarContainer::UpdateInfoBarArrowTargetHeights() { 179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (size_t i = 0; i < infobars_.size(); ++i) 180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen infobars_[i]->SetArrowTargetHeight(ArrowTargetHeightForInfoBar(i)); 181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenint InfoBarContainer::ArrowTargetHeightForInfoBar(size_t infobar_index) const { 184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!delegate_->DrawInfoBarArrows(NULL)) 185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return 0; 186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (infobar_index == 0) 187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return top_arrow_target_height_; 188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const ui::SlideAnimation* first_infobar_animation = 189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const_cast<const InfoBar*>(infobars_.front())->animation(); 190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if ((infobar_index > 1) || first_infobar_animation->IsShowing()) 191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return InfoBar::kDefaultArrowTargetHeight; 192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // When the first infobar is animating closed, we animate the second infobar's 193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // arrow target height from the default to the top target height. Note that 194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the animation values here are going from 1.0 -> 0.0 as the top bar closes. 195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return top_arrow_target_height_ + static_cast<int>( 196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen (InfoBar::kDefaultArrowTargetHeight - top_arrow_target_height_) * 197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen first_infobar_animation->GetCurrentValue()); 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 199