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