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 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 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 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 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 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 86 void InfobarWindow::Hide() { 87 DCHECK(host_ != NULL); 88 if (host_ == NULL) 89 return; 90 91 StartSlidingTowards(0); 92 } 93 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 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 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 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 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 193 InfobarWindow::FrameImpl::FrameImpl(InfobarWindow* infobar_window) 194 : infobar_window_(infobar_window) { 195 } 196 197 HWND InfobarWindow::FrameImpl::GetFrameWindow() { 198 return infobar_window_->host_->GetContainerWindow(); 199 } 200 201 void InfobarWindow::FrameImpl::CloseInfobar() { 202 infobar_window_->Hide(); 203 } 204