summaryrefslogtreecommitdiffstats
path: root/chrome_frame/infobars
diff options
context:
space:
mode:
authorerikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-03 16:22:00 +0000
committererikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-03 16:22:00 +0000
commitd7b59a0c525807f1eaf0456c3db3b4f659bcccc4 (patch)
treee26dd48c52a7755ec84f9c44b8a8e03fc9d39dde /chrome_frame/infobars
parent385a2b73c0e717d27145521f7c3f557004e0453e (diff)
downloadchromium_src-d7b59a0c525807f1eaf0456c3db3b4f659bcccc4.zip
chromium_src-d7b59a0c525807f1eaf0456c3db3b4f659bcccc4.tar.gz
chromium_src-d7b59a0c525807f1eaf0456c3db3b4f659bcccc4.tar.bz2
Add an infobar facility to Chrome Frame. Allows clients to display and hide arbitrary content at the top or botom of an IE tab.
BUG=None TEST=chrome_frame_unittests --gtest_filter=Infobars* Review URL: http://codereview.chromium.org/4766003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68176 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/infobars')
-rw-r--r--chrome_frame/infobars/infobar_content.h50
-rw-r--r--chrome_frame/infobars/infobar_manager.cc102
-rw-r--r--chrome_frame/infobars/infobar_manager.h50
-rw-r--r--chrome_frame/infobars/internal/displaced_window_manager.cc42
-rw-r--r--chrome_frame/infobars/internal/displaced_window_manager.h45
-rw-r--r--chrome_frame/infobars/internal/host_window_manager.cc119
-rw-r--r--chrome_frame/infobars/internal/host_window_manager.h48
-rw-r--r--chrome_frame/infobars/internal/infobar_window.cc183
-rw-r--r--chrome_frame/infobars/internal/infobar_window.h108
-rw-r--r--chrome_frame/infobars/internal/subclassing_window_with_delegate.h107
10 files changed, 854 insertions, 0 deletions
diff --git a/chrome_frame/infobars/infobar_content.h b/chrome_frame/infobars/infobar_content.h
new file mode 100644
index 0000000..fd80cd0
--- /dev/null
+++ b/chrome_frame/infobars/infobar_content.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 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_FRAME_INFOBARS_INFOBAR_CONTENT_H_
+#define CHROME_FRAME_INFOBARS_INFOBAR_CONTENT_H_
+
+#include <windows.h>
+
+// Provides an interface between content to be displayed in an infobar and the
+// infobar facility. Pass an instance of your implementation to
+// InfobarManager::Show, which will result in a call to InstallInFrame to
+// initialize the InfobarContent.
+//
+// The instance will be deleted by the infobar facility when it is no longer
+// being displayed (or immediately, in the case of a failure to display).
+class InfobarContent {
+ public:
+ // Provides access to the content's parent window and allows the
+ // InfobarContent to close itself, such as in response to user interaction.
+ class Frame {
+ public:
+ virtual ~Frame() {}
+
+ // Returns the window in which the content should display itself.
+ virtual HWND GetFrameWindow() = 0;
+
+ // Initiates closing of the infobar.
+ virtual void CloseInfobar() = 0;
+ }; // class Frame
+
+ virtual ~InfobarContent() {}
+
+ // Prepares the content to display in the provided frame.
+ //
+ // The frame pointer remains valid until the InfobarContent instance is
+ // deleted.
+ virtual bool InstallInFrame(Frame* frame) = 0;
+
+ // Provides the content with the dimensions available to it for display.
+ // Dimensions are relative to the origin of the frame window.
+ virtual void SetDimensions(const RECT& dimensions) = 0;
+
+ // Finds the desired value for one dimension given a fixed value for the other
+ // dimension. The fixed dimension parameter is non-zero whereas the requested
+ // dimension parameter will be zero.
+ virtual size_t GetDesiredSize(size_t width, size_t height) = 0;
+}; // class InfobarContent
+
+#endif // CHROME_FRAME_INFOBARS_INFOBAR_CONTENT_H_
diff --git a/chrome_frame/infobars/infobar_manager.cc b/chrome_frame/infobars/infobar_manager.cc
new file mode 100644
index 0000000..7d8e072
--- /dev/null
+++ b/chrome_frame/infobars/infobar_manager.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2010 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_frame/infobars/infobar_manager.h"
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "chrome_frame/infobars/internal/host_window_manager.h"
+#include "chrome_frame/infobars/internal/infobar_window.h"
+
+// Connects InfobarWindow to HostWindowManager, and exposes the result as an
+// InfobarManager.
+class InfobarManagerImpl
+ : public InfobarManager,
+ public InfobarWindow::Host,
+ public HostWindowManager::Delegate {
+ public:
+ explicit InfobarManagerImpl(HostWindowManager* manager);
+
+ // Implementation of InfobarManager
+ virtual bool Show(InfobarContent* content, InfobarType type);
+ virtual void Hide(InfobarType type);
+ virtual void HideAll();
+
+ // Implementation of HostWindowManager::Delegate
+ virtual void AdjustDisplacedWindowDimensions(RECT* rect);
+
+ // Implementation of InfobarWindow::Host
+ virtual HWND GetContainerWindow();
+ virtual void UpdateLayout();
+
+ private:
+ // Not owned by this instance.
+ HostWindowManager* manager_;
+ // Infobar windows.
+ scoped_ptr<InfobarWindow> infobars_[END_OF_INFOBAR_TYPE];
+ DISALLOW_COPY_AND_ASSIGN(InfobarManagerImpl);
+}; // class InfobarManagerImpl
+
+InfobarManagerImpl::InfobarManagerImpl(HostWindowManager* manager)
+ : manager_(manager) {
+ for (int index = 0; index < END_OF_INFOBAR_TYPE; ++index) {
+ infobars_[index].reset(
+ new InfobarWindow(static_cast<InfobarType>(index)));
+ infobars_[index]->SetHost(this);
+ }
+}
+
+bool InfobarManagerImpl::Show(InfobarContent* content, InfobarType type) {
+ DCHECK(type >= FIRST_INFOBAR_TYPE && type < END_OF_INFOBAR_TYPE);
+ return infobars_[type]->Show(content);
+}
+
+void InfobarManagerImpl::Hide(InfobarType type) {
+ DCHECK(type >= FIRST_INFOBAR_TYPE && type < END_OF_INFOBAR_TYPE);
+ infobars_[type]->Hide();
+}
+
+void InfobarManagerImpl::HideAll() {
+ for (int index = 0; index < END_OF_INFOBAR_TYPE; ++index)
+ Hide(static_cast<InfobarType>(index));
+}
+
+void InfobarManagerImpl::AdjustDisplacedWindowDimensions(RECT* rect) {
+ for (int index = 0; index < END_OF_INFOBAR_TYPE; ++index) {
+ if (infobars_[index] != NULL)
+ infobars_[index]->ReserveSpace(rect);
+ }
+}
+
+HWND InfobarManagerImpl::GetContainerWindow() {
+ return *manager_;
+}
+
+void InfobarManagerImpl::UpdateLayout() {
+ manager_->UpdateLayout();
+}
+
+InfobarManager::~InfobarManager() {
+}
+
+InfobarManager* InfobarManager::Get(HWND tab_window) {
+ HostWindowManager::Delegate* delegate =
+ HostWindowManager::GetDelegateForHwnd(tab_window);
+
+ if (delegate != NULL)
+ return static_cast<InfobarManagerImpl*>(delegate);
+
+ scoped_ptr<HostWindowManager> host_manager(new HostWindowManager());
+
+ // Transferred to host_manager in call to Initialize.
+ InfobarManagerImpl* infobar_manager = new InfobarManagerImpl(
+ host_manager.get());
+
+ if (host_manager->Initialize(tab_window, infobar_manager)) {
+ host_manager.release(); // takes ownership of itself
+ return infobar_manager;
+ }
+
+ return NULL;
+}
diff --git a/chrome_frame/infobars/infobar_manager.h b/chrome_frame/infobars/infobar_manager.h
new file mode 100644
index 0000000..e4dd3b7
--- /dev/null
+++ b/chrome_frame/infobars/infobar_manager.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 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_FRAME_INFOBARS_INFOBAR_MANAGER_H_
+#define CHROME_FRAME_INFOBARS_INFOBAR_MANAGER_H_
+
+#include <windows.h>
+
+class InfobarContent;
+
+enum InfobarType {
+ FIRST_INFOBAR_TYPE = 0,
+ TOP_INFOBAR = 0, // Infobar at the top.
+ BOTTOM_INFOBAR = 1, // Infobar at the bottom.
+ END_OF_INFOBAR_TYPE = 2
+};
+
+// Creates and manages infobars at the top or bottom of an IE content window.
+// Instances must only be retrieved and used within the UI thread of the IE
+// content window.
+class InfobarManager {
+ public:
+ // Returns an InfobarManager for the specified IE tab window. Caller does not
+ // own the pointer (resources will be freed when the window is destroyed).
+ //
+ // The pointer may be invalidated by further processing of window events, and
+ // as such should be immediately discarded after use.
+ //
+ // Returns NULL in case of failure.
+ static InfobarManager* Get(HWND tab_window);
+
+ virtual ~InfobarManager();
+
+ // Shows the supplied content in an infobar of the specified type.
+ // Normally, InfobarContent::InstallInFrame will be called with an interface
+ // the content may use to interact with the Infobar facility.
+ //
+ // InfobarContent is deleted when the Infobar facility is finished with the
+ // content (either through failure or when successfully hidden).
+ virtual bool Show(InfobarContent* content, InfobarType type) = 0;
+
+ // Hides the infobar of the specified type.
+ virtual void Hide(InfobarType type) = 0;
+
+ // Hides all infobars.
+ virtual void HideAll() = 0;
+}; // class InfobarManager
+
+#endif // CHROME_FRAME_INFOBARS_INFOBAR_MANAGER_H_
diff --git a/chrome_frame/infobars/internal/displaced_window_manager.cc b/chrome_frame/infobars/internal/displaced_window_manager.cc
new file mode 100644
index 0000000..0afce36
--- /dev/null
+++ b/chrome_frame/infobars/internal/displaced_window_manager.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2010 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_frame/infobars/internal/displaced_window_manager.h"
+
+DisplacedWindowManager::DisplacedWindowManager() {
+}
+
+void DisplacedWindowManager::UpdateLayout() {
+ // Call SetWindowPos with SWP_FRAMECHANGED for displaced window.
+ // Displaced window will receive WM_NCCALCSIZE to recalculate its client size.
+ ::SetWindowPos(m_hWnd,
+ NULL, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
+ SWP_FRAMECHANGED);
+}
+
+LRESULT DisplacedWindowManager::OnNcCalcSize(BOOL calc_valid_rects,
+ LPARAM lparam) {
+ // Ask the original window proc to calculate the 'natural' size of the window.
+ LRESULT ret = DefWindowProc(WM_NCCALCSIZE,
+ static_cast<WPARAM>(calc_valid_rects), lparam);
+ if (lparam == NULL)
+ return ret;
+
+ // Whether calc_valid_rects is true or false, we could treat beginning of
+ // lparam as a RECT object.
+ RECT* rect = reinterpret_cast<RECT*>(lparam);
+ RECT natural_rect = *rect;
+ if (delegate() != NULL)
+ delegate()->AdjustDisplacedWindowDimensions(rect);
+
+ // If we modified the window's dimensions, there is no way to respect a custom
+ // "client-area preservation strategy", so we must force a redraw to be safe.
+ if (calc_valid_rects && ret == WVR_VALIDRECTS &&
+ !EqualRect(&natural_rect, rect)) {
+ ret = WVR_REDRAW;
+ }
+
+ return ret;
+}
diff --git a/chrome_frame/infobars/internal/displaced_window_manager.h b/chrome_frame/infobars/internal/displaced_window_manager.h
new file mode 100644
index 0000000..3110abd
--- /dev/null
+++ b/chrome_frame/infobars/internal/displaced_window_manager.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2010 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_FRAME_INFOBARS_INTERNAL_DISPLACED_WINDOW_MANAGER_H_
+#define CHROME_FRAME_INFOBARS_INTERNAL_DISPLACED_WINDOW_MANAGER_H_
+
+#include <atlbase.h>
+#include <atlcrack.h>
+#include <atlwin.h>
+
+#include "base/basictypes.h"
+#include "chrome_frame/infobars/internal/subclassing_window_with_delegate.h"
+
+// DisplacedWindowManager observes the HWND passed to Initialize and:
+// 1) Intercepts NCCALCSIZE events allowing the client to modify the window's
+// requested dimensions.
+// 2) Allows the client to request a recalculation of the window's dimensions
+// (resulting in a deferred callback as in [1]).
+// 3) Is destroyed only when the window is destroyed.
+class DisplacedWindowManager
+ : public SubclassingWindowWithDelegate<DisplacedWindowManager> {
+ public:
+ DisplacedWindowManager();
+
+ // Triggers an immediate re-evaluation of the dimensions of the displaced
+ // window. Delegate::AdjustDisplacedWindowDimensions will be called with the
+ // natural dimensions of the displaced window.
+ void UpdateLayout();
+
+ BEGIN_MSG_MAP_EX(DisplacedWindowManager)
+ MSG_WM_NCCALCSIZE(OnNcCalcSize)
+ CHAIN_MSG_MAP(SubclassingWindowWithDelegate<DisplacedWindowManager>)
+ END_MSG_MAP()
+
+ private:
+ // The size of the displaced window is being calculated. Allow
+ // InfobarWindows to reserve a part of the space for themselves, if they are
+ // visible.
+ LRESULT OnNcCalcSize(BOOL calc_valid_rects, LPARAM lparam);
+
+ DISALLOW_COPY_AND_ASSIGN(DisplacedWindowManager);
+}; // class DisplacedWindowManager
+
+#endif // CHROME_FRAME_INFOBARS_INTERNAL_DISPLACED_WINDOW_MANAGER_H_
diff --git a/chrome_frame/infobars/internal/host_window_manager.cc b/chrome_frame/infobars/internal/host_window_manager.cc
new file mode 100644
index 0000000..34bcf90
--- /dev/null
+++ b/chrome_frame/infobars/internal/host_window_manager.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2010 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_frame/infobars/internal/host_window_manager.h"
+
+#include "chrome_frame/infobars/internal/displaced_window_manager.h"
+
+namespace {
+
+const wchar_t kIeTabContentParentWindowClass[] = L"Shell DocObject View";
+
+} // namespace
+
+// Receives notification when the displaced window is destroyed, and forwards
+// displaced window dimensions on to HostWindowManager::Delegate.
+class HostWindowManager::DisplacedWindowDelegate
+ : public DisplacedWindowManager::Delegate {
+ public:
+ explicit DisplacedWindowDelegate(HostWindowManager* manager);
+ virtual ~DisplacedWindowDelegate();
+
+ // DisplacedWindowManager::Delegate implementation
+ virtual void AdjustDisplacedWindowDimensions(RECT* rect);
+
+ private:
+ HostWindowManager* manager_; // Not owned by this instance
+ DISALLOW_COPY_AND_ASSIGN(DisplacedWindowDelegate);
+}; // class HostWindowManager::DisplacedWindowDelegate
+
+HostWindowManager::DisplacedWindowDelegate::DisplacedWindowDelegate(
+ HostWindowManager* manager) : manager_(manager) {
+}
+
+// Called when the displaced window is destroyed. Try to find a new displaced
+// window.
+HostWindowManager::DisplacedWindowDelegate::~DisplacedWindowDelegate() {
+ HWND old_window = *manager_->displaced_window_manager_;
+ // Will be deleted in its OnFinalMessage
+ manager_->displaced_window_manager_ = NULL;
+
+ // Check to see if a new window has already been created.
+ if (manager_->FindDisplacedWindow(old_window))
+ manager_->UpdateLayout();
+}
+
+// Forward this on to our delegate
+void HostWindowManager::DisplacedWindowDelegate::
+ AdjustDisplacedWindowDimensions(RECT* rect) {
+ manager_->delegate()->AdjustDisplacedWindowDimensions(rect);
+}
+
+// Callback function for EnumChildWindows (looks for a window with class
+// kIeTabContentParentWindowClass).
+//
+// lparam must point to an HWND that is either NULL or the HWND of the displaced
+// window that is being destroyed. We will ignore that window if we come across
+// it, and update lparam to point to the new displaced window if it is found.
+static BOOL CALLBACK FindDisplacedWindowProc(HWND hwnd, LPARAM lparam) {
+ DCHECK(lparam != NULL);
+ HWND* window_handle = reinterpret_cast<HWND*>(lparam);
+
+ if (hwnd == *window_handle)
+ return TRUE; // Skip this, it's the old displaced window.
+
+ // Variable to hold the class name. The size does not matter as long as it
+ // is at least can hold kIeTabContentParentWindowClass.
+ wchar_t class_name[100];
+ if (::GetClassName(hwnd, class_name, arraysize(class_name)) &&
+ lstrcmpi(kIeTabContentParentWindowClass, class_name) == 0) {
+ // We found the window. Return its handle and stop enumeration.
+ *window_handle = hwnd;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+HostWindowManager::HostWindowManager() : displaced_window_manager_(NULL) {
+}
+
+HostWindowManager::~HostWindowManager() {
+ // If we are holding a displaced_window_manager_, it means that
+ // OnDisplacedWindowManagerDestroyed has not been called yet, and therefore
+ // our DisplacedWindowDelegate might still be around, ready to invoke us.
+ // Fail fast to prevent a call into lala-land.
+ CHECK(displaced_window_manager_ == NULL);
+}
+
+void HostWindowManager::UpdateLayout() {
+ if (FindDisplacedWindow(NULL))
+ displaced_window_manager_->UpdateLayout();
+}
+
+bool HostWindowManager::FindDisplacedWindow(HWND old_window) {
+ if (displaced_window_manager_ == NULL ||
+ *displaced_window_manager_ == old_window) {
+ // Find the window which is the container for the HTML view (parent of
+ // the content). When the displaced window is destroyed, the new one might
+ // already exist, so we say "find a displaced window that is not this
+ // (old) one".
+ HWND displaced_window = old_window;
+ ::EnumChildWindows(*this, FindDisplacedWindowProc,
+ reinterpret_cast<LPARAM>(&displaced_window));
+
+ if (displaced_window == old_window) {
+ LOG(ERROR) << "Failed to locate IE renderer HWND to displace for "
+ << "Infobar installation.";
+ } else {
+ scoped_ptr<DisplacedWindowManager> displaced_window_manager(
+ new DisplacedWindowManager());
+ if (displaced_window_manager->Initialize(
+ displaced_window, new DisplacedWindowDelegate(this))) {
+ displaced_window_manager_ = displaced_window_manager.release();
+ }
+ }
+ }
+
+ return displaced_window_manager_ != NULL;
+}
diff --git a/chrome_frame/infobars/internal/host_window_manager.h b/chrome_frame/infobars/internal/host_window_manager.h
new file mode 100644
index 0000000..cf2abac
--- /dev/null
+++ b/chrome_frame/infobars/internal/host_window_manager.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 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_FRAME_INFOBARS_INTERNAL_HOST_WINDOW_MANAGER_H_
+#define CHROME_FRAME_INFOBARS_INTERNAL_HOST_WINDOW_MANAGER_H_
+
+#include "base/basictypes.h"
+#include "chrome_frame/infobars/internal/subclassing_window_with_delegate.h"
+
+class DisplacedWindowManager;
+
+// HostWindowManager observes the HWND passed to Initialize and:
+// 1) Monitors the lifecycle of a specific child window (as identified by
+// FindDisplacedWindow).
+// 2) Intercepts NCCALCSIZE events on the child window, allowing the client to
+// modify the child window's requested dimensions.
+// 3) Allows the client to request a recalculation of the child window's
+// dimensions (resulting in a callback as in [2]).
+//
+// See documentation of SubclasingWindowWithDelegate for further information.
+class HostWindowManager
+ : public SubclassingWindowWithDelegate<HostWindowManager> {
+ public:
+ HostWindowManager();
+ virtual ~HostWindowManager();
+
+ // Triggers an immediate re-evaluation of the dimensions of the displaced
+ // window. Delegate::AdjustDisplacedWindowDimensions will be called with the
+ // natural dimensions of the displaced window.
+ void UpdateLayout();
+
+ private:
+ class DisplacedWindowDelegate;
+ friend class DisplacedWindowDelegate;
+
+ // Finds the window to be displaced and instantiate a DisplacedWindowManager
+ // for it if one does not already exist. Returns true if
+ // displaced_window_manager_ is non-NULL at the end of the call.
+ bool FindDisplacedWindow(HWND old_window);
+
+ // Subclasses and observes changes to the displaced window.
+ DisplacedWindowManager* displaced_window_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostWindowManager);
+}; // class HostWindowManager
+
+#endif // CHROME_FRAME_INFOBARS_INTERNAL_HOST_WINDOW_MANAGER_H_
diff --git a/chrome_frame/infobars/internal/infobar_window.cc b/chrome_frame/infobars/internal/infobar_window.cc
new file mode 100644
index 0000000..92c332a
--- /dev/null
+++ b/chrome_frame/infobars/internal/infobar_window.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2010 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.
+//
+// Implementation of the manager for infobar windows.
+
+#include "chrome_frame/infobars/internal/infobar_window.h"
+
+#include <algorithm>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "chrome_frame/function_stub.h"
+
+namespace {
+
+// length of each step when opening or closing
+const UINT kInfobarSlidingTimerIntervalMs = 50U;
+// pixels per step, when opening or closing
+const int kInfobarSlideOpenStep = 2;
+const int kInfobarSlideCloseStep = 6;
+
+} // namespace
+
+void OnSliderTimer(InfobarWindow::Host* host) {
+ host->UpdateLayout();
+}
+
+InfobarWindow::InfobarWindow(InfobarType type)
+ : type_(type),
+ host_(NULL),
+ target_height_(0),
+ initial_height_(0),
+ current_height_(0),
+ current_width_(0),
+ timer_id_(0),
+ timer_stub_(NULL),
+ frame_impl_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ DCHECK(type_ >= FIRST_INFOBAR_TYPE);
+ DCHECK(type_ < END_OF_INFOBAR_TYPE);
+}
+
+InfobarWindow::~InfobarWindow() {
+ StopTimer();
+ if (timer_stub_ != NULL)
+ FunctionStub::Destroy(timer_stub_);
+}
+
+void InfobarWindow::SetHost(Host* host) {
+ DCHECK(host_ == NULL);
+ DCHECK(timer_stub_ == NULL);
+ DCHECK(host != NULL);
+ host_ = host;
+ timer_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(host),
+ OnSliderTimer);
+}
+
+bool InfobarWindow::Show(InfobarContent* content) {
+ DCHECK(host_ != NULL);
+ if (host_ == NULL)
+ return false;
+
+ scoped_ptr<InfobarContent> new_content(content);
+ content_.reset();
+
+ if (!new_content->InstallInFrame(&frame_impl_))
+ return false;
+
+ // Force a call to ReserveSpace, which will capture the width of the displaced
+ // window.
+ if (current_width_ == 0)
+ host_->UpdateLayout();
+ if (current_width_ == 0)
+ return false; // Might not be any displaced window.. then we can't display.
+
+ content_.swap(new_content);
+ StartSlidingTowards(content_->GetDesiredSize(current_width_, 0));
+
+ return true;
+}
+
+void InfobarWindow::Hide() {
+ DCHECK(host_ != NULL);
+ if (host_ == NULL)
+ return;
+
+ StartSlidingTowards(0);
+}
+
+void InfobarWindow::ReserveSpace(RECT* rect) {
+ DCHECK(rect);
+ DCHECK(host_ != NULL);
+ if (rect == NULL || host_ == NULL)
+ return;
+
+ current_width_ = rect->right - rect->left;
+ current_height_ = CalculateHeight();
+
+ RECT infobar_rect = *rect;
+
+ switch (type_) {
+ case TOP_INFOBAR:
+ infobar_rect.bottom = rect->top + current_height_;
+ rect->top = std::min(rect->bottom, infobar_rect.bottom);
+ break;
+ case BOTTOM_INFOBAR:
+ infobar_rect.top = rect->bottom - current_height_;
+ rect->bottom = std::max(rect->top, infobar_rect.top);
+ break;
+ default:
+ NOTREACHED() << "Unknown InfobarType value.";
+ break;
+ }
+
+ if (content_ != NULL)
+ content_->SetDimensions(infobar_rect);
+
+ // Done sliding?
+ if (current_height_ == target_height_) {
+ StopTimer();
+ if (current_height_ == 0)
+ content_.reset();
+ }
+}
+
+void InfobarWindow::StartSlidingTowards(int target_height) {
+ initial_height_ = current_height_;
+ target_height_ = target_height;
+
+ if (StartTimer())
+ slide_start_ = base::Time::Now();
+ else
+ slide_start_ = base::Time(); // NULL time means don't slide, resize now
+
+ // Trigger an immediate re-laying out. The timer will handle remaining steps.
+ host_->UpdateLayout();
+}
+
+bool InfobarWindow::StartTimer() {
+ timer_id_ = ::SetTimer(NULL,
+ timer_id_,
+ kInfobarSlidingTimerIntervalMs,
+ reinterpret_cast<TIMERPROC>(timer_stub_->code()));
+
+ DPLOG_IF(ERROR, timer_id_ == 0) << "Failure in SetTimer.";
+
+ return timer_id_ != 0;
+}
+
+void InfobarWindow::StopTimer() {
+ ::KillTimer(NULL, timer_id_);
+}
+
+int InfobarWindow::CalculateHeight() {
+ if (slide_start_.is_null())
+ return target_height_;
+
+ base::TimeDelta elapsed = base::Time::Now() - slide_start_;
+ int elapsed_steps = static_cast<int>(elapsed.InMilliseconds()) /
+ kInfobarSlidingTimerIntervalMs;
+
+ if (initial_height_ < target_height_) {
+ return std::min(initial_height_ + elapsed_steps * kInfobarSlideOpenStep,
+ target_height_);
+ } else if (initial_height_ > target_height_) {
+ return std::max(initial_height_ - elapsed_steps * kInfobarSlideCloseStep,
+ target_height_);
+ } else {
+ return target_height_;
+ }
+}
+
+InfobarWindow::FrameImpl::FrameImpl(InfobarWindow* infobar_window)
+ : infobar_window_(infobar_window) {
+}
+
+HWND InfobarWindow::FrameImpl::GetFrameWindow() {
+ return infobar_window_->host_->GetContainerWindow();
+}
+
+void InfobarWindow::FrameImpl::CloseInfobar() {
+ infobar_window_->Hide();
+}
diff --git a/chrome_frame/infobars/internal/infobar_window.h b/chrome_frame/infobars/internal/infobar_window.h
new file mode 100644
index 0000000..cc81bcc
--- /dev/null
+++ b/chrome_frame/infobars/internal/infobar_window.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2010 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_FRAME_INFOBARS_INTERNAL_INFOBAR_WINDOW_H_
+#define CHROME_FRAME_INFOBARS_INTERNAL_INFOBAR_WINDOW_H_
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+
+#include "chrome_frame/infobars/infobar_content.h"
+#include "chrome_frame/infobars/infobar_manager.h"
+
+struct FunctionStub;
+
+// Manages the display of an InfobarContent instance within a container window.
+// Positions the infobar content by displacing other "natural" content of the
+// window (see ReserveSpace). Allows positioning either above or below the
+// natural content.
+class InfobarWindow {
+ public:
+ // Integrates the InfobarWindow with its environment.
+ class Host {
+ public:
+ virtual ~Host() {}
+
+ // Returns a handle to the window within which infobar content should be
+ // created. All windows associated with the infobar content should be
+ // descendants of the container window.
+ virtual HWND GetContainerWindow() = 0;
+
+ // Triggers an immediate re-evaluation of the dimensions of the displaced
+ // content. InfobarWindow::ReserveSpace will be called with the natural
+ // dimensions of the displaced content.
+ virtual void UpdateLayout() = 0;
+ }; // class Host
+
+ explicit InfobarWindow(InfobarType type);
+ ~InfobarWindow();
+
+ void SetHost(Host* host);
+
+ // Shows the supplied content in this InfobarWindow. Normally,
+ // InfobarContent::InstallInFrame will be called with an InfobarContent::Frame
+ // instance the content may use to interact with the InfobarWindow.
+ //
+ // InfobarContent is deleted when the InfobarWindow is finished with the
+ // content (either through failure or when successfully hidden).
+ bool Show(InfobarContent* content);
+
+ // Hides the infobar.
+ void Hide();
+
+ // Receives the total space requested by the displaced content and subtracts
+ // any space required by this infobar. Passes the reserved dimensions to
+ // InfobarContent::SetDimensions.
+ void ReserveSpace(RECT* rect);
+
+ private:
+ // Provides InfobarContent with access to this InfobarWindow.
+ class FrameImpl : public InfobarContent::Frame {
+ public:
+ explicit FrameImpl(InfobarWindow* infobar_window);
+
+ // InfobarContent::Frame implementation
+ virtual HWND GetFrameWindow();
+ virtual void CloseInfobar();
+
+ private:
+ InfobarWindow* infobar_window_;
+ DISALLOW_COPY_AND_ASSIGN(FrameImpl);
+ }; // class FrameImpl
+
+ // Sets up our state to show or hide and calls Host::UpdateLayout to cause a
+ // call to ReserveSpace. Sets up a timer to periodically call UpdateLayout.
+ void StartSlidingTowards(int height);
+
+ // Based on the initial height, how long (and if) we have been sliding, and
+ // the target height, decides what the current height should be.
+ int CalculateHeight();
+
+ // Manage a timer that repeatedly calls Host::UpdateLayout
+ bool StartTimer();
+ void StopTimer();
+
+ scoped_ptr<InfobarContent> content_;
+ Host* host_;
+ FrameImpl frame_impl_;
+ InfobarType type_;
+ int current_width_;
+ int current_height_;
+
+ // These variables control our state for sliding and are used to calculate
+ // the desired height at any given time.
+ base::Time slide_start_; // When we started sliding, or the null time if we
+ // are not sliding.
+ int initial_height_; // Where we started sliding from
+ int target_height_; // Where we are sliding to
+
+ // ID and thunk for the slide-effect timer
+ int timer_id_;
+ FunctionStub* timer_stub_;
+
+ DISALLOW_COPY_AND_ASSIGN(InfobarWindow);
+}; // class InfobarWindow
+
+#endif // CHROME_FRAME_INFOBARS_INTERNAL_INFOBAR_WINDOW_H_
diff --git a/chrome_frame/infobars/internal/subclassing_window_with_delegate.h b/chrome_frame/infobars/internal/subclassing_window_with_delegate.h
new file mode 100644
index 0000000..0321f2c9
--- /dev/null
+++ b/chrome_frame/infobars/internal/subclassing_window_with_delegate.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2010 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_FRAME_INFOBARS_INTERNAL_SUBCLASSING_WINDOW_WITH_DELEGATE_H_
+#define CHROME_FRAME_INFOBARS_INTERNAL_SUBCLASSING_WINDOW_WITH_DELEGATE_H_
+
+#include <atlbase.h>
+#include <atlcrack.h>
+#include <atlwin.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "chrome_frame/utils.h"
+
+// Implements behavior common to HostWindowManager and DisplacedWindowManager.
+template<typename T> class SubclassingWindowWithDelegate
+ : public CWindowImpl<T> {
+ public:
+ // Allows clients to modify the dimensions of the displaced window.
+ // Through its destructor, allows clients to know when the subclassed window
+ // is destroyed.
+ class Delegate {
+ public:
+ // The delegate will be deleted when the subclassed window is destroyed.
+ virtual ~Delegate() {}
+
+ // Receives the natural dimensions of the displaced window. Upon return,
+ // rect should contain the adjusted dimensions (i.e., possibly reduced to
+ // accomodate an infobar).
+ virtual void AdjustDisplacedWindowDimensions(RECT* rect) = 0;
+ }; // class Delegate
+
+ SubclassingWindowWithDelegate() {}
+
+ // Returns true if the window is successfully subclassed, in which case this
+ // instance will take responsibility for its own destruction when the window
+ // is destroyed. If this method returns false, the caller should delete the
+ // instance immediately.
+ //
+ // Takes ownership of delegate in either case, deleting it when the window
+ // is destroyed (or immediately, in case of failure).
+ bool Initialize(HWND hwnd, Delegate* delegate) {
+ DCHECK(delegate != NULL);
+ DCHECK(delegate_ == NULL);
+ scoped_ptr<Delegate> new_delegate(delegate);
+
+ if (!::IsWindow(hwnd) || !SubclassWindow(hwnd)) {
+ PLOG(ERROR) << "Failed to subclass an HWND";
+ return false;
+ }
+
+ // Ensure we won't be unloaded while our window proc is attached to the tab
+ // window.
+ PinModule();
+
+ delegate_.swap(new_delegate);
+
+ return true;
+ }
+
+ // Returns the delegate associated with the specified window, if any.
+ static Delegate* GetDelegateForHwnd(HWND hwnd) {
+ return reinterpret_cast<Delegate*>(
+ ::SendMessage(hwnd, RegisterGetDelegateMessage(), NULL, NULL));
+ }
+
+ BEGIN_MSG_MAP_EX(SubclassingWindowWithDelegate)
+ MESSAGE_HANDLER(RegisterGetDelegateMessage(), OnGetDelegate)
+ MSG_WM_DESTROY(OnDestroy)
+ END_MSG_MAP()
+
+ // This instance is now free to be deleted.
+ virtual void OnFinalMessage(HWND hwnd) {
+ delete this;
+ }
+
+ protected:
+ scoped_ptr<Delegate>& delegate() { return delegate_; }
+
+ private:
+ // Registers a unique ID for our custom event.
+ static UINT RegisterGetDelegateMessage() {
+ static UINT message_id(
+ RegisterWindowMessage(L"SubclassingWindowWithDelegate::OnGetDelegate"));
+ return message_id;
+ }
+
+ // The subclassed window has been destroyed. Delete the delegate. We will
+ // delete ourselves in OnFinalMessage.
+ void OnDestroy() {
+ delegate_.reset();
+ }
+
+ LRESULT OnGetDelegate(UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ BOOL& handled) {
+ return reinterpret_cast<LRESULT>(delegate_.get());
+ }
+
+ scoped_ptr<Delegate> delegate_;
+ DISALLOW_COPY_AND_ASSIGN(SubclassingWindowWithDelegate);
+}; // class SubclassingWindowWithDelegate
+
+#endif // CHROME_FRAME_INFOBARS_INTERNAL_SUBCLASSING_WINDOW_WITH_DELEGATE_H_