diff options
Diffstat (limited to 'chrome/browser/views/infobars')
-rw-r--r-- | chrome/browser/views/infobars/infobar_container.cc | 19 | ||||
-rw-r--r-- | chrome/browser/views/infobars/infobars.cc | 111 | ||||
-rw-r--r-- | chrome/browser/views/infobars/infobars.h | 29 |
3 files changed, 133 insertions, 26 deletions
diff --git a/chrome/browser/views/infobars/infobar_container.cc b/chrome/browser/views/infobars/infobar_container.cc index 2fe7a35..3a79464 100644 --- a/chrome/browser/views/infobars/infobar_container.cc +++ b/chrome/browser/views/infobars/infobar_container.cc @@ -19,6 +19,10 @@ InfoBarContainer::InfoBarContainer(BrowserView* browser_view) } InfoBarContainer::~InfoBarContainer() { + // We NULL this pointer before resetting the TabContents to prevent view + // hierarchy modifications from attempting to adjust the BrowserView, which is + // in the process of shutting down. + browser_view_ = NULL; ChangeTabContents(NULL); } @@ -31,6 +35,9 @@ void InfoBarContainer::ChangeTabContents(TabContents* contents) { this, NOTIFY_TAB_CONTENTS_INFOBAR_REMOVED, Source<TabContents>(tab_contents_)); } + // No need to delete the child views here, their removal from the view + // hierarchy does this automatically (see InfoBar::InfoBarRemoved). + RemoveAllChildViews(false); tab_contents_ = contents; if (tab_contents_) { UpdateInfoBars(); @@ -44,7 +51,8 @@ void InfoBarContainer::ChangeTabContents(TabContents* contents) { } void InfoBarContainer::InfoBarAnimated(bool completed) { - browser_view_->SelectedTabToolbarSizeChanged(!completed); + if (browser_view_) + browser_view_->SelectedTabToolbarSizeChanged(!completed); } void InfoBarContainer::RemoveDelegate(InfoBarDelegate* delegate) { @@ -76,7 +84,7 @@ void InfoBarContainer::Layout() { void InfoBarContainer::ViewHierarchyChanged(bool is_add, views::View* parent, views::View* child) { - if (parent == this && child->GetParent() == this) { + if (parent == this && child->GetParent() == this && browser_view_) { // An InfoBar child was added or removed. Tell the BrowserView it needs to // re-layout since our preferred size will have changed. browser_view_->SelectedTabToolbarSizeChanged(false); @@ -100,10 +108,7 @@ void InfoBarContainer::Observe(NotificationType type, // InfoBarContainer, private: -------------------------------------------------- void InfoBarContainer::UpdateInfoBars() { - // Clear out all the old child views. - RemoveAllChildViews(true); - - for (size_t i = 0; i < tab_contents_->infobar_delegate_count(); ++i) { + for (int i = 0; i < tab_contents_->infobar_delegate_count(); ++i) { InfoBarDelegate* delegate = tab_contents_->GetInfoBarDelegateAt(i); InfoBar* infobar = delegate->CreateInfoBar(); infobar->set_container(this); @@ -120,7 +125,7 @@ void InfoBarContainer::AddInfoBar(InfoBarDelegate* delegate) { } void InfoBarContainer::RemoveInfoBar(InfoBarDelegate* delegate) { - size_t index = 0; + int index = 0; for (; index < tab_contents_->infobar_delegate_count(); ++index) { if (tab_contents_->GetInfoBarDelegateAt(index) == delegate) break; diff --git a/chrome/browser/views/infobars/infobars.cc b/chrome/browser/views/infobars/infobars.cc index 5733d33..466a74f 100644 --- a/chrome/browser/views/infobars/infobars.cc +++ b/chrome/browser/views/infobars/infobars.cc @@ -11,8 +11,10 @@ #include "chrome/common/slide_animation.h" #include "chrome/views/background.h" #include "chrome/views/button.h" +#include "chrome/views/external_focus_tracker.h" #include "chrome/views/image_view.h" #include "chrome/views/label.h" +#include "chrome/views/widget.h" #include "generated_resources.h" @@ -66,7 +68,11 @@ class InfoBarBackground : public views::Background { InfoBar::InfoBar(InfoBarDelegate* delegate) : delegate_(delegate), - close_button_(new views::Button) { + close_button_(new views::Button), + delete_factory_(this) { + // We delete ourselves when we're removed from the view hierarchy. + SetParentOwned(false); + set_background(new InfoBarBackground); ResourceBundle& rb = ResourceBundle::GetSharedInstance(); @@ -97,14 +103,19 @@ void InfoBar::Open() { } void InfoBar::AnimateClose() { + DestroyFocusTracker(true); animation_->Hide(); } void InfoBar::Close() { GetParent()->RemoveChildView(this); - if (delegate()) - delegate()->InfoBarClosed(); - delete this; + // Note that we only tell the delegate we're closed here, and not when we're + // simply destroyed (by virtue of a tab switch or being moved from window to + // window), since this action can cause the delegate to destroy itself. + if (delegate_) { + delegate_->InfoBarClosed(); + delegate_ = NULL; + } } // InfoBar, views::View overrides: --------------------------------------------- @@ -122,6 +133,17 @@ void InfoBar::Layout() { } +void InfoBar::ViewHierarchyChanged(bool is_add, views::View* parent, + views::View* child) { + if (child == this) { + if (is_add) { + InfoBarAdded(); + } else { + InfoBarRemoved(); + } + } +} + // InfoBar, protected: --------------------------------------------------------- int InfoBar::GetAvailableWidth() const { @@ -138,14 +160,58 @@ void InfoBar::ButtonPressed(views::BaseButton* sender) { // InfoBar, AnimationDelegate implementation: ---------------------------------- void InfoBar::AnimationProgressed(const Animation* animation) { - container_->InfoBarAnimated(true); + if (container_) + container_->InfoBarAnimated(true); } void InfoBar::AnimationEnded(const Animation* animation) { - container_->InfoBarAnimated(false); + if (container_) { + container_->InfoBarAnimated(false); + + if (!animation_->IsShowing()) + Close(); + } +} + +// InfoBar, private: ----------------------------------------------------------- + +void InfoBar::InfoBarAdded() { + // The container_ pointer must be set before adding to the view hierarchy. + DCHECK(container_); + // When we're added to a view hierarchy within a widget, we create an + // external focus tracker to track what was focused in case we obtain + // focus so that we can restore focus when we're removed. + views::Widget* widget = GetWidget(); + if (widget) { + focus_tracker_.reset( + new views::ExternalFocusTracker(this, + views::FocusManager::GetFocusManager(widget->GetHWND()))); + } +} - if (!animation_->IsShowing()) - Close(); +void InfoBar::InfoBarRemoved() { + DestroyFocusTracker(false); + // NULL our container_ pointer so that if Animation::Stop results in + // AnimationEnded being called, we do not try and delete ourselves twice. + container_ = NULL; + animation_->Stop(); + // Finally, clean ourselves up when we're removed from the view hierarchy + // since no-one refers to us now. + MessageLoop::current()->PostTask(FROM_HERE, + delete_factory_.NewRunnableMethod(&InfoBar::DeleteSelf)); +} + +void InfoBar::DestroyFocusTracker(bool restore_focus) { + if (focus_tracker_.get()) { + if (restore_focus) + focus_tracker_->FocusLastFocusedExternalView(); + focus_tracker_->SetFocusManager(NULL); + focus_tracker_.reset(NULL); + } +} + +void InfoBar::DeleteSelf() { + delete this; } // AlertInfoBar, public: ------------------------------------------------------- @@ -199,6 +265,12 @@ ConfirmInfoBar::ConfirmInfoBar(ConfirmInfoBarDelegate* delegate) cancel_button_(NULL), initialized_(false), AlertInfoBar(delegate) { + ok_button_ = new views::NativeButton( + delegate->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_OK)); + ok_button_->SetListener(this); + cancel_button_ = new views::NativeButton( + delegate->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_CANCEL)); + cancel_button_->SetListener(this); } ConfirmInfoBar::~ConfirmInfoBar() { @@ -214,10 +286,16 @@ void ConfirmInfoBar::Layout() { gfx::Size ok_ps = ok_button_->GetPreferredSize(); gfx::Size cancel_ps = cancel_button_->GetPreferredSize(); - if (GetDelegate()->GetButtons() & ConfirmInfoBarDelegate::BUTTON_OK) + if (GetDelegate()->GetButtons() & ConfirmInfoBarDelegate::BUTTON_OK) { ok_button_width = ok_ps.width(); - if (GetDelegate()->GetButtons() & ConfirmInfoBarDelegate::BUTTON_CANCEL) - cancel_button_width = cancel_ps.width(); + } else { + ok_button_->SetVisible(false); + } + if (GetDelegate()->GetButtons() & ConfirmInfoBarDelegate::BUTTON_CANCEL) { + cancel_button_width = cancel_ps.width(); + } else { + cancel_button_->SetVisible(false); + } cancel_button_->SetBounds(available_width - cancel_button_width, OffsetY(this, cancel_ps), cancel_ps.width(), @@ -225,13 +303,13 @@ void ConfirmInfoBar::Layout() { int spacing = cancel_button_width > 0 ? kButtonSpacing : 0; ok_button_->SetBounds(cancel_button_->x() - spacing - ok_button_width, OffsetY(this, ok_ps), ok_ps.width(), ok_ps.height()); - AlertInfoBar::Layout(); } void ConfirmInfoBar::ViewHierarchyChanged(bool is_add, views::View* parent, views::View* child) { + InfoBar::ViewHierarchyChanged(is_add, parent, child); if (is_add && child == this && !initialized_) { Init(); initialized_ = true; @@ -243,8 +321,10 @@ void ConfirmInfoBar::ViewHierarchyChanged(bool is_add, void ConfirmInfoBar::ButtonPressed(views::NativeButton* sender) { if (sender == ok_button_) { GetDelegate()->Accept(); + AnimateClose(); } else if (sender == cancel_button_) { GetDelegate()->Cancel(); + AnimateClose(); } else { NOTREACHED(); } @@ -267,14 +347,7 @@ ConfirmInfoBarDelegate* ConfirmInfoBar::GetDelegate() { } void ConfirmInfoBar::Init() { - ok_button_ = new views::NativeButton( - GetDelegate()->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_OK)); - ok_button_->SetListener(this); AddChildView(ok_button_); - - cancel_button_ = new views::NativeButton( - GetDelegate()->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_CANCEL)); - cancel_button_->SetListener(this); AddChildView(cancel_button_); } diff --git a/chrome/browser/views/infobars/infobars.h b/chrome/browser/views/infobars/infobars.h index 1e02f96..f2ccc73 100644 --- a/chrome/browser/views/infobars/infobars.h +++ b/chrome/browser/views/infobars/infobars.h @@ -13,6 +13,7 @@ class InfoBarContainer; class SlideAnimation; namespace views { class Button; +class ExternalFocusTracker; class ImageView; class Label; } @@ -30,6 +31,8 @@ class InfoBar : public views::View, InfoBarDelegate* delegate() const { return delegate_; } + // Set a link to the parent InfoBarContainer. This must be set before the + // InfoBar is added to the view hierarchy. void set_container(InfoBarContainer* container) { container_ = container; } // Starts animating the InfoBar open. @@ -52,6 +55,11 @@ class InfoBar : public views::View, virtual void Layout(); protected: + // Overridden from views::View: + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); + // Returns the available width of the View for use by child view layout, // excluding the close button. virtual int GetAvailableWidth() const; @@ -64,6 +72,20 @@ class InfoBar : public views::View, virtual void AnimationProgressed(const Animation* animation); virtual void AnimationEnded(const Animation* animation); + // Called when an InfoBar is added or removed from a view hierarchy to do + // setup and shutdown. + void InfoBarAdded(); + void InfoBarRemoved(); + + // Destroys the external focus tracker, if present. If |restore_focus| is + // true, restores focus to the view tracked by the focus tracker before doing + // so. + void DestroyFocusTracker(bool restore_focus); + + // Deletes this object (called after a return to the message loop to allow + // the stack in ViewHierarchyChanged to unwind). + void DeleteSelf(); + // The InfoBar's container InfoBarContainer* container_; @@ -76,6 +98,13 @@ class InfoBar : public views::View, // The animation that runs when the InfoBar is opened or closed. scoped_ptr<SlideAnimation> animation_; + // Tracks and stores the last focused view which is not the InfoBar or any of + // its children. Used to restore focus once the InfoBar is closed. + scoped_ptr<views::ExternalFocusTracker> focus_tracker_; + + // Used to delete this object after a return to the message loop. + ScopedRunnableMethodFactory<InfoBar> delete_factory_; + DISALLOW_COPY_AND_ASSIGN(InfoBar); }; |