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