1 // Copyright (c) 2011 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 // Implementation of the manager for infobar windows.
6
7 #include "chrome_frame/infobars/internal/infobar_window.h"
8
9 #include <algorithm>
10
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "chrome_frame/function_stub.h"
14
15 namespace {
16
17 // length of each step when opening or closing
18 const UINT kInfobarSlidingTimerIntervalMs = 50U;
19 // pixels per step, when opening or closing
20 const int kInfobarSlideOpenStep = 2;
21 const int kInfobarSlideCloseStep = 6;
22
23 } // namespace
24
OnSliderTimer(InfobarWindow::Host * host,HWND,UINT,UINT_PTR,DWORD)25 VOID CALLBACK OnSliderTimer(InfobarWindow::Host* host,
26 HWND /*hwnd*/, UINT /*uMsg*/,
27 UINT_PTR /*idEvent*/, DWORD /*dwTime*/) {
28 if (host)
29 host->UpdateLayout();
30 }
31
InfobarWindow(InfobarType type)32 InfobarWindow::InfobarWindow(InfobarType type)
33 : type_(type),
34 host_(NULL),
35 target_height_(0),
36 initial_height_(0),
37 current_height_(0),
38 current_width_(0),
39 timer_id_(0),
40 timer_stub_(NULL),
41 frame_impl_(this) {
42 DCHECK(type_ >= FIRST_INFOBAR_TYPE);
43 DCHECK(type_ < END_OF_INFOBAR_TYPE);
44 }
45
~InfobarWindow()46 InfobarWindow::~InfobarWindow() {
47 if (StopTimer() && timer_stub_ != NULL)
48 FunctionStub::Destroy(timer_stub_);
49 else if (timer_stub_ != NULL)
50 timer_stub_->set_argument(NULL); // Couldn't stop it, so orphan and disable
51 }
52
SetHost(Host * host)53 void InfobarWindow::SetHost(Host* host) {
54 DCHECK(host_ == NULL);
55 DCHECK(timer_stub_ == NULL);
56 DCHECK(host != NULL);
57 host_ = host;
58 timer_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(host),
59 OnSliderTimer);
60 }
61
Show(InfobarContent * content)62 bool InfobarWindow::Show(InfobarContent* content) {
63 DCHECK(host_ != NULL);
64 if (host_ == NULL)
65 return false;
66
67 scoped_ptr<InfobarContent> new_content(content);
68 content_.reset();
69
70 if (!new_content->InstallInFrame(&frame_impl_))
71 return false;
72
73 // Force a call to ReserveSpace, which will capture the width of the displaced
74 // window.
75 if (current_width_ == 0)
76 host_->UpdateLayout();
77 if (current_width_ == 0)
78 return false; // Might not be any displaced window.. then we can't display.
79
80 content_.swap(new_content);
81 StartSlidingTowards(content_->GetDesiredSize(current_width_, 0));
82
83 return true;
84 }
85
Hide()86 void InfobarWindow::Hide() {
87 DCHECK(host_ != NULL);
88 if (host_ == NULL)
89 return;
90
91 StartSlidingTowards(0);
92 }
93
ReserveSpace(RECT * rect)94 void InfobarWindow::ReserveSpace(RECT* rect) {
95 DCHECK(rect);
96 DCHECK(host_ != NULL);
97 if (rect == NULL || host_ == NULL)
98 return;
99
100 current_width_ = rect->right - rect->left;
101 current_height_ = CalculateHeight();
102
103 RECT infobar_rect = *rect;
104
105 switch (type_) {
106 case TOP_INFOBAR:
107 infobar_rect.bottom = rect->top + current_height_;
108 rect->top = std::min(rect->bottom, infobar_rect.bottom);
109 break;
110 case BOTTOM_INFOBAR:
111 infobar_rect.top = rect->bottom - current_height_;
112 rect->bottom = std::max(rect->top, infobar_rect.top);
113 break;
114 default:
115 NOTREACHED() << "Unknown InfobarType value.";
116 break;
117 }
118
119 if (content_ != NULL)
120 content_->SetDimensions(infobar_rect);
121
122 // Done sliding?
123 if (current_height_ == target_height_) {
124 StopTimer();
125 if (current_height_ == 0)
126 content_.reset();
127 }
128 }
129
StartSlidingTowards(int target_height)130 void InfobarWindow::StartSlidingTowards(int target_height) {
131 initial_height_ = current_height_;
132 target_height_ = target_height;
133
134 if (StartTimer())
135 slide_start_ = base::Time::Now();
136 else
137 slide_start_ = base::Time(); // NULL time means don't slide, resize now
138
139 // Trigger an immediate re-laying out. The timer will handle remaining steps.
140 host_->UpdateLayout();
141 }
142
StartTimer()143 bool InfobarWindow::StartTimer() {
144 if (timer_id_ != 0)
145 return true;
146
147 DCHECK(timer_stub_ != NULL);
148 if (timer_stub_ == NULL)
149 return false;
150
151 timer_id_ = ::SetTimer(NULL,
152 timer_id_,
153 kInfobarSlidingTimerIntervalMs,
154 reinterpret_cast<TIMERPROC>(timer_stub_->code()));
155
156 DPLOG_IF(ERROR, timer_id_ == 0) << "Failure in SetTimer.";
157
158 return timer_id_ != 0;
159 }
160
StopTimer()161 bool InfobarWindow::StopTimer() {
162 if (timer_id_ == 0)
163 return true;
164
165 if (::KillTimer(NULL, timer_id_)) {
166 timer_id_ = 0;
167 return true;
168 }
169
170 DPLOG(ERROR) << "Failure in KillTimer.";
171 return false;
172 }
173
CalculateHeight()174 int InfobarWindow::CalculateHeight() {
175 if (slide_start_.is_null())
176 return target_height_;
177
178 base::TimeDelta elapsed = base::Time::Now() - slide_start_;
179 int elapsed_steps = static_cast<int>(elapsed.InMilliseconds()) /
180 kInfobarSlidingTimerIntervalMs;
181
182 if (initial_height_ < target_height_) {
183 return std::min(initial_height_ + elapsed_steps * kInfobarSlideOpenStep,
184 target_height_);
185 } else if (initial_height_ > target_height_) {
186 return std::max(initial_height_ - elapsed_steps * kInfobarSlideCloseStep,
187 target_height_);
188 } else {
189 return target_height_;
190 }
191 }
192
FrameImpl(InfobarWindow * infobar_window)193 InfobarWindow::FrameImpl::FrameImpl(InfobarWindow* infobar_window)
194 : infobar_window_(infobar_window) {
195 }
196
GetFrameWindow()197 HWND InfobarWindow::FrameImpl::GetFrameWindow() {
198 return infobar_window_->host_->GetContainerWindow();
199 }
200
CloseInfobar()201 void InfobarWindow::FrameImpl::CloseInfobar() {
202 infobar_window_->Hide();
203 }
204