Add ICU message format support
[chromium-blink-merge.git] / content / browser / renderer_host / legacy_render_widget_host_win.cc
blob5126ad2bc2da97d1c829ad9f6913ecaeb5de0424
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/browser/renderer_host/render_widget_host_impl.h"
13 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
14 #include "content/public/browser/browser_accessibility_state.h"
15 #include "content/public/common/content_switches.h"
16 #include "ui/base/touch/touch_enabled.h"
17 #include "ui/base/view_prop.h"
18 #include "ui/base/win/internal_constants.h"
19 #include "ui/base/win/window_event_target.h"
20 #include "ui/gfx/geometry/rect.h"
21 #include "ui/gfx/win/dpi.h"
23 namespace content {
25 // A custom MSAA object id used to determine if a screen reader or some
26 // other client is listening on MSAA events - if so, we enable full web
27 // accessibility support.
28 const int kIdScreenReaderHoneyPot = 1;
30 // static
31 LegacyRenderWidgetHostHWND* LegacyRenderWidgetHostHWND::Create(
32 HWND parent) {
33 // content_unittests passes in the desktop window as the parent. We allow
34 // the LegacyRenderWidgetHostHWND instance to be created in this case for
35 // these tests to pass.
36 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
37 switches::kDisableLegacyIntermediateWindow) ||
38 (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
39 return nullptr;
41 LegacyRenderWidgetHostHWND* legacy_window_instance =
42 new LegacyRenderWidgetHostHWND(parent);
43 // If we failed to create the child, or if the switch to disable the legacy
44 // window is passed in, then return NULL.
45 if (!::IsWindow(legacy_window_instance->hwnd())) {
46 delete legacy_window_instance;
47 return NULL;
49 legacy_window_instance->Init();
50 return legacy_window_instance;
53 void LegacyRenderWidgetHostHWND::Destroy() {
54 if (::IsWindow(hwnd()))
55 ::DestroyWindow(hwnd());
58 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
59 if (GetWindowEventTarget(GetParent()))
60 GetWindowEventTarget(GetParent())->HandleParentChanged();
61 ::SetParent(hwnd(), parent);
62 // If the new parent is the desktop Window, then we disable the child window
63 // to ensure that it does not receive any input events. It should not because
64 // of WS_EX_TRANSPARENT. This is only for safety.
65 if (parent == ::GetDesktopWindow()) {
66 ::EnableWindow(hwnd(), FALSE);
67 } else {
68 ::EnableWindow(hwnd(), TRUE);
72 HWND LegacyRenderWidgetHostHWND::GetParent() {
73 return ::GetParent(hwnd());
76 void LegacyRenderWidgetHostHWND::Show() {
77 ::ShowWindow(hwnd(), SW_SHOW);
80 void LegacyRenderWidgetHostHWND::Hide() {
81 ::ShowWindow(hwnd(), SW_HIDE);
84 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
85 gfx::Rect bounds_in_pixel = gfx::win::DIPToScreenRect(bounds);
86 ::SetWindowPos(hwnd(), NULL, bounds_in_pixel.x(), bounds_in_pixel.y(),
87 bounds_in_pixel.width(), bounds_in_pixel.height(),
88 SWP_NOREDRAW);
91 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
92 if (host_) {
93 host_->OnLegacyWindowDestroyed();
94 host_ = NULL;
96 delete this;
99 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
100 : mouse_tracking_enabled_(false),
101 host_(NULL) {
102 RECT rect = {0};
103 Base::Create(parent, rect, L"Chrome Legacy Window",
104 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
105 WS_EX_TRANSPARENT);
108 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
109 DCHECK(!::IsWindow(hwnd()));
112 bool LegacyRenderWidgetHostHWND::Init() {
113 if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
114 ui::AreTouchEventsEnabled())
115 RegisterTouchWindow(hwnd(), TWF_WANTPALM);
117 HRESULT hr = ::CreateStdAccessibleObject(
118 hwnd(), OBJID_WINDOW, IID_IAccessible,
119 reinterpret_cast<void **>(window_accessible_.Receive()));
120 DCHECK(SUCCEEDED(hr));
122 if (!BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
123 // Attempt to detect screen readers or other clients who want full
124 // accessibility support, by seeing if they respond to this event.
125 NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
126 CHILDID_SELF);
129 return !!SUCCEEDED(hr);
132 // static
133 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
134 HWND parent) {
135 return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
136 parent, ui::WindowEventTarget::kWin32InputEventTarget));
139 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
140 WPARAM w_param,
141 LPARAM l_param) {
142 return 1;
145 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
146 WPARAM w_param,
147 LPARAM l_param) {
148 // Only the lower 32 bits of l_param are valid when checking the object id
149 // because it sometimes gets sign-extended incorrectly (but not always).
150 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param));
152 if (kIdScreenReaderHoneyPot == obj_id) {
153 // When an MSAA client has responded to our fake event on this id,
154 // enable screen reader support.
155 BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
156 return static_cast<LRESULT>(0L);
159 if (OBJID_CLIENT != obj_id || !host_)
160 return static_cast<LRESULT>(0L);
162 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(
163 host_->GetRenderWidgetHost());
164 if (!rwhi)
165 return static_cast<LRESULT>(0L);
167 BrowserAccessibilityManagerWin* manager =
168 static_cast<BrowserAccessibilityManagerWin*>(
169 rwhi->GetRootBrowserAccessibilityManager());
170 if (!manager)
171 return static_cast<LRESULT>(0L);
173 base::win::ScopedComPtr<IAccessible> root(
174 manager->GetRoot()->ToBrowserAccessibilityWin());
175 return LresultFromObject(IID_IAccessible, w_param,
176 static_cast<IAccessible*>(root.Detach()));
179 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
180 // While this works, this has the side effect of converting input messages into
181 // sent messages which changes their priority and could technically result
182 // in these messages starving other messages in the queue. Additionally
183 // keyboard/mouse hooks would not see these messages. The alternative approach
184 // is to set and release capture as needed on the parent to ensure that it
185 // receives all mouse events. However that was shelved due to possible issues
186 // with capture changes.
187 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
188 WPARAM w_param,
189 LPARAM l_param,
190 BOOL& handled) {
191 LRESULT ret = 0;
192 if (GetWindowEventTarget(GetParent())) {
193 bool msg_handled = false;
194 ret = GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
195 message, w_param, l_param, &msg_handled);
196 handled = msg_handled;
198 return ret;
201 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
202 WPARAM w_param,
203 LPARAM l_param,
204 BOOL& handled) {
205 if (message == WM_MOUSEMOVE) {
206 if (!mouse_tracking_enabled_) {
207 mouse_tracking_enabled_ = true;
208 TRACKMOUSEEVENT tme;
209 tme.cbSize = sizeof(tme);
210 tme.dwFlags = TME_LEAVE;
211 tme.hwndTrack = hwnd();
212 tme.dwHoverTime = 0;
213 TrackMouseEvent(&tme);
216 // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
217 // in screen coordinates. We should not be converting them to parent
218 // coordinates.
219 if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
220 (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
221 POINT mouse_coords;
222 mouse_coords.x = GET_X_LPARAM(l_param);
223 mouse_coords.y = GET_Y_LPARAM(l_param);
224 ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
225 l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
228 LRESULT ret = 0;
230 if (GetWindowEventTarget(GetParent())) {
231 bool msg_handled = false;
232 ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
233 message, w_param, l_param, &msg_handled);
234 handled = msg_handled;
235 // If the parent did not handle non client mouse messages, we call
236 // DefWindowProc on the message with the parent window handle. This
237 // ensures that WM_SYSCOMMAND is generated for the parent and we are
238 // out of the picture.
239 if (!handled &&
240 (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) {
241 ret = ::DefWindowProc(GetParent(), message, w_param, l_param);
242 handled = TRUE;
245 return ret;
248 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
249 WPARAM w_param,
250 LPARAM l_param) {
251 mouse_tracking_enabled_ = false;
252 LRESULT ret = 0;
253 if ((::GetCapture() != GetParent()) && GetWindowEventTarget(GetParent())) {
254 // We should send a WM_MOUSELEAVE to the parent window only if the mouse
255 // has moved outside the bounds of the parent.
256 POINT cursor_pos;
257 ::GetCursorPos(&cursor_pos);
258 if (::WindowFromPoint(cursor_pos) != GetParent()) {
259 bool msg_handled = false;
260 ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
261 message, w_param, l_param, &msg_handled);
262 SetMsgHandled(msg_handled);
265 return ret;
268 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
269 WPARAM w_param,
270 LPARAM l_param) {
271 // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
272 // message going all the way to the parent which then messes up state
273 // related to focused views, etc. This is because it treats this as if
274 // it lost activation.
275 // Our dummy window should not interfere with focus and activation in
276 // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
277 // is preserved. The only exception is if the parent was created with the
278 // WS_EX_NOACTIVATE style.
279 if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
280 return MA_NOACTIVATE;
281 // On Windows, if we select the menu item by touch and if the window at the
282 // location is another window on the same thread, that window gets a
283 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
284 // correct. We workaround this by setting a property on the window at the
285 // current cursor location. We check for this property in our
286 // WM_MOUSEACTIVATE handler and don't activate the window if the property is
287 // set.
288 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
289 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
290 return MA_NOACTIVATE;
292 return MA_ACTIVATE;
295 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
296 WPARAM w_param,
297 LPARAM l_param) {
298 LRESULT ret = 0;
299 if (GetWindowEventTarget(GetParent())) {
300 bool msg_handled = false;
301 ret = GetWindowEventTarget(GetParent())->HandleTouchMessage(
302 message, w_param, l_param, &msg_handled);
303 SetMsgHandled(msg_handled);
305 return ret;
308 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
309 WPARAM w_param,
310 LPARAM l_param) {
311 LRESULT ret = 0;
312 if (GetWindowEventTarget(GetParent())) {
313 bool msg_handled = false;
314 ret = GetWindowEventTarget(GetParent())->HandleScrollMessage(
315 message, w_param, l_param, &msg_handled);
316 SetMsgHandled(msg_handled);
318 return ret;
321 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
322 WPARAM w_param,
323 LPARAM l_param) {
324 if (GetWindowEventTarget(GetParent())) {
325 bool msg_handled = false;
326 LRESULT hit_test = GetWindowEventTarget(
327 GetParent())->HandleNcHitTestMessage(message, w_param, l_param,
328 &msg_handled);
329 // If the parent returns HTNOWHERE which can happen for popup windows, etc
330 // we return HTCLIENT.
331 if (hit_test == HTNOWHERE)
332 hit_test = HTCLIENT;
333 return hit_test;
335 return HTNOWHERE;
338 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
339 WPARAM w_param,
340 LPARAM l_param) {
341 return 0;
344 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
345 WPARAM w_param,
346 LPARAM l_param) {
347 PAINTSTRUCT ps = {0};
348 ::BeginPaint(hwnd(), &ps);
349 ::EndPaint(hwnd(), &ps);
350 return 0;
353 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
354 WPARAM w_param,
355 LPARAM l_param) {
356 return 0;
359 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
360 WPARAM w_param,
361 LPARAM l_param) {
362 // Prevent scrollbars, etc from drawing.
363 return 0;
366 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
367 WPARAM w_param,
368 LPARAM l_param) {
369 // Certain trackpad drivers on Windows have bugs where in they don't generate
370 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
371 // unless there is an entry for Chrome with the class name of the Window.
372 // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
373 // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
374 // We add these styles to ensure that trackpad/trackpoint scrolling
375 // work.
376 long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
377 ::SetWindowLong(hwnd(), GWL_STYLE,
378 current_style | WS_VSCROLL | WS_HSCROLL);
379 return 0;
382 } // namespace content