diff options
author | erikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-03 16:22:00 +0000 |
---|---|---|
committer | erikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-03 16:22:00 +0000 |
commit | d7b59a0c525807f1eaf0456c3db3b4f659bcccc4 (patch) | |
tree | e26dd48c52a7755ec84f9c44b8a8e03fc9d39dde /chrome_frame/infobars | |
parent | 385a2b73c0e717d27145521f7c3f557004e0453e (diff) | |
download | chromium_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.h | 50 | ||||
-rw-r--r-- | chrome_frame/infobars/infobar_manager.cc | 102 | ||||
-rw-r--r-- | chrome_frame/infobars/infobar_manager.h | 50 | ||||
-rw-r--r-- | chrome_frame/infobars/internal/displaced_window_manager.cc | 42 | ||||
-rw-r--r-- | chrome_frame/infobars/internal/displaced_window_manager.h | 45 | ||||
-rw-r--r-- | chrome_frame/infobars/internal/host_window_manager.cc | 119 | ||||
-rw-r--r-- | chrome_frame/infobars/internal/host_window_manager.h | 48 | ||||
-rw-r--r-- | chrome_frame/infobars/internal/infobar_window.cc | 183 | ||||
-rw-r--r-- | chrome_frame/infobars/internal/infobar_window.h | 108 | ||||
-rw-r--r-- | chrome_frame/infobars/internal/subclassing_window_with_delegate.h | 107 |
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_ |