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#include "chrome/browser/ui/views/tab_contents/native_tab_contents_view_win.h" 6 7#include "chrome/browser/renderer_host/render_widget_host_view_win.h" 8#include "chrome/browser/tab_contents/web_drop_target_win.h" 9#include "chrome/browser/ui/views/tab_contents/tab_contents_drag_win.h" 10#include "chrome/browser/ui/views/tab_contents/native_tab_contents_view_delegate.h" 11#include "content/browser/tab_contents/tab_contents.h" 12#include "content/browser/tab_contents/tab_contents_view.h" 13 14namespace { 15 16// Tabs must be created as child widgets, otherwise they will be given 17// a FocusManager which will conflict with the FocusManager of the 18// window they eventually end up attached to. 19// 20// A tab will not have a parent HWND whenever it is not active in its 21// host window - for example at creation time and when it's in the 22// background, so we provide a default widget to host them. 23// 24// It may be tempting to use GetDesktopWindow() instead, but this is 25// problematic as the shell sends messages to children of the desktop 26// window that interact poorly with us. 27// 28// See: http://crbug.com/16476 29HWND GetHiddenTabHostWindow() { 30 static views::Widget* widget = NULL; 31 32 if (!widget) { 33 views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP); 34 widget = views::Widget::CreateWidget(params); 35 widget->Init(NULL, gfx::Rect()); 36 // If a background window requests focus, the hidden tab host will 37 // be activated to focus the tab. Use WS_DISABLED to prevent 38 // this. 39 EnableWindow(widget->GetNativeView(), FALSE); 40 } 41 42 return widget->GetNativeView(); 43} 44 45} // namespace 46 47//////////////////////////////////////////////////////////////////////////////// 48// NativeTabContentsViewWin, public: 49 50NativeTabContentsViewWin::NativeTabContentsViewWin( 51 internal::NativeTabContentsViewDelegate* delegate) 52 : delegate_(delegate), 53 focus_manager_(NULL) { 54} 55 56NativeTabContentsViewWin::~NativeTabContentsViewWin() { 57 CloseNow(); 58} 59 60TabContents* NativeTabContentsViewWin::GetTabContents() const { 61 return delegate_->GetTabContents(); 62} 63 64void NativeTabContentsViewWin::EndDragging() { 65 delegate_->OnNativeTabContentsViewDraggingEnded(); 66 drag_handler_ = NULL; 67} 68 69//////////////////////////////////////////////////////////////////////////////// 70// NativeTabContentsViewWin, NativeTabContentsView implementation: 71 72void NativeTabContentsViewWin::InitNativeTabContentsView() { 73 views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_CONTROL); 74 params.delete_on_destroy = false; 75 SetCreateParams(params); 76 WidgetWin::Init(GetHiddenTabHostWindow(), gfx::Rect()); 77 78 // Remove the root view drop target so we can register our own. 79 RevokeDragDrop(GetNativeView()); 80 drop_target_ = new WebDropTarget(GetNativeView(), 81 delegate_->GetTabContents()); 82} 83 84void NativeTabContentsViewWin::Unparent() { 85 // Remember who our FocusManager is, we won't be able to access it once 86 // unparented. 87 focus_manager_ = views::WidgetWin::GetFocusManager(); 88 // Note that we do not DCHECK on focus_manager_ as it may be NULL when used 89 // with an external tab container. 90 NativeWidget::ReparentNativeView(GetNativeView(), GetHiddenTabHostWindow()); 91} 92 93RenderWidgetHostView* NativeTabContentsViewWin::CreateRenderWidgetHostView( 94 RenderWidgetHost* render_widget_host) { 95 RenderWidgetHostViewWin* view = 96 new RenderWidgetHostViewWin(render_widget_host); 97 view->CreateWnd(GetNativeView()); 98 view->ShowWindow(SW_SHOW); 99 return view; 100} 101 102gfx::NativeWindow NativeTabContentsViewWin::GetTopLevelNativeWindow() const { 103 return ::GetAncestor(GetNativeView(), GA_ROOT); 104} 105 106void NativeTabContentsViewWin::SetPageTitle(const std::wstring& title) { 107 // It's possible to get this after the hwnd has been destroyed. 108 if (GetNativeView()) 109 ::SetWindowText(GetNativeView(), title.c_str()); 110} 111 112void NativeTabContentsViewWin::StartDragging(const WebDropData& drop_data, 113 WebKit::WebDragOperationsMask ops, 114 const SkBitmap& image, 115 const gfx::Point& image_offset) { 116 drag_handler_ = new TabContentsDragWin(this); 117 drag_handler_->StartDragging(drop_data, ops, image, image_offset); 118} 119 120void NativeTabContentsViewWin::CancelDrag() { 121 drag_handler_->CancelDrag(); 122} 123 124bool NativeTabContentsViewWin::IsDoingDrag() const { 125 return drag_handler_.get() != NULL; 126} 127 128void NativeTabContentsViewWin::SetDragCursor( 129 WebKit::WebDragOperation operation) { 130 drop_target_->set_drag_cursor(operation); 131} 132 133views::NativeWidget* NativeTabContentsViewWin::AsNativeWidget() { 134 return this; 135} 136 137//////////////////////////////////////////////////////////////////////////////// 138// NativeTabContentsViewWin, views::WidgetWin overrides: 139 140void NativeTabContentsViewWin::OnDestroy() { 141 if (drop_target_.get()) { 142 RevokeDragDrop(GetNativeView()); 143 drop_target_ = NULL; 144 } 145 146 WidgetWin::OnDestroy(); 147} 148 149void NativeTabContentsViewWin::OnHScroll(int scroll_type, 150 short position, 151 HWND scrollbar) { 152 ScrollCommon(WM_HSCROLL, scroll_type, position, scrollbar); 153} 154 155LRESULT NativeTabContentsViewWin::OnMouseRange(UINT msg, 156 WPARAM w_param, 157 LPARAM l_param) { 158 if (delegate_->IsShowingSadTab()) 159 return WidgetWin::OnMouseRange(msg, w_param, l_param); 160 161 switch (msg) { 162 case WM_LBUTTONDOWN: 163 case WM_MBUTTONDOWN: 164 case WM_RBUTTONDOWN: { 165 delegate_->OnNativeTabContentsViewMouseDown(); 166 break; 167 } 168 case WM_MOUSEMOVE: 169 delegate_->OnNativeTabContentsViewMouseMove(); 170 break; 171 default: 172 break; 173 } 174 return 0; 175} 176 177// A message is reflected here from view(). 178// Return non-zero to indicate that it is handled here. 179// Return 0 to allow view() to further process it. 180LRESULT NativeTabContentsViewWin::OnReflectedMessage(UINT msg, 181 WPARAM w_param, 182 LPARAM l_param) { 183 MSG* message = reinterpret_cast<MSG*>(l_param); 184 switch (message->message) { 185 case WM_MOUSEWHEEL: 186 // This message is reflected from the view() to this window. 187 if (GET_KEYSTATE_WPARAM(message->wParam) & MK_CONTROL) { 188 delegate_->OnNativeTabContentsViewWheelZoom( 189 GET_WHEEL_DELTA_WPARAM(message->wParam)); 190 return 1; 191 } 192 break; 193 case WM_HSCROLL: 194 case WM_VSCROLL: 195 if (ScrollZoom(LOWORD(message->wParam))) 196 return 1; 197 default: 198 break; 199 } 200 201 return 0; 202} 203 204void NativeTabContentsViewWin::OnVScroll(int scroll_type, 205 short position, 206 HWND scrollbar) { 207 ScrollCommon(WM_VSCROLL, scroll_type, position, scrollbar); 208} 209 210void NativeTabContentsViewWin::OnWindowPosChanged(WINDOWPOS* window_pos) { 211 if (window_pos->flags & SWP_HIDEWINDOW) { 212 delegate_->OnNativeTabContentsViewHidden(); 213 } else { 214 // The TabContents was shown by a means other than the user selecting a 215 // Tab, e.g. the window was minimized then restored. 216 if (window_pos->flags & SWP_SHOWWINDOW) 217 delegate_->OnNativeTabContentsViewShown(); 218 219 // Unless we were specifically told not to size, cause the renderer to be 220 // sized to the new bounds, which forces a repaint. Not required for the 221 // simple minimize-restore case described above, for example, since the 222 // size hasn't changed. 223 if (!(window_pos->flags & SWP_NOSIZE)) { 224 delegate_->OnNativeTabContentsViewSized( 225 gfx::Size(window_pos->cx, window_pos->cy)); 226 } 227 } 228 WidgetWin::OnWindowPosChanged(window_pos); 229} 230 231void NativeTabContentsViewWin::OnSize(UINT param, const CSize& size) { 232 // NOTE: Because TabContentsViewViews handles OnWindowPosChanged without 233 // calling DefWindowProc, OnSize is NOT called on window resize. This handler 234 // is called only once when the window is created. 235 236 // Don't call base class OnSize to avoid useless layout for 0x0 size. 237 // We will get OnWindowPosChanged later and layout root view in WasSized. 238 239 // Hack for ThinkPad touch-pad driver. 240 // Set fake scrollbars so that we can get scroll messages, 241 SCROLLINFO si = {0}; 242 si.cbSize = sizeof(si); 243 si.fMask = SIF_ALL; 244 245 si.nMin = 1; 246 si.nMax = 100; 247 si.nPage = 10; 248 si.nPos = 50; 249 250 ::SetScrollInfo(GetNativeView(), SB_HORZ, &si, FALSE); 251 ::SetScrollInfo(GetNativeView(), SB_VERT, &si, FALSE); 252} 253 254LRESULT NativeTabContentsViewWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) { 255 // Hack for ThinkPad mouse wheel driver. We have set the fake scroll bars 256 // to receive scroll messages from ThinkPad touch-pad driver. Suppress 257 // painting of scrollbars by returning 0 size for them. 258 return 0; 259} 260 261void NativeTabContentsViewWin::OnNCPaint(HRGN rgn) { 262 // Suppress default WM_NCPAINT handling. We don't need to do anything 263 // here since the view will draw everything correctly. 264} 265 266views::FocusManager* NativeTabContentsViewWin::GetFocusManager() { 267 views::FocusManager* focus_manager = WidgetWin::GetFocusManager(); 268 if (focus_manager) { 269 // If focus_manager_ is non NULL, it means we have been reparented, in which 270 // case its value may not be valid anymore. 271 focus_manager_ = NULL; 272 return focus_manager; 273 } 274 // TODO(jcampan): we should DCHECK on focus_manager_, as it should not be 275 // NULL. We are not doing it as it breaks some unit-tests. We should 276 // probably have an empty TabContentView implementation for the unit-tests, 277 // that would prevent that code being executed in the unit-test case. 278 // DCHECK(focus_manager_); 279 return focus_manager_; 280} 281 282//////////////////////////////////////////////////////////////////////////////// 283// NativeTabContentsViewWin, private: 284 285void NativeTabContentsViewWin::ScrollCommon(UINT message, int scroll_type, 286 short position, HWND scrollbar) { 287 // This window can receive scroll events as a result of the ThinkPad's 288 // touch-pad scroll wheel emulation. 289 if (!ScrollZoom(scroll_type)) { 290 // Reflect scroll message to the view() to give it a chance 291 // to process scrolling. 292 SendMessage(delegate_->GetTabContents()->view()->GetContentNativeView(), 293 message, MAKELONG(scroll_type, position), 294 reinterpret_cast<LPARAM>(scrollbar)); 295 } 296} 297 298bool NativeTabContentsViewWin::ScrollZoom(int scroll_type) { 299 // If ctrl is held, zoom the UI. There are three issues with this: 300 // 1) Should the event be eaten or forwarded to content? We eat the event, 301 // which is like Firefox and unlike IE. 302 // 2) Should wheel up zoom in or out? We zoom in (increase font size), which 303 // is like IE and Google maps, but unlike Firefox. 304 // 3) Should the mouse have to be over the content area? We zoom as long as 305 // content has focus, although FF and IE require that the mouse is over 306 // content. This is because all events get forwarded when content has 307 // focus. 308 if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { 309 int distance = 0; 310 switch (scroll_type) { 311 case SB_LINEUP: 312 distance = WHEEL_DELTA; 313 break; 314 case SB_LINEDOWN: 315 distance = -WHEEL_DELTA; 316 break; 317 // TODO(joshia): Handle SB_PAGEUP, SB_PAGEDOWN, SB_THUMBPOSITION, 318 // and SB_THUMBTRACK for completeness 319 default: 320 break; 321 } 322 323 delegate_->OnNativeTabContentsViewWheelZoom(distance); 324 return true; 325 } 326 return false; 327} 328 329//////////////////////////////////////////////////////////////////////////////// 330// NativeTabContentsView, public: 331 332// static 333NativeTabContentsView* NativeTabContentsView::CreateNativeTabContentsView( 334 internal::NativeTabContentsViewDelegate* delegate) { 335 return new NativeTabContentsViewWin(delegate); 336} 337