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.
4
5 #include "content/browser/renderer_host/legacy_render_widget_host_win.h"
6
7 #include <objbase.h>
8
9 #include <memory>
10 #include <utility>
11
12 #include "base/command_line.h"
13 #include "base/win/win_util.h"
14 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
15 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
16 #include "content/browser/accessibility/browser_accessibility_win.h"
17 #include "content/browser/accessibility/one_shot_accessibility_tree_search.h"
18 #include "content/browser/renderer_host/direct_manipulation_helper_win.h"
19 #include "content/browser/renderer_host/render_widget_host_impl.h"
20 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
21 #include "content/public/common/content_switches.h"
22 #include "ui/accessibility/accessibility_switches.h"
23 #include "ui/accessibility/platform/ax_fragment_root_win.h"
24 #include "ui/accessibility/platform/ax_system_caret_win.h"
25 #include "ui/aura/window.h"
26 #include "ui/aura/window_tree_host.h"
27 #include "ui/base/ui_base_features.h"
28 #include "ui/base/view_prop.h"
29 #include "ui/base/win/internal_constants.h"
30 #include "ui/base/win/window_event_target.h"
31 #include "ui/display/win/screen_win.h"
32 #include "ui/gfx/geometry/rect.h"
33
34 namespace content {
35
36 // A custom MSAA object id used to determine if a screen reader or some
37 // other client is listening on MSAA events - if so, we enable full web
38 // accessibility support.
39 const int kIdScreenReaderHoneyPot = 1;
40
41 // static
Create(HWND parent)42 LegacyRenderWidgetHostHWND* LegacyRenderWidgetHostHWND::Create(
43 HWND parent) {
44 // content_unittests passes in the desktop window as the parent. We allow
45 // the LegacyRenderWidgetHostHWND instance to be created in this case for
46 // these tests to pass.
47 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
48 switches::kDisableLegacyIntermediateWindow) ||
49 (!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow()))
50 return nullptr;
51
52 LegacyRenderWidgetHostHWND* legacy_window_instance =
53 new LegacyRenderWidgetHostHWND(parent);
54 // If we failed to create the child, or if the switch to disable the legacy
55 // window is passed in, then return NULL.
56 if (!::IsWindow(legacy_window_instance->hwnd())) {
57 delete legacy_window_instance;
58 return NULL;
59 }
60 legacy_window_instance->Init();
61 return legacy_window_instance;
62 }
63
Destroy()64 void LegacyRenderWidgetHostHWND::Destroy() {
65 // Delete DirectManipulationHelper before the window is destroyed.
66 if (direct_manipulation_helper_)
67 direct_manipulation_helper_.reset();
68 host_ = nullptr;
69 if (::IsWindow(hwnd()))
70 ::DestroyWindow(hwnd());
71 }
72
UpdateParent(HWND parent)73 void LegacyRenderWidgetHostHWND::UpdateParent(HWND parent) {
74 if (GetWindowEventTarget(GetParent()))
75 GetWindowEventTarget(GetParent())->HandleParentChanged();
76
77 ::SetParent(hwnd(), parent);
78
79 // Direct Manipulation is enabled on Windows 10+. The CreateInstance function
80 // returns NULL if Direct Manipulation is not available. Recreate
81 // |direct_manipulation_helper_| when parent changed (compositor and window
82 // event target updated).
83 direct_manipulation_helper_ = DirectManipulationHelper::CreateInstance(
84 hwnd(), host_->GetNativeView()->GetHost()->compositor(),
85 GetWindowEventTarget(GetParent()));
86 }
87
GetParent()88 HWND LegacyRenderWidgetHostHWND::GetParent() {
89 return ::GetParent(hwnd());
90 }
91
Show()92 void LegacyRenderWidgetHostHWND::Show() {
93 ::ShowWindow(hwnd(), SW_SHOW);
94 }
95
Hide()96 void LegacyRenderWidgetHostHWND::Hide() {
97 ::ShowWindow(hwnd(), SW_HIDE);
98 }
99
SetBounds(const gfx::Rect & bounds)100 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
101 gfx::Rect bounds_in_pixel = display::win::ScreenWin::DIPToClientRect(hwnd(),
102 bounds);
103 ::SetWindowPos(hwnd(), NULL, bounds_in_pixel.x(), bounds_in_pixel.y(),
104 bounds_in_pixel.width(), bounds_in_pixel.height(),
105 SWP_NOREDRAW);
106 if (direct_manipulation_helper_)
107 direct_manipulation_helper_->SetSizeInPixels(bounds_in_pixel.size());
108 }
109
OnFinalMessage(HWND hwnd)110 void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
111 if (host_) {
112 host_->OnLegacyWindowDestroyed();
113 host_ = NULL;
114 }
115
116 // Re-enable flicks for just a moment
117 base::win::EnableFlicks(hwnd);
118
119 delete this;
120 }
121
LegacyRenderWidgetHostHWND(HWND parent)122 LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(HWND parent)
123 : mouse_tracking_enabled_(false), host_(nullptr) {
124 RECT rect = {0};
125 Base::Create(parent, rect, L"Chrome Legacy Window",
126 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
127 WS_EX_TRANSPARENT);
128 // We create a system caret regardless of accessibility mode since not all
129 // assistive software that makes use of a caret is classified as a screen
130 // reader, e.g. the built-in Windows Magnifier.
131 ax_system_caret_ = std::make_unique<ui::AXSystemCaretWin>(hwnd());
132 }
133
~LegacyRenderWidgetHostHWND()134 LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
135 DCHECK(!::IsWindow(hwnd()));
136 }
137
Init()138 void LegacyRenderWidgetHostHWND::Init() {
139 // Only register a touch window if we are using WM_TOUCH.
140 if (!features::IsUsingWMPointerForTouch())
141 RegisterTouchWindow(hwnd(), TWF_WANTPALM);
142
143 // Ignore failure from this call. Some SKUs of Windows such as Hololens do not
144 // support MSAA, and this call failing should not stop us from initializing
145 // UI Automation support.
146 ::CreateStdAccessibleObject(hwnd(), OBJID_WINDOW,
147 IID_PPV_ARGS(&window_accessible_));
148
149 if (::switches::IsExperimentalAccessibilityPlatformUIAEnabled()) {
150 // The usual way for UI Automation to obtain a fragment root is through
151 // WM_GETOBJECT. However, if there's a relation such as "Controller For"
152 // between element A in one window and element B in another window, UIA
153 // might call element A to discover the relation, receive a pointer to
154 // element B, then ask element B for its fragment root, without having sent
155 // WM_GETOBJECT to element B's window. So we create the fragment root now to
156 // ensure it's ready if asked for.
157 ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(hwnd(), this);
158 }
159
160 ui::AXMode mode =
161 BrowserAccessibilityStateImpl::GetInstance()->GetAccessibilityMode();
162 if (!mode.has_mode(ui::AXMode::kNativeAPIs)) {
163 // Attempt to detect screen readers or other clients who want full
164 // accessibility support, by seeing if they respond to this event.
165 NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
166 CHILDID_SELF);
167 }
168
169 // Disable pen flicks (http://crbug.com/506977)
170 base::win::DisableFlicks(hwnd());
171 }
172
173 // static
GetWindowEventTarget(HWND parent)174 ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
175 HWND parent) {
176 return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
177 parent, ui::WindowEventTarget::kWin32InputEventTarget));
178 }
179
OnEraseBkGnd(UINT message,WPARAM w_param,LPARAM l_param)180 LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
181 WPARAM w_param,
182 LPARAM l_param) {
183 return 1;
184 }
185
OnGetObject(UINT message,WPARAM w_param,LPARAM l_param)186 LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
187 WPARAM w_param,
188 LPARAM l_param) {
189 // Only the lower 32 bits of l_param are valid when checking the object id
190 // because it sometimes gets sign-extended incorrectly (but not always).
191 DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param));
192
193 if (kIdScreenReaderHoneyPot == obj_id) {
194 // When an MSAA client has responded to fake event for this id,
195 // only basic accessibility support is enabled. (Full screen reader support
196 // is detected later when specific, more advanced APIs are accessed.)
197 for (ui::WinAccessibilityAPIUsageObserver& observer :
198 ui::GetWinAccessibilityAPIUsageObserverList()) {
199 observer.OnScreenReaderHoneyPotQueried();
200 }
201 return static_cast<LRESULT>(0L);
202 }
203
204 if (!host_)
205 return static_cast<LRESULT>(0L);
206
207 bool is_uia_request = static_cast<DWORD>(UiaRootObjectId) == obj_id;
208 bool is_msaa_request = static_cast<DWORD>(OBJID_CLIENT) == obj_id;
209
210 if ((is_uia_request &&
211 ::switches::IsExperimentalAccessibilityPlatformUIAEnabled()) ||
212 is_msaa_request) {
213 gfx::NativeViewAccessible root =
214 GetOrCreateWindowRootAccessible(is_uia_request);
215
216 if (is_uia_request) {
217 Microsoft::WRL::ComPtr<IRawElementProviderSimple> root_uia;
218 root->QueryInterface(IID_PPV_ARGS(&root_uia));
219 return UiaReturnRawElementProvider(hwnd(), w_param, l_param,
220 root_uia.Get());
221 } else {
222 if (root == nullptr)
223 return static_cast<LRESULT>(0L);
224
225 Microsoft::WRL::ComPtr<IAccessible> root_msaa(root);
226 return LresultFromObject(IID_IAccessible, w_param, root_msaa.Get());
227 }
228 }
229
230 if (static_cast<DWORD>(OBJID_CARET) == obj_id && host_->HasFocus()) {
231 DCHECK(ax_system_caret_);
232 Microsoft::WRL::ComPtr<IAccessible> ax_system_caret_accessible =
233 ax_system_caret_->GetCaret();
234 return LresultFromObject(IID_IAccessible, w_param,
235 ax_system_caret_accessible.Get());
236 }
237
238 return static_cast<LRESULT>(0L);
239 }
240
241 // We send keyboard/mouse/touch messages to the parent window via SendMessage.
242 // While this works, this has the side effect of converting input messages into
243 // sent messages which changes their priority and could technically result
244 // in these messages starving other messages in the queue. Additionally
245 // keyboard/mouse hooks would not see these messages. The alternative approach
246 // is to set and release capture as needed on the parent to ensure that it
247 // receives all mouse events. However that was shelved due to possible issues
248 // with capture changes.
OnKeyboardRange(UINT message,WPARAM w_param,LPARAM l_param,BOOL & handled)249 LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
250 WPARAM w_param,
251 LPARAM l_param,
252 BOOL& handled) {
253 LRESULT ret = 0;
254 if (GetWindowEventTarget(GetParent())) {
255 bool msg_handled = false;
256 ret = GetWindowEventTarget(GetParent())->HandleKeyboardMessage(
257 message, w_param, l_param, &msg_handled);
258 handled = msg_handled;
259 }
260 return ret;
261 }
262
OnMouseRange(UINT message,WPARAM w_param,LPARAM l_param,BOOL & handled)263 LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
264 WPARAM w_param,
265 LPARAM l_param,
266 BOOL& handled) {
267 if (message == WM_MOUSEMOVE) {
268 if (!mouse_tracking_enabled_) {
269 mouse_tracking_enabled_ = true;
270 TRACKMOUSEEVENT tme;
271 tme.cbSize = sizeof(tme);
272 tme.dwFlags = TME_LEAVE;
273 tme.hwndTrack = hwnd();
274 tme.dwHoverTime = 0;
275 TrackMouseEvent(&tme);
276 }
277 }
278 // The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
279 // in screen coordinates. We should not be converting them to parent
280 // coordinates.
281 if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
282 (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
283 POINT mouse_coords;
284 mouse_coords.x = GET_X_LPARAM(l_param);
285 mouse_coords.y = GET_Y_LPARAM(l_param);
286 ::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
287 l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
288 }
289
290 LRESULT ret = 0;
291
292 if (GetWindowEventTarget(GetParent())) {
293 bool msg_handled = false;
294 ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
295 message, w_param, l_param, &msg_handled);
296 handled = msg_handled;
297 // If the parent did not handle non client mouse messages, we call
298 // DefWindowProc on the message with the parent window handle. This
299 // ensures that WM_SYSCOMMAND is generated for the parent and we are
300 // out of the picture.
301 if (!handled &&
302 (message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) {
303 ret = ::DefWindowProc(GetParent(), message, w_param, l_param);
304 handled = TRUE;
305 }
306 }
307 return ret;
308 }
309
OnMouseLeave(UINT message,WPARAM w_param,LPARAM l_param)310 LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
311 WPARAM w_param,
312 LPARAM l_param) {
313 mouse_tracking_enabled_ = false;
314 LRESULT ret = 0;
315 HWND capture_window = ::GetCapture();
316 if ((capture_window != GetParent()) && GetWindowEventTarget(GetParent())) {
317 // We should send a WM_MOUSELEAVE to the parent window only if the mouse
318 // has moved outside the bounds of the parent.
319 POINT cursor_pos;
320 ::GetCursorPos(&cursor_pos);
321
322 // WindowFromPoint returns the top-most HWND. As hwnd() may not
323 // respond with HTTRANSPARENT to a WM_NCHITTEST message,
324 // it may be returned.
325 HWND window_from_point = ::WindowFromPoint(cursor_pos);
326 if (window_from_point != GetParent() &&
327 (capture_window || window_from_point != hwnd())) {
328 bool msg_handled = false;
329 ret = GetWindowEventTarget(GetParent())->HandleMouseMessage(
330 message, w_param, l_param, &msg_handled);
331 SetMsgHandled(msg_handled);
332 }
333 }
334 return ret;
335 }
336
OnMouseActivate(UINT message,WPARAM w_param,LPARAM l_param)337 LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
338 WPARAM w_param,
339 LPARAM l_param) {
340 // Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
341 // message going all the way to the parent which then messes up state
342 // related to focused views, etc. This is because it treats this as if
343 // it lost activation.
344 // Our dummy window should not interfere with focus and activation in
345 // the parent. Return MA_ACTIVATE here ensures that focus state in the parent
346 // is preserved. The only exception is if the parent was created with the
347 // WS_EX_NOACTIVATE style.
348 if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)
349 return MA_NOACTIVATE;
350 // On Windows, if we select the menu item by touch and if the window at the
351 // location is another window on the same thread, that window gets a
352 // WM_MOUSEACTIVATE message and ends up activating itself, which is not
353 // correct. We workaround this by setting a property on the window at the
354 // current cursor location. We check for this property in our
355 // WM_MOUSEACTIVATE handler and don't activate the window if the property is
356 // set.
357 if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
358 ::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
359 return MA_NOACTIVATE;
360 }
361 return MA_ACTIVATE;
362 }
363
OnPointer(UINT message,WPARAM w_param,LPARAM l_param)364 LRESULT LegacyRenderWidgetHostHWND::OnPointer(UINT message,
365 WPARAM w_param,
366 LPARAM l_param) {
367 LRESULT ret = 0;
368 if (GetWindowEventTarget(GetParent())) {
369 bool msg_handled = false;
370 ret = GetWindowEventTarget(GetParent())
371 ->HandlePointerMessage(message, w_param, l_param, &msg_handled);
372 SetMsgHandled(msg_handled);
373 }
374 return ret;
375 }
376
OnTouch(UINT message,WPARAM w_param,LPARAM l_param)377 LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
378 WPARAM w_param,
379 LPARAM l_param) {
380 LRESULT ret = 0;
381 if (GetWindowEventTarget(GetParent())) {
382 bool msg_handled = false;
383 ret = GetWindowEventTarget(GetParent())->HandleTouchMessage(
384 message, w_param, l_param, &msg_handled);
385 SetMsgHandled(msg_handled);
386 }
387 return ret;
388 }
389
OnInput(UINT message,WPARAM w_param,LPARAM l_param)390 LRESULT LegacyRenderWidgetHostHWND::OnInput(UINT message,
391 WPARAM w_param,
392 LPARAM l_param) {
393 LRESULT ret = 0;
394 if (GetWindowEventTarget(GetParent())) {
395 bool msg_handled = false;
396 ret = GetWindowEventTarget(GetParent())
397 ->HandleInputMessage(message, w_param, l_param, &msg_handled);
398 SetMsgHandled(msg_handled);
399 }
400 return ret;
401 }
402
OnScroll(UINT message,WPARAM w_param,LPARAM l_param)403 LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
404 WPARAM w_param,
405 LPARAM l_param) {
406 LRESULT ret = 0;
407 if (GetWindowEventTarget(GetParent())) {
408 bool msg_handled = false;
409 ret = GetWindowEventTarget(GetParent())->HandleScrollMessage(
410 message, w_param, l_param, &msg_handled);
411 SetMsgHandled(msg_handled);
412 }
413 return ret;
414 }
415
OnNCHitTest(UINT message,WPARAM w_param,LPARAM l_param)416 LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
417 WPARAM w_param,
418 LPARAM l_param) {
419 if (GetWindowEventTarget(GetParent())) {
420 bool msg_handled = false;
421 LRESULT hit_test = GetWindowEventTarget(
422 GetParent())->HandleNcHitTestMessage(message, w_param, l_param,
423 &msg_handled);
424 // If the parent returns HTNOWHERE which can happen for popup windows, etc
425 // we return HTCLIENT.
426 if (hit_test == HTNOWHERE)
427 hit_test = HTCLIENT;
428 return hit_test;
429 }
430 return HTNOWHERE;
431 }
432
OnNCPaint(UINT message,WPARAM w_param,LPARAM l_param)433 LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
434 WPARAM w_param,
435 LPARAM l_param) {
436 return 0;
437 }
438
OnPaint(UINT message,WPARAM w_param,LPARAM l_param)439 LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
440 WPARAM w_param,
441 LPARAM l_param) {
442 PAINTSTRUCT ps = {0};
443 ::BeginPaint(hwnd(), &ps);
444 ::EndPaint(hwnd(), &ps);
445 return 0;
446 }
447
OnSetCursor(UINT message,WPARAM w_param,LPARAM l_param)448 LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
449 WPARAM w_param,
450 LPARAM l_param) {
451 return 0;
452 }
453
OnNCCalcSize(UINT message,WPARAM w_param,LPARAM l_param)454 LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
455 WPARAM w_param,
456 LPARAM l_param) {
457 // Prevent scrollbars, etc from drawing.
458 return 0;
459 }
460
OnSize(UINT message,WPARAM w_param,LPARAM l_param)461 LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
462 WPARAM w_param,
463 LPARAM l_param) {
464 // Certain trackpad drivers on Windows have bugs where in they don't generate
465 // WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
466 // unless there is an entry for Chrome with the class name of the Window.
467 // Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
468 // generate the legacy WM_VSCROLL/WM_HSCROLL messages.
469 // We add these styles to ensure that trackpad/trackpoint scrolling
470 // work.
471 long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
472 ::SetWindowLong(hwnd(), GWL_STYLE,
473 current_style | WS_VSCROLL | WS_HSCROLL);
474 return 0;
475 }
476
OnDestroy(UINT message,WPARAM w_param,LPARAM l_param)477 LRESULT LegacyRenderWidgetHostHWND::OnDestroy(UINT message,
478 WPARAM w_param,
479 LPARAM l_param) {
480 if (::switches::IsExperimentalAccessibilityPlatformUIAEnabled()) {
481 // Signal to UIA that all objects associated with this HWND can be
482 // discarded.
483 UiaReturnRawElementProvider(hwnd(), 0, 0, nullptr);
484 }
485 return 0;
486 }
487
OnPointerHitTest(UINT message,WPARAM w_param,LPARAM l_param)488 LRESULT LegacyRenderWidgetHostHWND::OnPointerHitTest(UINT message,
489 WPARAM w_param,
490 LPARAM l_param) {
491 if (!direct_manipulation_helper_)
492 return 0;
493
494 DebugLogging("Receive DM_POINTERHITTEST.");
495
496 direct_manipulation_helper_->OnPointerHitTest(w_param);
497
498 return 0;
499 }
500
501 gfx::NativeViewAccessible
GetChildOfAXFragmentRoot()502 LegacyRenderWidgetHostHWND::GetChildOfAXFragmentRoot() {
503 return GetOrCreateBrowserAccessibilityRoot();
504 }
505
506 gfx::NativeViewAccessible
GetParentOfAXFragmentRoot()507 LegacyRenderWidgetHostHWND::GetParentOfAXFragmentRoot() {
508 if (host_)
509 return host_->GetParentNativeViewAccessible();
510 return nullptr;
511 }
512
IsAXFragmentRootAControlElement()513 bool LegacyRenderWidgetHostHWND::IsAXFragmentRootAControlElement() {
514 // Treat LegacyRenderWidgetHostHWND as a non-control element so that clients
515 // don't read out "Chrome Legacy Window" for it.
516 return false;
517 }
518
519 gfx::NativeViewAccessible
GetOrCreateWindowRootAccessible(bool is_uia_request)520 LegacyRenderWidgetHostHWND::GetOrCreateWindowRootAccessible(
521 bool is_uia_request) {
522 if (is_uia_request) {
523 DCHECK(::switches::IsExperimentalAccessibilityPlatformUIAEnabled());
524 return ax_fragment_root_->GetNativeViewAccessible();
525 }
526 return GetOrCreateBrowserAccessibilityRoot();
527 }
528
529 gfx::NativeViewAccessible
GetOrCreateBrowserAccessibilityRoot()530 LegacyRenderWidgetHostHWND::GetOrCreateBrowserAccessibilityRoot() {
531 if (!host_)
532 return nullptr;
533
534 RenderWidgetHostImpl* rwhi =
535 RenderWidgetHostImpl::From(host_->GetRenderWidgetHost());
536 if (!rwhi)
537 return nullptr;
538
539 BrowserAccessibilityManagerWin* manager =
540 static_cast<BrowserAccessibilityManagerWin*>(
541 rwhi->GetOrCreateRootBrowserAccessibilityManager());
542 if (!manager || !manager->GetRoot())
543 return nullptr;
544
545 BrowserAccessibility* root_node = manager->GetRoot();
546
547 // A datetime popup will have a second window with its own kRootWebArea.
548 // However, the BrowserAccessibilityManager is shared with the main window,
549 // and the popup window's kRootWebArea will be inserted as a sibling of the
550 // popup button. When this is called on a popup, we must return the popup
551 // window's kRootWebArea instead of the root document's kRootWebArea. This
552 // will ensure that we're not placing duplicate document roots in the
553 // accessibility tree.
554 if (host_->GetWidgetType() == WidgetType::kPopup) {
555 OneShotAccessibilityTreeSearch tree_search(root_node);
556 tree_search.SetStartNode(root_node);
557 tree_search.SetDirection(OneShotAccessibilityTreeSearch::FORWARDS);
558 tree_search.SetImmediateDescendantsOnly(false);
559 tree_search.SetCanWrapToLastElement(false);
560 tree_search.AddPredicate(AccessibilityPopupButtonPredicate);
561
562 size_t matches = tree_search.CountMatches();
563 for (size_t i = 0; i < matches; ++i) {
564 BrowserAccessibility* match = tree_search.GetMatchAtIndex(i);
565 DCHECK(match);
566
567 // The web root should be the next sibling of the popup node, however it
568 // is not created instantly, so sometimes the popup window exists before
569 // the popup's kRootWebArea has been added to the tree. In this case we
570 // will fall back to the main document's root.
571 BrowserAccessibility* popup_web_root = match->PlatformGetNextSibling();
572 if (popup_web_root &&
573 popup_web_root->GetRole() == ax::mojom::Role::kRootWebArea) {
574 return popup_web_root->GetNativeViewAccessible();
575 }
576 }
577 }
578
579 return root_node->GetNativeViewAccessible();
580 }
581
582 } // namespace content
583