1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome_frame/infobars/internal/host_window_manager.h"
6
7#include "chrome_frame/infobars/internal/displaced_window_manager.h"
8
9namespace {
10
11const wchar_t kIeTabContentParentWindowClass[] = L"Shell DocObject View";
12
13}  // namespace
14
15// Receives notification when the displaced window is destroyed, and forwards
16// displaced window dimensions on to HostWindowManager::Delegate.
17class HostWindowManager::DisplacedWindowDelegate
18    : public DisplacedWindowManager::Delegate {
19 public:
20  explicit DisplacedWindowDelegate(HostWindowManager* manager);
21  virtual ~DisplacedWindowDelegate();
22
23  // DisplacedWindowManager::Delegate implementation
24  virtual void AdjustDisplacedWindowDimensions(RECT* rect);
25
26 private:
27  HostWindowManager* manager_;  // Not owned by this instance
28  DISALLOW_COPY_AND_ASSIGN(DisplacedWindowDelegate);
29};  // class HostWindowManager::DisplacedWindowDelegate
30
31HostWindowManager::DisplacedWindowDelegate::DisplacedWindowDelegate(
32    HostWindowManager* manager) : manager_(manager) {
33}
34
35// Called when the displaced window is destroyed. Try to find a new displaced
36// window.
37HostWindowManager::DisplacedWindowDelegate::~DisplacedWindowDelegate() {
38  HWND old_window = *manager_->displaced_window_manager_;
39  // Will be deleted in its OnFinalMessage
40  manager_->displaced_window_manager_ = NULL;
41
42  // Check to see if a new window has already been created.
43  if (manager_->FindDisplacedWindow(old_window))
44    manager_->UpdateLayout();
45}
46
47// Forward this on to our delegate
48void HostWindowManager::DisplacedWindowDelegate::
49    AdjustDisplacedWindowDimensions(RECT* rect) {
50  manager_->delegate()->AdjustDisplacedWindowDimensions(rect);
51}
52
53// Callback function for EnumChildWindows (looks for a window with class
54// kIeTabContentParentWindowClass).
55//
56// lparam must point to an HWND that is either NULL or the HWND of the displaced
57// window that is being destroyed. We will ignore that window if we come across
58// it, and update lparam to point to the new displaced window if it is found.
59static BOOL CALLBACK FindDisplacedWindowProc(HWND hwnd, LPARAM lparam) {
60  DCHECK(lparam != NULL);
61  HWND* window_handle = reinterpret_cast<HWND*>(lparam);
62
63  if (hwnd == *window_handle)
64    return TRUE;  // Skip this, it's the old displaced window.
65
66  // Variable to hold the class name. The size does not matter as long as it
67  // is at least can hold kIeTabContentParentWindowClass.
68  wchar_t class_name[100];
69  if (::GetClassName(hwnd, class_name, arraysize(class_name)) &&
70      lstrcmpi(kIeTabContentParentWindowClass, class_name) == 0) {
71    // We found the window. Return its handle and stop enumeration.
72    *window_handle = hwnd;
73    return FALSE;
74  }
75  return TRUE;
76}
77
78HostWindowManager::HostWindowManager() : displaced_window_manager_(NULL) {
79}
80
81HostWindowManager::~HostWindowManager() {
82  // If we are holding a displaced_window_manager_, it means that
83  // OnDisplacedWindowManagerDestroyed has not been called yet, and therefore
84  // our DisplacedWindowDelegate might still be around, ready to invoke us.
85  // Fail fast to prevent a call into lala-land.
86  CHECK(displaced_window_manager_ == NULL);
87}
88
89void HostWindowManager::UpdateLayout() {
90  if (FindDisplacedWindow(NULL))
91    displaced_window_manager_->UpdateLayout();
92}
93
94bool HostWindowManager::FindDisplacedWindow(HWND old_window) {
95  if (displaced_window_manager_ == NULL ||
96      *displaced_window_manager_ == old_window) {
97    // Find the window which is the container for the HTML view (parent of
98    // the content). When the displaced window is destroyed, the new one might
99    // already exist, so we say "find a displaced window that is not this
100    // (old) one".
101    HWND displaced_window = old_window;
102    ::EnumChildWindows(*this, FindDisplacedWindowProc,
103                       reinterpret_cast<LPARAM>(&displaced_window));
104
105    if (displaced_window == old_window) {
106      LOG(ERROR) << "Failed to locate IE renderer HWND to displace for "
107                 << "Infobar installation.";
108    } else {
109      scoped_ptr<DisplacedWindowManager> displaced_window_manager(
110          new DisplacedWindowManager());
111      if (displaced_window_manager->Initialize(
112              displaced_window, new DisplacedWindowDelegate(this))) {
113        displaced_window_manager_ = displaced_window_manager.release();
114      }
115    }
116  }
117
118  return displaced_window_manager_ != NULL;
119}
120