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 14 namespace { 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 29 HWND 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 50 NativeTabContentsViewWin::NativeTabContentsViewWin( 51 internal::NativeTabContentsViewDelegate* delegate) 52 : delegate_(delegate), 53 focus_manager_(NULL) { 54 } 55 56 NativeTabContentsViewWin::~NativeTabContentsViewWin() { 57 CloseNow(); 58 } 59 60 TabContents* NativeTabContentsViewWin::GetTabContents() const { 61 return delegate_->GetTabContents(); 62 } 63 64 void NativeTabContentsViewWin::EndDragging() { 65 delegate_->OnNativeTabContentsViewDraggingEnded(); 66 drag_handler_ = NULL; 67 } 68 69 //////////////////////////////////////////////////////////////////////////////// 70 // NativeTabContentsViewWin, NativeTabContentsView implementation: 71 72 void 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 84 void 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 93 RenderWidgetHostView* 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 102 gfx::NativeWindow NativeTabContentsViewWin::GetTopLevelNativeWindow() const { 103 return ::GetAncestor(GetNativeView(), GA_ROOT); 104 } 105 106 void 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 112 void 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 120 void NativeTabContentsViewWin::CancelDrag() { 121 drag_handler_->CancelDrag(); 122 } 123 124 bool NativeTabContentsViewWin::IsDoingDrag() const { 125 return drag_handler_.get() != NULL; 126 } 127 128 void NativeTabContentsViewWin::SetDragCursor( 129 WebKit::WebDragOperation operation) { 130 drop_target_->set_drag_cursor(operation); 131 } 132 133 views::NativeWidget* NativeTabContentsViewWin::AsNativeWidget() { 134 return this; 135 } 136 137 //////////////////////////////////////////////////////////////////////////////// 138 // NativeTabContentsViewWin, views::WidgetWin overrides: 139 140 void NativeTabContentsViewWin::OnDestroy() { 141 if (drop_target_.get()) { 142 RevokeDragDrop(GetNativeView()); 143 drop_target_ = NULL; 144 } 145 146 WidgetWin::OnDestroy(); 147 } 148 149 void NativeTabContentsViewWin::OnHScroll(int scroll_type, 150 short position, 151 HWND scrollbar) { 152 ScrollCommon(WM_HSCROLL, scroll_type, position, scrollbar); 153 } 154 155 LRESULT 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. 180 LRESULT 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 204 void NativeTabContentsViewWin::OnVScroll(int scroll_type, 205 short position, 206 HWND scrollbar) { 207 ScrollCommon(WM_VSCROLL, scroll_type, position, scrollbar); 208 } 209 210 void 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 231 void 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 254 LRESULT 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 261 void 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 266 views::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 285 void 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 298 bool 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 333 NativeTabContentsView* NativeTabContentsView::CreateNativeTabContentsView( 334 internal::NativeTabContentsViewDelegate* delegate) { 335 return new NativeTabContentsViewWin(delegate); 336 } 337