1 // Copyright (c) 2012 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.
5 #include "content/browser/web_contents/web_contents_view_win.h"
8 #include "base/memory/scoped_vector.h"
9 #include "content/browser/renderer_host/render_view_host_factory.h"
10 #include "content/browser/renderer_host/render_view_host_impl.h"
11 #include "content/browser/renderer_host/render_widget_host_view_win.h"
12 #include "content/browser/web_contents/interstitial_page_impl.h"
13 #include "content/browser/web_contents/web_contents_drag_win.h"
14 #include "content/browser/web_contents/web_contents_impl.h"
15 #include "content/browser/web_contents/web_drag_dest_win.h"
16 #include "content/public/browser/web_contents_delegate.h"
17 #include "content/public/browser/web_contents_view_delegate.h"
18 #include "ui/base/win/hidden_window.h"
19 #include "ui/base/win/hwnd_subclass.h"
20 #include "ui/gfx/screen.h"
23 WebContentsViewPort
* CreateWebContentsView(
24 WebContentsImpl
* web_contents
,
25 WebContentsViewDelegate
* delegate
,
26 RenderViewHostDelegateView
** render_view_host_delegate_view
) {
27 WebContentsViewWin
* rv
= new WebContentsViewWin(web_contents
, delegate
);
28 *render_view_host_delegate_view
= rv
;
34 typedef std::map
<HWND
, WebContentsViewWin
*> HwndToWcvMap
;
35 HwndToWcvMap hwnd_to_wcv_map
;
37 void RemoveHwndToWcvMapEntry(WebContentsViewWin
* wcv
) {
38 HwndToWcvMap::iterator it
;
39 for (it
= hwnd_to_wcv_map
.begin(); it
!= hwnd_to_wcv_map
.end();) {
40 if (it
->second
== wcv
)
41 hwnd_to_wcv_map
.erase(it
++);
47 BOOL CALLBACK
EnumChildProc(HWND hwnd
, LPARAM lParam
) {
48 HwndToWcvMap::iterator it
= hwnd_to_wcv_map
.find(hwnd
);
49 if (it
== hwnd_to_wcv_map
.end())
50 return TRUE
; // must return TRUE to continue enumeration.
51 WebContentsViewWin
* wcv
= it
->second
;
52 RenderWidgetHostViewWin
* rwhv
= static_cast<RenderWidgetHostViewWin
*>(
53 wcv
->web_contents()->GetRenderWidgetHostView());
55 rwhv
->UpdateScreenInfo(rwhv
->GetNativeView());
57 return TRUE
; // must return TRUE to continue enumeration.
60 class PositionChangedMessageFilter
: public ui::HWNDMessageFilter
{
62 PositionChangedMessageFilter() {}
65 // Overridden from ui::HWNDMessageFilter:
66 virtual bool FilterMessage(HWND hwnd
,
70 LRESULT
* l_result
) OVERRIDE
{
71 if (message
== WM_WINDOWPOSCHANGED
|| message
== WM_SETTINGCHANGE
)
72 EnumChildWindows(hwnd
, EnumChildProc
, 0);
77 DISALLOW_COPY_AND_ASSIGN(PositionChangedMessageFilter
);
80 void AddFilterToParentHwndSubclass(HWND hwnd
, ui::HWNDMessageFilter
* filter
) {
81 HWND parent
= ::GetAncestor(hwnd
, GA_ROOT
);
83 ui::HWNDSubclass::RemoveFilterFromAllTargets(filter
);
84 ui::HWNDSubclass::AddFilterToTarget(parent
, filter
);
88 } // namespace namespace
90 WebContentsViewWin::WebContentsViewWin(WebContentsImpl
* web_contents
,
91 WebContentsViewDelegate
* delegate
)
92 : web_contents_(web_contents
),
94 hwnd_message_filter_(new PositionChangedMessageFilter
) {
97 WebContentsViewWin::~WebContentsViewWin() {
98 RemoveHwndToWcvMapEntry(this);
100 if (IsWindow(hwnd()))
101 DestroyWindow(hwnd());
104 gfx::NativeView
WebContentsViewWin::GetNativeView() const {
108 gfx::NativeView
WebContentsViewWin::GetContentNativeView() const {
109 RenderWidgetHostView
* rwhv
= web_contents_
->GetRenderWidgetHostView();
110 return rwhv
? rwhv
->GetNativeView() : NULL
;
113 gfx::NativeWindow
WebContentsViewWin::GetTopLevelNativeWindow() const {
114 return ::GetAncestor(GetNativeView(), GA_ROOT
);
117 void WebContentsViewWin::GetContainerBounds(gfx::Rect
*out
) const {
118 // Copied from NativeWidgetWin::GetClientAreaScreenBounds().
120 GetClientRect(hwnd(), &r
);
121 POINT point
= { r
.left
, r
.top
};
122 ClientToScreen(hwnd(), &point
);
123 *out
= gfx::Rect(point
.x
, point
.y
, r
.right
- r
.left
, r
.bottom
- r
.top
);
126 void WebContentsViewWin::OnTabCrashed(base::TerminationStatus status
,
130 void WebContentsViewWin::SizeContents(const gfx::Size
& size
) {
132 GetContainerBounds(&bounds
);
133 if (bounds
.size() != size
) {
134 SetWindowPos(hwnd(), NULL
, 0, 0, size
.width(), size
.height(),
135 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_NOMOVE
);
137 // Our size matches what we want but the renderers size may not match.
138 // Pretend we were resized so that the renderers size is updated too.
139 if (web_contents_
->GetInterstitialPage())
140 web_contents_
->GetInterstitialPage()->SetSize(size
);
141 RenderWidgetHostView
* rwhv
= web_contents_
->GetRenderWidgetHostView();
147 void WebContentsViewWin::CreateView(
148 const gfx::Size
& initial_size
, gfx::NativeView context
) {
149 initial_size_
= initial_size
;
151 set_window_style(WS_VISIBLE
| WS_CHILD
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
);
153 Init(ui::GetHiddenWindow(), gfx::Rect(initial_size_
));
155 // Remove the root view drop target so we can register our own.
156 RevokeDragDrop(GetNativeView());
157 drag_dest_
= new WebDragDest(hwnd(), web_contents_
);
159 WebDragDestDelegate
* delegate
= delegate_
->GetDragDestDelegate();
161 drag_dest_
->set_delegate(delegate
);
165 void WebContentsViewWin::Focus() {
166 if (web_contents_
->GetInterstitialPage()) {
167 web_contents_
->GetInterstitialPage()->Focus();
171 if (delegate_
.get() && delegate_
->Focus())
174 RenderWidgetHostView
* rwhv
= web_contents_
->GetRenderWidgetHostView();
179 void WebContentsViewWin::SetInitialFocus() {
180 if (web_contents_
->FocusLocationBarByDefault())
181 web_contents_
->SetFocusToLocationBar(false);
186 void WebContentsViewWin::StoreFocus() {
188 delegate_
->StoreFocus();
191 void WebContentsViewWin::RestoreFocus() {
193 delegate_
->RestoreFocus();
196 DropData
* WebContentsViewWin::GetDropData() const {
197 return drag_dest_
->current_drop_data();
200 gfx::Rect
WebContentsViewWin::GetViewBounds() const {
202 GetWindowRect(hwnd(), &r
);
206 RenderWidgetHostView
* WebContentsViewWin::CreateViewForWidget(
207 RenderWidgetHost
* render_widget_host
) {
208 if (render_widget_host
->GetView()) {
209 // During testing, the view will already be set up in most cases to the
210 // test view, so we don't want to clobber it with a real one. To verify that
211 // this actually is happening (and somebody isn't accidentally creating the
212 // view twice), we check for the RVH Factory, which will be set when we're
213 // making special ones (which go along with the special views).
214 DCHECK(RenderViewHostFactory::has_factory());
215 return render_widget_host
->GetView();
218 RenderWidgetHostViewWin
* view
= static_cast<RenderWidgetHostViewWin
*>(
219 RenderWidgetHostView::CreateViewForWidget(render_widget_host
));
220 view
->CreateWnd(GetNativeView());
221 view
->ShowWindow(SW_SHOW
);
222 view
->SetSize(initial_size_
);
226 RenderWidgetHostView
* WebContentsViewWin::CreateViewForPopupWidget(
227 RenderWidgetHost
* render_widget_host
) {
228 return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host
);
231 void WebContentsViewWin::SetPageTitle(const string16
& title
) {
232 // It's possible to get this after the hwnd has been destroyed.
234 ::SetWindowText(GetNativeView(), title
.c_str());
237 void WebContentsViewWin::RenderViewCreated(RenderViewHost
* host
) {
240 void WebContentsViewWin::RenderViewSwappedIn(RenderViewHost
* host
) {
243 void WebContentsViewWin::SetOverscrollControllerEnabled(bool enabled
) {
246 void WebContentsViewWin::ShowContextMenu(const ContextMenuParams
& params
) {
248 delegate_
->ShowContextMenu(params
);
249 // WARNING: this may have been deleted.
252 void WebContentsViewWin::ShowPopupMenu(const gfx::Rect
& bounds
,
254 double item_font_size
,
256 const std::vector
<MenuItem
>& items
,
258 bool allow_multiple_selection
) {
259 // External popup menus are only used on Mac and Android.
263 void WebContentsViewWin::StartDragging(const DropData
& drop_data
,
264 WebKit::WebDragOperationsMask operations
,
265 const gfx::ImageSkia
& image
,
266 const gfx::Vector2d
& image_offset
,
267 const DragEventSourceInfo
& event_info
) {
268 drag_handler_
= new WebContentsDragWin(
272 base::Bind(&WebContentsViewWin::EndDragging
, base::Unretained(this)));
273 drag_handler_
->StartDragging(drop_data
, operations
, image
, image_offset
);
276 void WebContentsViewWin::UpdateDragCursor(WebKit::WebDragOperation operation
) {
277 drag_dest_
->set_drag_cursor(operation
);
280 void WebContentsViewWin::GotFocus() {
281 if (web_contents_
->GetDelegate())
282 web_contents_
->GetDelegate()->WebContentsFocused(web_contents_
);
285 void WebContentsViewWin::TakeFocus(bool reverse
) {
286 if (web_contents_
->GetDelegate() &&
287 !web_contents_
->GetDelegate()->TakeFocus(web_contents_
, reverse
) &&
289 delegate_
->TakeFocus(reverse
);
293 void WebContentsViewWin::EndDragging() {
294 drag_handler_
= NULL
;
295 web_contents_
->SystemDragEnded();
298 void WebContentsViewWin::CloseTab() {
299 RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
300 rvh
->GetDelegate()->Close(rvh
);
303 LRESULT
WebContentsViewWin::OnCreate(
304 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
305 hwnd_to_wcv_map
.insert(std::make_pair(hwnd(), this));
306 AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_
.get());
310 LRESULT
WebContentsViewWin::OnDestroy(
311 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
313 RevokeDragDrop(GetNativeView());
317 drag_handler_
->CancelDrag();
318 drag_handler_
= NULL
;
323 LRESULT
WebContentsViewWin::OnWindowPosChanged(
324 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
326 // Our parent might have changed. So we re-install our hwnd message filter.
327 AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_
.get());
329 WINDOWPOS
* window_pos
= reinterpret_cast<WINDOWPOS
*>(lparam
);
330 if (window_pos
->flags
& SWP_HIDEWINDOW
) {
331 web_contents_
->WasHidden();
335 // The WebContents was shown by a means other than the user selecting a
336 // Tab, e.g. the window was minimized then restored.
337 if (window_pos
->flags
& SWP_SHOWWINDOW
)
338 web_contents_
->WasShown();
340 RenderWidgetHostView
* rwhv
= web_contents_
->GetRenderWidgetHostView();
342 RenderWidgetHostViewWin
* view
= static_cast<RenderWidgetHostViewWin
*>(rwhv
);
343 view
->UpdateScreenInfo(view
->GetNativeView());
346 // Unless we were specifically told not to size, cause the renderer to be
347 // sized to the new bounds, which forces a repaint. Not required for the
348 // simple minimize-restore case described above, for example, since the
349 // size hasn't changed.
350 if (window_pos
->flags
& SWP_NOSIZE
)
353 gfx::Size
size(window_pos
->cx
, window_pos
->cy
);
354 if (web_contents_
->GetInterstitialPage())
355 web_contents_
->GetInterstitialPage()->SetSize(size
);
360 delegate_
->SizeChanged(size
);
365 LRESULT
WebContentsViewWin::OnMouseDown(
366 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
367 // Make sure this WebContents is activated when it is clicked on.
368 if (web_contents_
->GetDelegate())
369 web_contents_
->GetDelegate()->ActivateContents(web_contents_
);
373 LRESULT
WebContentsViewWin::OnMouseMove(
374 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
375 // Let our delegate know that the mouse moved (useful for resetting status
377 if (web_contents_
->GetDelegate()) {
378 web_contents_
->GetDelegate()->ContentsMouseEvent(
380 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(),
386 LRESULT
WebContentsViewWin::OnNCCalcSize(
387 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
388 // Hack for ThinkPad mouse wheel driver. We have set the fake scroll bars
389 // to receive scroll messages from ThinkPad touch-pad driver. Suppress
390 // painting of scrollbars by returning 0 size for them.
394 LRESULT
WebContentsViewWin::OnNCHitTest(
395 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
396 return HTTRANSPARENT
;
399 LRESULT
WebContentsViewWin::OnScroll(
400 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
401 int scroll_type
= LOWORD(wparam
);
402 short position
= HIWORD(wparam
);
403 HWND scrollbar
= reinterpret_cast<HWND
>(lparam
);
404 // This window can receive scroll events as a result of the ThinkPad's
405 // touch-pad scroll wheel emulation.
406 // If ctrl is held, zoom the UI. There are three issues with this:
407 // 1) Should the event be eaten or forwarded to content? We eat the event,
408 // which is like Firefox and unlike IE.
409 // 2) Should wheel up zoom in or out? We zoom in (increase font size), which
410 // is like IE and Google maps, but unlike Firefox.
411 // 3) Should the mouse have to be over the content area? We zoom as long as
412 // content has focus, although FF and IE require that the mouse is over
413 // content. This is because all events get forwarded when content has
415 if (GetAsyncKeyState(VK_CONTROL
) & 0x8000) {
417 switch (scroll_type
) {
419 distance
= WHEEL_DELTA
;
422 distance
= -WHEEL_DELTA
;
424 // TODO(joshia): Handle SB_PAGEUP, SB_PAGEDOWN, SB_THUMBPOSITION,
425 // and SB_THUMBTRACK for completeness
430 web_contents_
->GetDelegate()->ContentsZoomChange(distance
> 0);
434 // Reflect scroll message to the view() to give it a chance
435 // to process scrolling.
436 SendMessage(GetContentNativeView(), message
, wparam
, lparam
);
440 LRESULT
WebContentsViewWin::OnSize(
441 UINT message
, WPARAM wparam
, LPARAM lparam
, BOOL
& handled
) {
442 // NOTE: Because we handle OnWindowPosChanged without calling DefWindowProc,
443 // OnSize is NOT called on window resize. This handler is called only once
444 // when the window is created.
445 // Don't call base class OnSize to avoid useless layout for 0x0 size.
446 // We will get OnWindowPosChanged later and layout root view in WasSized.
448 // Hack for ThinkPad touch-pad driver.
449 // Set fake scrollbars so that we can get scroll messages,
451 si
.cbSize
= sizeof(si
);
459 ::SetScrollInfo(hwnd(), SB_HORZ
, &si
, FALSE
);
460 ::SetScrollInfo(hwnd(), SB_VERT
, &si
, FALSE
);
465 } // namespace content