summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/infobars
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views/infobars')
-rw-r--r--chrome/browser/views/infobars/infobar_container.cc19
-rw-r--r--chrome/browser/views/infobars/infobars.cc111
-rw-r--r--chrome/browser/views/infobars/infobars.h29
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);
};