1 // Copyright (c) 2014 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/renderer_host/legacy_render_widget_host_win.h"
7 #include "base/command_line.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/win/windows_version.h"
10 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
11 #include "content/browser/accessibility/browser_accessibility_win.h"
12 #include "content/public/common/content_switches.h"
13 #include "ui/base/touch/touch_enabled.h"
14 #include "ui/base/view_prop.h"
15 #include "ui/base/win/internal_constants.h"
16 #include "ui/base/win/window_event_target.h"
17 #include "ui/gfx/geometry/rect.h"
21 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
22 ::DestroyWindow(hwnd());
26 scoped_ptr<LegacyRenderWidgetHostHWND> LegacyRenderWidgetHostHWND::Create(
28 // content_unittests passes in the desktop window as the parent. We allow
29 // the LegacyRenderWidgetHostHWND instance to be created in this case for
30 // these tests to pass.
31 if (CommandLine::ForCurrentProcess()->HasSwitch(
32 switches::kDisableLegacyIntermediateWindow) ||
33 (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
34 return scoped_ptr<LegacyRenderWidgetHostHWND>();
36 scoped_ptr<LegacyRenderWidgetHostHWND> legacy_window_instance;
37 legacy_window_instance.reset(new LegacyRenderWidgetHostHWND(parent));
38 // If we failed to create the child, or if the switch to disable the legacy
39 // window is passed in, then return NULL.
40 if (!::IsWindow(legacy_window_instance->hwnd()))
41 return scoped_ptr<LegacyRenderWidgetHostHWND>();
43 legacy_window_instance->Init();
44 return legacy_window_instance.Pass();
47 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
48 ::SetParent(hwnd(), parent);
49 // If the new parent is the desktop Window, then we disable the child window
50 // to ensure that it does not receive any input events. It should not because
51 // of WS_EX_TRANSPARENT. This is only for safety.
52 if (parent == ::GetDesktopWindow()) {
53 ::EnableWindow(hwnd(), FALSE);
55 ::EnableWindow(hwnd(), TRUE);
59 HWND LegacyRenderWidgetHostHWND::GetParent() {
60 return ::GetParent(hwnd());
63 void LegacyRenderWidgetHostHWND::OnManagerDeleted() {
67 void LegacyRenderWidgetHostHWND::Show() {
68 ::ShowWindow(hwnd(), SW_SHOW);
71 void LegacyRenderWidgetHostHWND::Hide() {
72 ::ShowWindow(hwnd(), SW_HIDE);
75 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
76 ::SetWindowPos(hwnd(), NULL, bounds.x(), bounds.y(), bounds.width(),
80 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
82 manager_->OnAccessibleHwndDeleted();
85 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
87 mouse_tracking_enabled_(false) {
89 Base::Create(parent, rect, L"Chrome Legacy Window",
90 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
94 bool LegacyRenderWidgetHostHWND::Init() {
95 if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
96 ui::AreTouchEventsEnabled())
97 RegisterTouchWindow(hwnd(), TWF_WANTPALM);
99 HRESULT hr = ::CreateStdAccessibleObject(
100 hwnd(), OBJID_WINDOW, IID_IAccessible,
101 reinterpret_cast<void **>(window_accessible_.Receive()));
102 DCHECK(SUCCEEDED(hr));
103 return !!SUCCEEDED(hr);
107 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
109 return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
110 parent, ui::WindowEventTarget::kWin32InputEventTarget));
113 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
119 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
122 if (OBJID_CLIENT != l_param || !manager_)
123 return static_cast<LRESULT>(0L);
125 base::win::ScopedComPtr<IAccessible> root(
126 manager_->GetRoot()->ToBrowserAccessibilityWin());
127 return LresultFromObject(IID_IAccessible, w_param,
128 static_cast<IAccessible*>(root.Detach()));
131 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
132 // While this works, this has the side effect of converting input messages into
133 // sent messages which changes their priority and could technically result
134 // in these messages starving other messages in the queue. Additionally
135 // keyboard/mouse hooks would not see these messages. The alternative approach
136 // is to set and release capture as needed on the parent to ensure that it
137 // receives all mouse events. However that was shelved due to possible issues
138 // with capture changes.
139 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
143 if (GetWindowEventTarget(GetParent())) {
144 return GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
145 message, w_param, l_param);
150 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
154 if (message == WM_MOUSEMOVE) {
155 if (!mouse_tracking_enabled_) {
156 mouse_tracking_enabled_ = true;
158 tme.cbSize = sizeof(tme);
159 tme.dwFlags = TME_LEAVE;
160 tme.hwndTrack = hwnd();
162 TrackMouseEvent(&tme);
165 // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
166 // in screen coordinates. We should not be converting them to parent
168 if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
169 (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
171 mouse_coords.x = GET_X_LPARAM(l_param);
172 mouse_coords.y = GET_Y_LPARAM(l_param);
173 ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
174 l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
176 if (GetWindowEventTarget(GetParent())) {
177 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
178 message, w_param, l_param);
183 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
186 mouse_tracking_enabled_ = false;
187 if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
188 // We should send a WM_MOUSELEAVE to the parent window only if the mouse
189 // has moved outside the bounds of the parent.
191 ::GetCursorPos(&cursor_pos);
192 if (::WindowFromPoint(cursor_pos) != GetParent()) {
193 return GetWindowEventTarget(GetParent())->HandleMouseMessage(
194 message, w_param, l_param);
200 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
203 // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
204 // message going all the way to the parent which then messes up state
205 // related to focused views, etc. This is because it treats this as if
206 // it lost activation.
207 // Our dummy window should not interfere with focus and activation in
208 // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
209 // is preserved. The only exception is if the parent was created with the
210 // WS_EX_NOACTIVATE style.
211 if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
212 return MA_NOACTIVATE;
213 // On Windows, if we select the menu item by touch and if the window at the
214 // location is another window on the same thread, that window gets a
215 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
216 // correct. We workaround this by setting a property on the window at the
217 // current cursor location. We check for this property in our
218 // WM_MOUSEACTIVATE handler and don't activate the window if the property is
220 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
221 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
222 return MA_NOACTIVATE;
227 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
230 if (GetWindowEventTarget(GetParent())) {
231 return GetWindowEventTarget(GetParent())->HandleTouchMessage(
232 message, w_param, l_param);
237 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
240 if (GetWindowEventTarget(GetParent())) {
241 return GetWindowEventTarget(GetParent())->HandleScrollMessage(
242 message, w_param, l_param);
247 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
250 if (GetWindowEventTarget(GetParent())) {
251 LRESULT hit_test = GetWindowEventTarget(
252 GetParent())->HandleNcHitTestMessage(message, w_param, l_param);
253 // If the parent returns HTNOWHERE which can happen for popup windows, etc
254 // we return HTCLIENT.
255 if (hit_test == HTNOWHERE)
262 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
268 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
271 PAINTSTRUCT ps = {0};
272 ::BeginPaint(hwnd(), &ps);
273 ::EndPaint(hwnd(), &ps);
277 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
283 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
286 // Prevent scrollbars, etc from drawing.
290 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
293 // Certain trackpad drivers on Windows have bugs where in they don't generate
294 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
295 // unless there is an entry for Chrome with the class name of the Window.
296 // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
297 // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
298 // We add these styles to ensure that trackpad/trackpoint scrolling
300 long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
301 ::SetWindowLong(hwnd(), GWL_STYLE,
302 current_style | WS_VSCROLL | WS_HSCROLL);
306 } // namespace content