diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-21 22:27:24 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-21 22:27:24 +0000 |
commit | 616ed5a653d4bc13b4633d7c7b85d2f0c73bfd72 (patch) | |
tree | 04b8cd92d18cd6e1ff54e46c6c32b0d70391a831 /chrome | |
parent | c7eeed7fa25e06ec3d0367a74d2b43671958c5d3 (diff) | |
download | chromium_src-616ed5a653d4bc13b4633d7c7b85d2f0c73bfd72.zip chromium_src-616ed5a653d4bc13b4633d7c7b85d2f0c73bfd72.tar.gz chromium_src-616ed5a653d4bc13b4633d7c7b85d2f0c73bfd72.tar.bz2 |
Beginnings of a new InfoBar system.
This implements AlertInfoBar and InfoBarContainer. It also makes the crashed plugin/js oom infobars use this new system.
Design Doc: http://dev.chromium.org/developers/design-documents/info-barshttp://crbug.com/4620
Review URL: http://codereview.chromium.org/11318
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5856 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/browser.vcproj | 8 | ||||
-rw-r--r-- | chrome/browser/infobar_delegate.cc | 54 | ||||
-rw-r--r-- | chrome/browser/infobar_delegate.h | 122 | ||||
-rw-r--r-- | chrome/browser/tab_contents.cc | 77 | ||||
-rw-r--r-- | chrome/browser/tab_contents.h | 32 | ||||
-rw-r--r-- | chrome/browser/views/browser_views.vcproj | 16 | ||||
-rw-r--r-- | chrome/browser/views/frame/browser_view.cc | 35 | ||||
-rw-r--r-- | chrome/browser/views/frame/browser_view.h | 4 | ||||
-rw-r--r-- | chrome/browser/views/infobars/infobar_container.cc | 131 | ||||
-rw-r--r-- | chrome/browser/views/infobars/infobar_container.h | 73 | ||||
-rw-r--r-- | chrome/browser/views/infobars/infobars.cc | 291 | ||||
-rw-r--r-- | chrome/browser/views/infobars/infobars.h | 138 | ||||
-rw-r--r-- | chrome/browser/web_contents.cc | 10 | ||||
-rw-r--r-- | chrome/browser/web_contents.h | 1 | ||||
-rw-r--r-- | chrome/common/notification_types.h | 22 |
15 files changed, 989 insertions, 25 deletions
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index f900e92..447618e 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -822,6 +822,14 @@ > </File> <File + RelativePath=".\infobar_delegate.cc" + > + </File> + <File + RelativePath=".\infobar_delegate.h" + > + </File> + <File RelativePath=".\ipc_status_view.cc" > </File> diff --git a/chrome/browser/infobar_delegate.cc b/chrome/browser/infobar_delegate.cc new file mode 100644 index 0000000..81965be --- /dev/null +++ b/chrome/browser/infobar_delegate.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/infobar_delegate.h" + +#include "base/logging.h" +#include "chrome/common/l10n_util.h" + +#include "generated_resources.h" + +// AlertInfoBarDelegate, InfoBarDelegate overrides: ---------------------------- + +bool AlertInfoBarDelegate::EqualsDelegate(InfoBarDelegate* delegate) const { + AlertInfoBarDelegate* alert_delegate = delegate->AsAlertInfoBarDelegate(); + if (!alert_delegate) + return false; + + return alert_delegate->GetMessageText() == GetMessageText(); +} + +// ConfirmInfoBarDelegate, public: --------------------------------------------- + +std::wstring ConfirmInfoBarDelegate::GetButtonLabel( + InfoBarButton button) const { + if (button == BUTTON_OK) + return l10n_util::GetString(IDS_OK); + if (button == BUTTON_CANCEL) + return l10n_util::GetString(IDS_CANCEL); + NOTREACHED(); + return std::wstring(); +} + +// SimpleAlertInfoBarDelegate, public: ----------------------------------------- + +SimpleAlertInfoBarDelegate::SimpleAlertInfoBarDelegate( + const std::wstring& message, SkBitmap* icon) + : message_(message), + icon_(icon) { +} + +// SimpleAlertInfoBarDelegate, AlertInfoBarDelegate implementation: ------------ + +std::wstring SimpleAlertInfoBarDelegate::GetMessageText() const { + return message_; +} + +SkBitmap* SimpleAlertInfoBarDelegate::GetIcon() const { + return icon_; +} + +void SimpleAlertInfoBarDelegate::InfoBarClosed() { + delete this; +} diff --git a/chrome/browser/infobar_delegate.h b/chrome/browser/infobar_delegate.h new file mode 100644 index 0000000..57b0737 --- /dev/null +++ b/chrome/browser/infobar_delegate.h @@ -0,0 +1,122 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_INFOBAR_DELEGATE_H_ +#define CHROME_BROWSER_INFOBAR_DELEGATE_H_ + +#include "base/basictypes.h" +#include "skia/include/SkBitmap.h" + +class AlertInfoBarDelegate; +class ConfirmInfoBarDelegate; +class InfoBar; + +// An interface implemented by objects wishing to control an InfoBar. +// Implementing this interface is not sufficient to use an InfoBar, since it +// does not map to a specific InfoBar type. Instead, you must implement either +// AlertInfoBarDelegate or ConfirmInfoBarDelegate, or override with your own +// delegate for your own InfoBar variety. +class InfoBarDelegate { + public: + // Returns true if the supplied |delegate| is equal to this one. Equality is + // left to the implementation to define. This function is called by the + // TabContents when determining whether or not a delegate should be added + // because a matching one already exists. If this function returns true, the + // TabContents will not add the new delegate because it considers one to + // already be present. + virtual bool EqualsDelegate(InfoBarDelegate* delegate) const { + return false; + } + + // Returns true if the InfoBar should be closed automatically after the page + // is navigated. + virtual bool ShouldCloseOnNavigate() const { return true; } + + // Called after the InfoBar is closed. The delegate is free to delete itself + // at this point. + virtual void InfoBarClosed() {} + + // Called to create the InfoBar. Implementation of this method is + // platform-specific. + virtual InfoBar* CreateInfoBar() = 0; + + // Returns a pointer to the ConfirmInfoBarDelegate interface, if implemented. + virtual AlertInfoBarDelegate* AsAlertInfoBarDelegate() { + return NULL; + } + + // Returns a pointer to the ConfirmInfoBarDelegate interface, if implemented. + virtual ConfirmInfoBarDelegate* AsConfirmInfoBarDelegate() { + return NULL; + } +}; + +// An interface derived from InfoBarDelegate implemented by objects wishing to +// control an AlertInfoBar. +class AlertInfoBarDelegate : public InfoBarDelegate { + public: + // Returns the message string to be displayed for the InfoBar. + virtual std::wstring GetMessageText() const = 0; + + // Return the icon to be shown for this InfoBar. If the returned bitmap is + // NULL, no icon is shown. + virtual SkBitmap* GetIcon() const { return NULL; } + + // Overridden from InfoBarDelegate: + virtual bool EqualsDelegate(InfoBarDelegate* delegate) const; + virtual InfoBar* CreateInfoBar(); + virtual AlertInfoBarDelegate* AsAlertInfoBarDelegate() { return this; } +}; + +// An interface derived from InfoBarDelegate implemented by objects wishing to +// control a ConfirmInfoBar. +class ConfirmInfoBarDelegate : public AlertInfoBarDelegate { + public: + enum InfoBarButton { + BUTTON_NONE = 0, + BUTTON_OK, + BUTTON_CANCEL + }; + + // Return the buttons to be shown for this InfoBar. + virtual int GetButtons() const { + return BUTTON_NONE; + } + + // Return the label for the specified button. The default implementation + // returns "OK" for the OK button and "Cancel" for the Cancel button. + virtual std::wstring GetButtonLabel(InfoBarButton button) const; + + // Called when the OK button is pressed. + virtual void Accept() {} + + // Called when the Cancel button is pressed. + virtual void Cancel() {} + + // Overridden from InfoBarDelegate: + virtual InfoBar* CreateInfoBar(); + virtual ConfirmInfoBarDelegate* AsConfirmInfoBarDelegate() { + return this; + } +}; + +// Simple implementations for common use cases --------------------------------- + +class SimpleAlertInfoBarDelegate : public AlertInfoBarDelegate { + public: + SimpleAlertInfoBarDelegate(const std::wstring& message, SkBitmap* icon); + + // Overridden from AlertInfoBarDelegate: + virtual std::wstring GetMessageText() const; + virtual SkBitmap* GetIcon() const; + virtual void InfoBarClosed(); + + private: + std::wstring message_; + SkBitmap* icon_; + + DISALLOW_COPY_AND_ASSIGN(SimpleAlertInfoBarDelegate); +}; + +#endif // #ifndef CHROME_BROWSER_INFOBAR_DELEGATE_H_ diff --git a/chrome/browser/tab_contents.cc b/chrome/browser/tab_contents.cc index e5131dc..510c611 100644 --- a/chrome/browser/tab_contents.cc +++ b/chrome/browser/tab_contents.cc @@ -6,6 +6,7 @@ #include "chrome/browser/cert_store.h" #include "chrome/browser/navigation_entry.h" +#include "chrome/browser/infobar_delegate.h" #include "chrome/browser/views/download_shelf_view.h" #include "chrome/browser/views/download_started_animation.h" #include "chrome/browser/views/blocked_popup_container.h" @@ -386,6 +387,48 @@ void TabContents::SetInitialFocus() { ::SetFocus(GetContainerHWND()); } +void TabContents::AddInfoBar(InfoBarDelegate* delegate) { + // Look through the existing InfoBarDelegates we have for a match. If we've + // already got one that matches, then we don't add the new one. + for (size_t i = 0; i < infobar_delegate_count(); ++i) { + if (GetInfoBarDelegateAt(i)->EqualsDelegate(delegate)) + return; + } + + infobar_delegates_.push_back(delegate); + NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_INFOBAR_ADDED, + Source<TabContents>(this), + Details<InfoBarDelegate>(delegate)); + + // Add ourselves as an observer for navigations the first time a delegate is + // added. We use this notification to expire InfoBars that need to expire on + // page transitions. + if (infobar_delegates_.size() == 1) { + NotificationService::current()->AddObserver( + this, NOTIFY_NAV_ENTRY_COMMITTED, + Source<NavigationController>(controller())); + } +} + +void TabContents::RemoveInfoBar(InfoBarDelegate* delegate) { + std::vector<InfoBarDelegate*>::iterator it = + find(infobar_delegates_.begin(), infobar_delegates_.end(), delegate); + if (it != infobar_delegates_.end()) { + InfoBarDelegate* delegate = *it; + infobar_delegates_.erase(it); + NotificationService::current()->Notify(NOTIFY_TAB_CONTENTS_INFOBAR_REMOVED, + Source<TabContents>(this), + Details<InfoBarDelegate>(delegate)); + } + + // Remove ourselves as an observer if we are tracking no more InfoBars. + if (infobar_delegates_.empty()) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_NAV_ENTRY_COMMITTED, + Source<NavigationController>(controller())); + } +} + void TabContents::SetDownloadShelfVisible(bool visible) { if (shelf_visible_ != visible) { if (visible) { @@ -467,6 +510,17 @@ void TabContents::DidMoveOrResize(ConstrainedWindow* window) { UpdateWindow(GetContainerHWND()); } +void TabContents::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_NAV_ENTRY_COMMITTED); + DCHECK(controller() == Source<NavigationController>(source).ptr()); + + NavigationController::LoadCommittedDetails& committed_details = + *(Details<NavigationController::LoadCommittedDetails>(details).ptr()); + ExpireInfoBars(committed_details); +} + // static void TabContents::MigrateShelfView(TabContents* from, TabContents* to) { bool was_shelf_visible = from->IsDownloadShelfVisible(); @@ -521,3 +575,26 @@ bool TabContents::ShowingBlockedPopupNotification() const { return blocked_popups_ != NULL && blocked_popups_->GetTabContentsCount() != 0; } + +namespace { +bool TransitionIsReload(PageTransition::Type transition) { + return PageTransition::StripQualifier(transition) == PageTransition::RELOAD; +} +} + +void TabContents::ExpireInfoBars( + const NavigationController::LoadCommittedDetails& details) { + // Only hide InfoBars when the user has done something that makes the main + // frame load. We don't want various automatic or subframe navigations making + // it disappear. + if (!details.is_user_initiated_main_frame_load()) + return; + + for (size_t i = 0; i < infobar_delegate_count(); ++i) { + InfoBarDelegate* delegate = GetInfoBarDelegateAt(i); + if (!TransitionIsReload(details.entry->transition_type()) && + delegate->ShouldCloseOnNavigate()) { + RemoveInfoBar(delegate); + } + } +} diff --git a/chrome/browser/tab_contents.h b/chrome/browser/tab_contents.h index ebbc47b..6893ee1 100644 --- a/chrome/browser/tab_contents.h +++ b/chrome/browser/tab_contents.h @@ -10,6 +10,7 @@ #include "chrome/browser/autocomplete/autocomplete_edit.h" #include "chrome/browser/constrained_window.h" +#include "chrome/browser/infobar_delegate.h" #include "chrome/browser/navigation_controller.h" #include "chrome/browser/page_navigator.h" #include "chrome/browser/tab_contents_type.h" @@ -53,7 +54,8 @@ class WebContents; // the NavigationController makes the active TabContents inactive, notifies the // TabContentsDelegate that the TabContents is being replaced, and then // activates the new TabContents. -class TabContents : public PageNavigator { +class TabContents : public PageNavigator, + public NotificationObserver { public: // Flags passed to the TabContentsDelegate.NavigationStateChanged to tell it // what has changed. Combine them to update more than one thing. @@ -382,6 +384,20 @@ class TabContents : public PageNavigator { // the focus is passed to the RootView. virtual views::RootView* GetContentsRootView() { return NULL; } + // Infobars ------------------------------------------------------------------ + + // Adds an InfoBar for the specified |delegate|. + void AddInfoBar(InfoBarDelegate* delegate); + + // Removes the InfoBar for the specified |delegate|. + void RemoveInfoBar(InfoBarDelegate* delegate); + + // Enumeration and access functions. + size_t infobar_delegate_count() const { return infobar_delegates_.size(); } + InfoBarDelegate* GetInfoBarDelegateAt(size_t index) { + return infobar_delegates_.at(index); + } + // Toolbars and such --------------------------------------------------------- // Returns whether the bookmark bar should be visible. @@ -417,6 +433,11 @@ class TabContents : public PageNavigator { void DidMoveOrResize(ConstrainedWindow* window); protected: + // NotificationObserver implementation: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + friend class NavigationController; // Used to access the child_windows_ (ConstrainedWindowList) for testing // automation purposes. @@ -470,6 +491,12 @@ class TabContents : public PageNavigator { bool ShowingBlockedPopupNotification() const; private: + // Expires InfoBars that need to be expired, according to the state carried + // in |details|, in response to a new NavigationEntry being committed (the + // user navigated to another page). + void ExpireInfoBars( + const NavigationController::LoadCommittedDetails& details); + // Data ---------------------------------------------------------------------- TabContentsType type_; @@ -512,6 +539,9 @@ class TabContents : public PageNavigator { // repositioning, etc. BlockedPopupContainer* blocked_popups_; + // Delegates for InfoBars associated with this TabContents. + std::vector<InfoBarDelegate*> infobar_delegates_; + DISALLOW_COPY_AND_ASSIGN(TabContents); }; diff --git a/chrome/browser/views/browser_views.vcproj b/chrome/browser/views/browser_views.vcproj index 5a543cd..f7ca623 100644 --- a/chrome/browser/views/browser_views.vcproj +++ b/chrome/browser/views/browser_views.vcproj @@ -216,6 +216,22 @@ RelativePath=".\info_bar_view.h" > </File> + <File + RelativePath=".\infobars\infobar_container.cc" + > + </File> + <File + RelativePath=".\infobars\infobar_container.h" + > + </File> + <File + RelativePath=".\infobars\infobars.cc" + > + </File> + <File + RelativePath=".\infobars\infobars.h" + > + </File> </Filter> <Filter Name="Options" diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index 8dbeb98..c3b5d07 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -25,6 +25,7 @@ #include "chrome/browser/views/frame/browser_frame.h" #include "chrome/browser/views/html_dialog_view.h" #include "chrome/browser/views/importer_view.h" +#include "chrome/browser/views/infobars/infobar_container.h" #include "chrome/browser/views/keyword_editor_view.h" #include "chrome/browser/views/password_manager_view.h" #include "chrome/browser/views/status_bubble.h" @@ -338,6 +339,9 @@ void BrowserView::Init() { toolbar_->Init(browser_->profile()); toolbar_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TOOLBAR)); + infobar_container_ = new InfoBarContainer(this); + AddChildView(infobar_container_); + contents_container_ = new TabContentsContainerView; set_contents_view(contents_container_); AddChildView(contents_container_); @@ -645,7 +649,9 @@ void BrowserView::TabSelectedAt(TabContents* old_contents, if (old_contents) old_contents->StoreFocus(); - // Tell the frame what happened so that the TabContents gets resized, etc. + // Update various elements that are interested in knowing the current + // TabContents. + infobar_container_->ChangeTabContents(new_contents); contents_container_->SetTabContents(new_contents); // TODO(beng): This should be called automatically by SetTabContents, but I // am striving for parity now rather than cleanliness. This is @@ -1055,7 +1061,7 @@ int BrowserView::LayoutBookmarkAndInfoBars(int top) { // the Bookmark bar isn't visible on all tabs, then we need to show the // Info bar _above_ the Bookmark bar, since the Bookmark bar is styled to // look like it's part of the New Tab Page... - if (active_info_bar_ && active_bookmark_bar_ && + if (active_bookmark_bar_ && bookmark_bar_view_->IsNewTabPage() && !bookmark_bar_view_->IsAlwaysShown()) { top = LayoutInfoBar(top); @@ -1078,13 +1084,21 @@ int BrowserView::LayoutBookmarkBar(int top) { return top; } int BrowserView::LayoutInfoBar(int top) { - if (SupportsWindowFeature(FEATURE_INFOBAR) && active_info_bar_) { - gfx::Size ps = active_info_bar_->GetPreferredSize(); - active_info_bar_->SetBounds(0, top, width(), ps.height()); + if (SupportsWindowFeature(FEATURE_INFOBAR)) { + // Layout the new infobar. + gfx::Size ps = infobar_container_->GetPreferredSize(); + infobar_container_->SetBounds(0, top, width(), ps.height()); top += ps.height(); - if (SupportsWindowFeature(FEATURE_BOOKMARKBAR) && active_bookmark_bar_ && - !show_bookmark_bar_pref_.GetValue()) { - top -= kSeparationLineHeight; + + if (active_info_bar_) { + // Layout the old infobar. + ps = active_info_bar_->GetPreferredSize(); + active_info_bar_->SetBounds(0, top, width(), ps.height()); + top += ps.height(); + if (SupportsWindowFeature(FEATURE_BOOKMARKBAR) && active_bookmark_bar_ && + !show_bookmark_bar_pref_.GetValue()) { + top -= kSeparationLineHeight; + } } } return top; @@ -1131,7 +1145,10 @@ bool BrowserView::MaybeShowInfoBar(TabContents* contents) { if (contents && contents->AsWebContents() && contents->AsWebContents()->view()->IsInfoBarVisible()) new_info_bar = contents->AsWebContents()->view()->GetInfoBarView(); - return UpdateChildViewAndLayout(new_info_bar, &active_info_bar_); + UpdateChildViewAndLayout(new_info_bar, &active_info_bar_); + + // TODO(beng): remove this function once the InfoBar rejiggering is complete. + return true; } bool BrowserView::MaybeShowDownloadShelf(TabContents* contents) { diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h index d777f5a..172a1db 100644 --- a/chrome/browser/views/frame/browser_view.h +++ b/chrome/browser/views/frame/browser_view.h @@ -22,6 +22,7 @@ class BookmarkBarView; class Browser; class BrowserToolbarView; class EncodingMenuControllerDelegate; +class InfoBarContainer; class Menu; class StatusBubble; class TabContentsContainerView; @@ -343,6 +344,9 @@ class BrowserView : public BrowserWindow, // The Bookmark Bar View for this window. Lazily created. scoped_ptr<BookmarkBarView> bookmark_bar_view_; + // The InfoBarContainer that contains InfoBars for the current tab. + InfoBarContainer* infobar_container_; + // The view that contains the selected TabContents. TabContentsContainerView* contents_container_; diff --git a/chrome/browser/views/infobars/infobar_container.cc b/chrome/browser/views/infobars/infobar_container.cc new file mode 100644 index 0000000..2fe7a35 --- /dev/null +++ b/chrome/browser/views/infobars/infobar_container.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/infobar_container.h" + +#include "chrome/browser/infobar_delegate.h" +#include "chrome/browser/tab_contents.h" +#include "chrome/browser/views/frame/browser_view.h" +#include "chrome/browser/views/infobars/infobars.h" +#include "chrome/common/notification_types.h" + +// InfoBarContainer, public: --------------------------------------------------- + +InfoBarContainer::InfoBarContainer(BrowserView* browser_view) + : browser_view_(browser_view), + tab_contents_(NULL) { + +} + +InfoBarContainer::~InfoBarContainer() { + ChangeTabContents(NULL); +} + +void InfoBarContainer::ChangeTabContents(TabContents* contents) { + if (tab_contents_) { + NotificationService::current()->RemoveObserver( + this, NOTIFY_TAB_CONTENTS_INFOBAR_ADDED, + Source<TabContents>(tab_contents_)); + NotificationService::current()->RemoveObserver( + this, NOTIFY_TAB_CONTENTS_INFOBAR_REMOVED, + Source<TabContents>(tab_contents_)); + } + tab_contents_ = contents; + if (tab_contents_) { + UpdateInfoBars(); + NotificationService::current()->AddObserver( + this, NOTIFY_TAB_CONTENTS_INFOBAR_ADDED, + Source<TabContents>(tab_contents_)); + NotificationService::current()->AddObserver( + this, NOTIFY_TAB_CONTENTS_INFOBAR_REMOVED, + Source<TabContents>(tab_contents_)); + } +} + +void InfoBarContainer::InfoBarAnimated(bool completed) { + browser_view_->SelectedTabToolbarSizeChanged(!completed); +} + +void InfoBarContainer::RemoveDelegate(InfoBarDelegate* delegate) { + tab_contents_->RemoveInfoBar(delegate); +} + +// InfoBarContainer, views::View overrides: ------------------------------------ + +gfx::Size InfoBarContainer::GetPreferredSize() { + // We do not have a preferred width (we will expand to fit the available width + // of the BrowserView). Our preferred height is the sum of the preferred + // heights of the InfoBars contained within us. + int height = 0; + for (int i = 0; i < GetChildViewCount(); ++i) + height += GetChildViewAt(i)->GetPreferredSize().height(); + return gfx::Size(0, height); +} + +void InfoBarContainer::Layout() { + int top = 0; + for (int i = 0; i < GetChildViewCount(); ++i) { + views::View* child = GetChildViewAt(i); + gfx::Size ps = child->GetPreferredSize(); + child->SetBounds(0, top, width(), ps.height()); + top += ps.height(); + } +} + +void InfoBarContainer::ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child) { + if (parent == this && child->GetParent() == this) { + // 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); + } +} + +// InfoBarContainer, NotificationObserver implementation: ---------------------- + +void InfoBarContainer::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NOTIFY_TAB_CONTENTS_INFOBAR_ADDED) { + AddInfoBar(Details<InfoBarDelegate>(details).ptr()); + } else if (type == NOTIFY_TAB_CONTENTS_INFOBAR_REMOVED) { + RemoveInfoBar(Details<InfoBarDelegate>(details).ptr()); + } else { + NOTREACHED(); + } +} + +// 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) { + InfoBarDelegate* delegate = tab_contents_->GetInfoBarDelegateAt(i); + InfoBar* infobar = delegate->CreateInfoBar(); + infobar->set_container(this); + AddChildView(infobar); + infobar->Open(); + } +} + +void InfoBarContainer::AddInfoBar(InfoBarDelegate* delegate) { + InfoBar* infobar = delegate->CreateInfoBar(); + infobar->set_container(this); + infobar->AnimateOpen(); + AddChildView(infobar); +} + +void InfoBarContainer::RemoveInfoBar(InfoBarDelegate* delegate) { + size_t index = 0; + for (; index < tab_contents_->infobar_delegate_count(); ++index) { + if (tab_contents_->GetInfoBarDelegateAt(index) == delegate) + break; + } + + // The View will be removed once the Close animation completes. + static_cast<InfoBar*>(GetChildViewAt(index))->AnimateClose(); +} diff --git a/chrome/browser/views/infobars/infobar_container.h b/chrome/browser/views/infobars/infobar_container.h new file mode 100644 index 0000000..43c5280 --- /dev/null +++ b/chrome/browser/views/infobars/infobar_container.h @@ -0,0 +1,73 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_CONTAINER_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_CONTAINER_H_ + +#include "chrome/common/notification_service.h" +#include "chrome/views/view.h" + +class BrowserView; +class InfoBarDelegate; +class TabContents; + +// A views::View subclass that contains a collection of InfoBars associated with +// a TabContents. +class InfoBarContainer : public views::View, + public NotificationObserver { + public: + explicit InfoBarContainer(BrowserView* browser_view); + virtual ~InfoBarContainer(); + + // Changes the TabContents for which this container is showing InfoBars. Can + // be NULL. + void ChangeTabContents(TabContents* contents); + + // Called by child InfoBars as they animate. If |completed| is true, the + // animation has finished running. + void InfoBarAnimated(bool completed); + + // Remove the specified InfoBarDelegate from the selected TabContents. This + // will notify us back and cause us to close the View. This is called from + // the InfoBar's close button handler. + void RemoveDelegate(InfoBarDelegate* delegate); + + // Overridden from views::View: + virtual gfx::Size GetPreferredSize(); + virtual void Layout(); + protected: + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); + + private: + // Overridden from NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Constructs the InfoBars needed to reflect the state of the current + // TabContents associated with this container. No animations are run during + // this process. + void UpdateInfoBars(); + + // Adds an InfoBar for the specified delegate, in response to a notification + // from the selected TabContents. The InfoBar's appearance will be animated. + void AddInfoBar(InfoBarDelegate* delegate); + + // Removes an InfoBar for the specified delegate, in response to a + // notification from the selected TabContents. The InfoBar's disappearance + // will be animated. + void RemoveInfoBar(InfoBarDelegate* delegate); + + // The BrowserView that hosts this InfoBarContainer. + BrowserView* browser_view_; + + // The TabContents for which we are currently showing InfoBars. + TabContents* tab_contents_; + + DISALLOW_COPY_AND_ASSIGN(InfoBarContainer); +}; + +#endif // #ifndef CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_CONTAINER_H_ diff --git a/chrome/browser/views/infobars/infobars.cc b/chrome/browser/views/infobars/infobars.cc new file mode 100644 index 0000000..5733d33 --- /dev/null +++ b/chrome/browser/views/infobars/infobars.cc @@ -0,0 +1,291 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/infobars.h" + +#include "chrome/app/theme/theme_resources.h" +#include "chrome/browser/views/infobars/infobar_container.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/slide_animation.h" +#include "chrome/views/background.h" +#include "chrome/views/button.h" +#include "chrome/views/image_view.h" +#include "chrome/views/label.h" + +#include "generated_resources.h" + +const double kInfoBarHeight = 37.0; + +static const int kVerticalPadding = 3; +static const int kHorizontalPadding = 3; +static const int kIconLabelSpacing = 5; +static const int kButtonSpacing = 5; + +static const SkColor kBackgroundColorTop = SkColorSetRGB(255, 242, 183); +static const SkColor kBackgroundColorBottom = SkColorSetRGB(250, 230, 145); + +static const int kSeparatorLineHeight = 1; +static const SkColor kSeparatorColor = SkColorSetRGB(165, 165, 165); + +namespace { +int OffsetY(views::View* parent, const gfx::Size prefsize) { + return std::max((parent->height() - prefsize.height()) / 2, 0); +} +} + +// InfoBarBackground ----------------------------------------------------------- + +class InfoBarBackground : public views::Background { + public: + InfoBarBackground() { + gradient_background_.reset( + views::Background::CreateVerticalGradientBackground( + kBackgroundColorTop, kBackgroundColorBottom)); + } + + // Overridden from views::View: + virtual void Paint(ChromeCanvas* canvas, views::View* view) const { + // First paint the gradient background. + gradient_background_->Paint(canvas, view); + + // Now paint the separator line. + canvas->FillRectInt(kSeparatorColor, 0, + view->height() - kSeparatorLineHeight, view->width(), + kSeparatorLineHeight); + } + + private: + scoped_ptr<views::Background> gradient_background_; + + DISALLOW_COPY_AND_ASSIGN(InfoBarBackground); +}; + +// InfoBar, public: ------------------------------------------------------------ + +InfoBar::InfoBar(InfoBarDelegate* delegate) + : delegate_(delegate), + close_button_(new views::Button) { + set_background(new InfoBarBackground); + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + close_button_->SetImage(views::Button::BS_NORMAL, + rb.GetBitmapNamed(IDR_CLOSE_BAR)); + close_button_->SetImage(views::Button::BS_HOT, + rb.GetBitmapNamed(IDR_CLOSE_BAR_H)); + close_button_->SetImage(views::Button::BS_PUSHED, + rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); + close_button_->SetListener(this, 0); + close_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE)); + AddChildView(close_button_); + + animation_.reset(new SlideAnimation(this)); + animation_->SetTweenType(SlideAnimation::NONE); +} + +InfoBar::~InfoBar() { +} + +void InfoBar::AnimateOpen() { + animation_->Show(); +} + +void InfoBar::Open() { + animation_->Reset(1.0); + animation_->Show(); +} + +void InfoBar::AnimateClose() { + animation_->Hide(); +} + +void InfoBar::Close() { + GetParent()->RemoveChildView(this); + if (delegate()) + delegate()->InfoBarClosed(); + delete this; +} + +// InfoBar, views::View overrides: --------------------------------------------- + +gfx::Size InfoBar::GetPreferredSize() { + int height = static_cast<int>(kInfoBarHeight * animation_->GetCurrentValue()); + return gfx::Size(0, height); +} + +void InfoBar::Layout() { + gfx::Size button_ps = close_button_->GetPreferredSize(); + close_button_->SetBounds(width() - kHorizontalPadding - button_ps.width(), + OffsetY(this, button_ps), button_ps.width(), + button_ps.height()); + +} + +// InfoBar, protected: --------------------------------------------------------- + +int InfoBar::GetAvailableWidth() const { + return close_button_->x() - kIconLabelSpacing; +} + +// InfoBar, views::BaseButton::ButtonListener implementation: ------------------ + +void InfoBar::ButtonPressed(views::BaseButton* sender) { + if (sender == close_button_) + container_->RemoveDelegate(delegate()); +} + +// InfoBar, AnimationDelegate implementation: ---------------------------------- + +void InfoBar::AnimationProgressed(const Animation* animation) { + container_->InfoBarAnimated(true); +} + +void InfoBar::AnimationEnded(const Animation* animation) { + container_->InfoBarAnimated(false); + + if (!animation_->IsShowing()) + Close(); +} + +// AlertInfoBar, public: ------------------------------------------------------- + +AlertInfoBar::AlertInfoBar(AlertInfoBarDelegate* delegate) + : InfoBar(delegate) { + label_ = new views::Label( + delegate->GetMessageText(), + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont)); + label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + AddChildView(label_); + + icon_ = new views::ImageView; + if (delegate->GetIcon()) + icon_->SetImage(delegate->GetIcon()); + AddChildView(icon_); +} + +AlertInfoBar::~AlertInfoBar() { + +} + +// AlertInfoBar, views::View overrides: ---------------------------------------- + +void AlertInfoBar::Layout() { + // Layout the close button. + InfoBar::Layout(); + + // Layout the icon and text. + gfx::Size icon_ps = icon_->GetPreferredSize(); + icon_->SetBounds(kHorizontalPadding, OffsetY(this, icon_ps), icon_ps.width(), + icon_ps.height()); + + gfx::Size text_ps = label_->GetPreferredSize(); + int text_width = + GetAvailableWidth() - icon_->bounds().right() - kIconLabelSpacing; + label_->SetBounds(icon_->bounds().right() + kIconLabelSpacing, + OffsetY(this, text_ps), text_width, text_ps.height()); +} + +// AlertInfoBar, private: ------------------------------------------------------ + +AlertInfoBarDelegate* AlertInfoBar::GetDelegate() { + return delegate()->AsAlertInfoBarDelegate(); +} + +// ConfirmInfoBar, public: ----------------------------------------------------- + +ConfirmInfoBar::ConfirmInfoBar(ConfirmInfoBarDelegate* delegate) + : ok_button_(NULL), + cancel_button_(NULL), + initialized_(false), + AlertInfoBar(delegate) { +} + +ConfirmInfoBar::~ConfirmInfoBar() { +} + +// ConfirmInfoBar, views::View overrides: -------------------------------------- + +void ConfirmInfoBar::Layout() { + InfoBar::Layout(); + int available_width = InfoBar::GetAvailableWidth(); + int ok_button_width = 0; + int cancel_button_width = 0; + gfx::Size ok_ps = ok_button_->GetPreferredSize(); + gfx::Size cancel_ps = cancel_button_->GetPreferredSize(); + + if (GetDelegate()->GetButtons() & ConfirmInfoBarDelegate::BUTTON_OK) + ok_button_width = ok_ps.width(); + if (GetDelegate()->GetButtons() & ConfirmInfoBarDelegate::BUTTON_CANCEL) + cancel_button_width = cancel_ps.width(); + + cancel_button_->SetBounds(available_width - cancel_button_width, + OffsetY(this, cancel_ps), cancel_ps.width(), + cancel_ps.height()); + 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) { + if (is_add && child == this && !initialized_) { + Init(); + initialized_ = true; + } +} + +// ConfirmInfoBar, views::NativeButton::Listener implementation: --------------- + +void ConfirmInfoBar::ButtonPressed(views::NativeButton* sender) { + if (sender == ok_button_) { + GetDelegate()->Accept(); + } else if (sender == cancel_button_) { + GetDelegate()->Cancel(); + } else { + NOTREACHED(); + } +} + +// ConfirmInfoBar, InfoBar overrides: ------------------------------------------ + +int ConfirmInfoBar::GetAvailableWidth() const { + if (ok_button_) + return ok_button_->x() - kButtonSpacing; + if (cancel_button_) + return cancel_button_->x() - kButtonSpacing; + return InfoBar::GetAvailableWidth(); +} + +// ConfirmInfoBar, private: ---------------------------------------------------- + +ConfirmInfoBarDelegate* ConfirmInfoBar::GetDelegate() { + return delegate()->AsConfirmInfoBarDelegate(); +} + +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_); +} + +// AlertInfoBarDelegate, InfoBarDelegate overrides: ---------------------------- + +InfoBar* AlertInfoBarDelegate::CreateInfoBar() { + return new AlertInfoBar(this); +} + +// ConfirmInfoBarDelegate, InfoBarDelegate overrides: -------------------------- + +InfoBar* ConfirmInfoBarDelegate::CreateInfoBar() { + return new ConfirmInfoBar(this); +} diff --git a/chrome/browser/views/infobars/infobars.h b/chrome/browser/views/infobars/infobars.h new file mode 100644 index 0000000..1e02f96 --- /dev/null +++ b/chrome/browser/views/infobars/infobars.h @@ -0,0 +1,138 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_INFOBARS_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_INFOBARS_H_ + +#include "chrome/browser/infobar_delegate.h" +#include "chrome/views/base_button.h" +#include "chrome/views/native_button.h" + +class InfoBarContainer; +class SlideAnimation; +namespace views { +class Button; +class ImageView; +class Label; +} + +// This file contains implementations for some general purpose InfoBars. See +// chrome/browser/infobar_delegate.h for the delegate interface(s) that you must +// implement to use these. + +class InfoBar : public views::View, + public views::BaseButton::ButtonListener, + public AnimationDelegate { + public: + explicit InfoBar(InfoBarDelegate* delegate); + virtual ~InfoBar(); + + InfoBarDelegate* delegate() const { return delegate_; } + + void set_container(InfoBarContainer* container) { container_ = container; } + + // Starts animating the InfoBar open. + void AnimateOpen(); + + // Opens the InfoBar immediately. + void Open(); + + // Starts animating the InfoBar closed. It will not be closed until the + // animation has completed, when |Close| will be called. + void AnimateClose(); + + // Closes the InfoBar immediately and removes it from its container. Notifies + // the delegate that it has closed. The InfoBar is deleted after this function + // is called. + void Close(); + + // Overridden from views::View: + virtual gfx::Size GetPreferredSize(); + virtual void Layout(); + + protected: + // Returns the available width of the View for use by child view layout, + // excluding the close button. + virtual int GetAvailableWidth() const; + + private: + // Overridden from views::Button::ButtonListener: + virtual void ButtonPressed(views::BaseButton* sender); + + // Overridden from AnimationDelegate: + virtual void AnimationProgressed(const Animation* animation); + virtual void AnimationEnded(const Animation* animation); + + // The InfoBar's container + InfoBarContainer* container_; + + // The InfoBar's delegate. + InfoBarDelegate* delegate_; + + // The Close Button at the right edge of the InfoBar. + views::Button* close_button_; + + // The animation that runs when the InfoBar is opened or closed. + scoped_ptr<SlideAnimation> animation_; + + DISALLOW_COPY_AND_ASSIGN(InfoBar); +}; + +class AlertInfoBar : public InfoBar { + public: + explicit AlertInfoBar(AlertInfoBarDelegate* delegate); + virtual ~AlertInfoBar(); + + // Overridden from views::View: + virtual void Layout(); + + protected: + views::Label* label() const { return label_; } + views::ImageView* icon() const { return icon_; } + + private: + AlertInfoBarDelegate* GetDelegate(); + + views::Label* label_; + views::ImageView* icon_; + + DISALLOW_COPY_AND_ASSIGN(AlertInfoBar); +}; + +class ConfirmInfoBar : public AlertInfoBar, + public views::NativeButton::Listener { + public: + explicit ConfirmInfoBar(ConfirmInfoBarDelegate* delegate); + virtual ~ConfirmInfoBar(); + + // Overridden from views::View: + virtual void Layout(); + + protected: + // Overridden from views::View: + virtual void ViewHierarchyChanged(bool is_add, + views::View* parent, + views::View* child); + + // Overridden from views::NativeButton::Listener: + virtual void ButtonPressed(views::NativeButton* sender); + + // Overridden from InfoBar: + virtual int GetAvailableWidth() const; + + private: + void Init(); + + ConfirmInfoBarDelegate* GetDelegate(); + + views::NativeButton* ok_button_; + views::NativeButton* cancel_button_; + + bool initialized_; + + DISALLOW_COPY_AND_ASSIGN(ConfirmInfoBar); +}; + + +#endif // #ifndef CHROME_BROWSER_VIEWS_INFOBARS_INFOBARS_H_ diff --git a/chrome/browser/web_contents.cc b/chrome/browser/web_contents.cc index a8fe7db..60ba95b4 100644 --- a/chrome/browser/web_contents.cc +++ b/chrome/browser/web_contents.cc @@ -1338,13 +1338,13 @@ void WebContents::OnCrashedPlugin(const std::wstring& plugin_path) { if (!product_name.empty()) plugin_name = product_name; } - view_->DisplayErrorInInfoBar( - l10n_util::GetStringF(IDS_PLUGIN_CRASHED_PROMPT, plugin_name)); + AddInfoBar(new SimpleAlertInfoBarDelegate( + l10n_util::GetStringF(IDS_PLUGIN_CRASHED_PROMPT, plugin_name), NULL)); } void WebContents::OnJSOutOfMemory() { - view_->DisplayErrorInInfoBar( - l10n_util::GetString(IDS_JS_OUT_OF_MEMORY_PROMPT)); + AddInfoBar(new SimpleAlertInfoBarDelegate( + l10n_util::GetString(IDS_JS_OUT_OF_MEMORY_PROMPT), NULL)); } bool WebContents::CanBlur() const { @@ -1477,7 +1477,7 @@ void WebContents::Observe(NotificationType type, break; } default: { - NOTREACHED(); + TabContents::Observe(type, source, details); break; } } diff --git a/chrome/browser/web_contents.h b/chrome/browser/web_contents.h index 71894de..75c5c689 100644 --- a/chrome/browser/web_contents.h +++ b/chrome/browser/web_contents.h @@ -30,7 +30,6 @@ class WebContents : public TabContents, public RenderViewHostDelegate, public RenderViewHostManager::Delegate, public SelectFileDialog::Listener, - public NotificationObserver, public WebApp::Observer { public: // If instance is NULL, then creates a new process for this view. Otherwise diff --git a/chrome/common/notification_types.h b/chrome/common/notification_types.h index 53e0954..c748d36 100644 --- a/chrome/common/notification_types.h +++ b/chrome/common/notification_types.h @@ -200,15 +200,19 @@ enum NotificationType { // No details are expected. NOTIFY_WEB_CONTENTS_DISCONNECTED, - // This message is sent when a new message is added to a WebContents. The - // source is a Source<WebContents> with a pointer to the WebContents the - // message was added to. - NOTIFY_WEB_CONTENTS_MESSAGE_ADDED, - - // This message is sent when a message is removed from a WebContents. The - // source is a Source<WebContents> with a pointer to the WebContents the - // message was removed from. - NOTIFY_WEB_CONTENTS_MESSAGE_REMOVED, + // This message is sent when a new InfoBar is added to a TabContents. The + // source is a Source<TabContents> with a pointer to the TabContents the + // InfoBar was added to. The details is a Details<InfoBarDelegate> with a + // pointer to an object implementing the InfoBarDelegate interface for the + // InfoBar that was added. + NOTIFY_TAB_CONTENTS_INFOBAR_ADDED, + + // This message is sent when an InfoBar is removed from a TabContents. The + // source is a Source<TabContents> with a pointer to the TabContents the + // InfoBar was removed from. The details is a Details<InfoBarDelegate> with a + // pointer to an object implementing the InfoBarDelegate interface for the + // InfoBar that was removed. + NOTIFY_TAB_CONTENTS_INFOBAR_REMOVED, // This is sent when an externally hosted tab is created. The details contain // the ExternalTabContainer that contains the tab |