blob: 475629d48e8807547e0c79f6fa00d458bac3416c [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/legacy_render_widget_host_win.h"
#include <objbase.h>
#include <memory>
#include <utility>
#include "base/check.h"
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/win/win_util.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/renderer_host/direct_manipulation_helper_win.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "content/common/features.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/platform/ax_fragment_root_win.h"
#include "ui/accessibility/platform/ax_platform.h"
#include "ui/accessibility/platform/ax_system_caret_win.h"
#include "ui/accessibility/platform/browser_accessibility_manager_win.h"
#include "ui/accessibility/platform/browser_accessibility_win.h"
#include "ui/accessibility/platform/one_shot_accessibility_tree_search.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/view_prop.h"
#include "ui/base/win/internal_constants.h"
#include "ui/base/win/window_event_target.h"
#include "ui/display/win/screen_win.h"
#include "ui/gfx/geometry/rect.h"
namespace content {
// A custom MSAA object id used to determine if a screen reader or some
// other client is listening on MSAA events - if so, we enable full web
// accessibility support.
static constexpr int kIdScreenReaderHoneyPot = 1;
// static
LegacyRenderWidgetHostHWND* LegacyRenderWidgetHostHWND::Create(
HWND parent,
RenderWidgetHostViewAura* host) {
// content_unittests passes in the desktop window as the parent. We allow
// the LegacyRenderWidgetHostHWND instance to be created in this case for
// these tests to pass.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableLegacyIntermediateWindow) ||
(!GetWindowEventTarget(parent) && parent != ::GetDesktopWindow())) {
return nullptr;
}
auto* legacy_window_instance = new LegacyRenderWidgetHostHWND(host);
if (!legacy_window_instance->InitOrDeleteSelf(parent)) {
return nullptr;
}
return legacy_window_instance;
}
void LegacyRenderWidgetHostHWND::Destroy() {
// Delete DirectManipulationHelper before the window is destroyed.
direct_manipulation_helper_.reset();
window_tree_host_prop_.reset();
host_ = nullptr;
if (::IsWindow(hwnd())) {
::DestroyWindow(hwnd());
}
}
// TODO(crbug.com/424432184): Remove this function when the feature is cleaned
// up.
void LegacyRenderWidgetHostHWND::CreateDirectManipulationHelper() {
CHECK(!base::FeatureList::IsEnabled(
features::kUpdateDirectManipulationHelperOnParentChange));
// Direct Manipulation is enabled on Windows 10+. The CreateInstance function
// returns NULL if Direct Manipulation is not available. Recreate
// |direct_manipulation_helper_| when parent changed (compositor and window
// event target updated).
direct_manipulation_helper_ =
DirectManipulationHelper::CreateInstance(hwnd());
if (direct_manipulation_helper_) {
direct_manipulation_helper_->UpdateEventHandler(
host_->GetNativeView()->GetHost()->GetWeakPtr(),
GetWindowEventTarget(GetParent()));
}
}
void LegacyRenderWidgetHostHWND::UpdateParent(HWND new_parent) {
const bool only_update_direct_manipulation_helper =
base::FeatureList::IsEnabled(
features::kUpdateDirectManipulationHelperOnParentChange);
// Performance profiles for resizing show that roughly 1/3 of the
// browser main thread CPU samples are inside of the ::SetParent call, even
// though the parent is never changed during this operation. The CPU samples
// disappear if we ask the OS for the current parent and avoid the SetParent
// call altogether.
const HWND current_parent = GetParent();
if (current_parent != new_parent) {
::SetParent(hwnd(), new_parent);
if (!only_update_direct_manipulation_helper) {
CreateDirectManipulationHelper();
}
// Reset tooltips when parent changed; otherwise tooltips could stay open as
// the former parent wouldn't be forwarded any mouse leave messages.
host_->UpdateTooltip(std::u16string());
} else {
// The first call to UpdateParent may have the parent correctly set on
// account of InitOrDeleteSelf having just created the correctly parented
// Window. We will need to create the DirectManipulationHelper in this case
// if we haven't already done so. After initial creation, the
// DirectManipulationHelper only needs to be re-created if the parent
// subsequently changes.
if (!only_update_direct_manipulation_helper &&
!direct_manipulation_helper_) {
CreateDirectManipulationHelper();
}
}
if (only_update_direct_manipulation_helper) {
// The DirectManipulationHelper was created in InitOrDeleteSelf. It must be
// initialized on the first call to UpdateParent. After that it only needs
// to be updated if the parent changes.
if (direct_manipulation_helper_ &&
(!direct_manipulation_helper_->event_target() ||
current_parent != new_parent)) {
direct_manipulation_helper_->UpdateEventHandler(
host_->GetNativeView()->GetHost()->GetWeakPtr(),
GetWindowEventTarget(new_parent));
}
}
}
HWND LegacyRenderWidgetHostHWND::GetParent() {
return ::GetParent(hwnd());
}
void LegacyRenderWidgetHostHWND::Show() {
::ShowWindow(hwnd(), SW_SHOW);
}
void LegacyRenderWidgetHostHWND::Hide() {
::ShowWindow(hwnd(), SW_HIDE);
}
void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
gfx::Rect bounds_in_pixel =
display::win::GetScreenWin()->DIPToClientRect(hwnd(), bounds);
::SetWindowPos(hwnd(), nullptr, bounds_in_pixel.x(), bounds_in_pixel.y(),
bounds_in_pixel.width(), bounds_in_pixel.height(),
SWP_NOREDRAW);
if (direct_manipulation_helper_) {
direct_manipulation_helper_->SetSizeInPixels(bounds_in_pixel.size());
}
}
void LegacyRenderWidgetHostHWND::OnFinalMessage(HWND hwnd) {
if (host_) {
host_->OnLegacyWindowDestroyed();
host_ = nullptr;
}
// Re-enable flicks for just a moment
base::win::EnableFlicks(hwnd);
delete this;
}
LegacyRenderWidgetHostHWND::LegacyRenderWidgetHostHWND(
RenderWidgetHostViewAura* host)
: host_(host) {}
LegacyRenderWidgetHostHWND::~LegacyRenderWidgetHostHWND() {
DCHECK(!::IsWindow(hwnd()));
}
bool LegacyRenderWidgetHostHWND::InitOrDeleteSelf(HWND parent) {
// Need to use weak_ptr to guard against `this` from being deleted by
// Base::Create(), which used to be called in the constructor and caused
// heap-use-after-free crash (https://crbug.com/1194694).
auto weak_ptr = weak_factory_.GetWeakPtr();
RECT rect = {0};
Base::Create(parent, rect, L"Chrome Legacy Window",
WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
WS_EX_TRANSPARENT);
if (!weak_ptr) {
// Base::Create() runs nested windows message loops that could end up
// deleting `this`. Therefore, upon returning false here, `this` is already
// deleted.
return false;
}
// We create a system caret regardless of accessibility mode since not all
// assistive software that makes use of a caret is classified as a screen
// reader, e.g. the built-in Windows Magnifier.
ax_system_caret_ = std::make_unique<ui::AXSystemCaretWin>(hwnd());
// If we failed to create the child, then return false.
if (!::IsWindow(hwnd())) {
delete this;
return false;
}
// Ignore failure from this call. Some SKUs of Windows such as Hololens do not
// support MSAA, and this call failing should not stop us from initializing
// UI Automation support.
::CreateStdAccessibleObject(hwnd(), OBJID_WINDOW,
IID_PPV_ARGS(&window_accessible_));
if (::ui::AXPlatform::GetInstance().IsUiaProviderEnabled()) {
// The usual way for UI Automation to obtain a fragment root is through
// WM_GETOBJECT. However, if there's a relation such as "Controller For"
// between element A in one window and element B in another window, UIA
// might call element A to discover the relation, receive a pointer to
// element B, then ask element B for its fragment root, without having sent
// WM_GETOBJECT to element B's window. So we create the fragment root now to
// ensure it's ready if asked for.
ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(hwnd(), this);
}
// Continue to send honey pot events until we have kWebContents to
// ensure screen readers have the opportunity to enable.
ui::AXMode mode =
BrowserAccessibilityStateImpl::GetInstance()->GetAccessibilityMode();
if (!mode.has_mode(ui::AXMode::kWebContents)) {
// Attempt to detect screen readers or other clients who want full
// accessibility support, by seeing if they respond to this event.
NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kIdScreenReaderHoneyPot,
CHILDID_SELF);
}
// Disable pen flicks (http://crbug.com/506977)
base::win::DisableFlicks(hwnd());
host_->UpdateTooltip(std::u16string());
// Instruct aura::WindowTreeHost to use the HWND's parent for lookup.
window_tree_host_prop_ = std::make_unique<ui::ViewProp>(
hwnd(), aura::WindowTreeHost::kWindowTreeHostUsesParent,
reinterpret_cast<HANDLE>(true));
if (base::FeatureList::IsEnabled(
features::kUpdateDirectManipulationHelperOnParentChange)) {
// Create the DirectManipulationHelper as soon as hwnd() is set.
// UpdateParent() will assign an event target to it. Note Direct
// Manipulation is enabled on Windows 10+. The CreateInstance function
// returns NULL if Direct Manipulation is not available.
direct_manipulation_helper_ =
DirectManipulationHelper::CreateInstance(hwnd());
}
return true;
}
// static
ui::WindowEventTarget* LegacyRenderWidgetHostHWND::GetWindowEventTarget(
HWND parent) {
return reinterpret_cast<ui::WindowEventTarget*>(ui::ViewProp::GetValue(
parent, ui::WindowEventTarget::kWin32InputEventTarget));
}
LRESULT LegacyRenderWidgetHostHWND::OnEraseBkGnd(UINT message,
WPARAM w_param,
LPARAM l_param) {
return 1;
}
LRESULT LegacyRenderWidgetHostHWND::OnGetObject(UINT message,
WPARAM w_param,
LPARAM l_param) {
// Only the lower 32 bits of l_param are valid when checking the object id
// because it sometimes gets sign-extended incorrectly (but not always).
DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(l_param));
if (kIdScreenReaderHoneyPot == obj_id) {
// When an MSAA client has responded to fake event for this id,
// only basic accessibility support is enabled. (Full screen reader support
// is detected later when specific, more advanced APIs are accessed.)
ui::AXPlatform::GetInstance().OnScreenReaderHoneyPotQueried();
return 0;
}
if (!host_) {
return 0;
}
const bool is_uia_request = static_cast<DWORD>(UiaRootObjectId) == obj_id;
const bool is_uia_active =
is_uia_request && ::ui::AXPlatform::GetInstance().IsUiaProviderEnabled();
const bool is_msaa_request = static_cast<DWORD>(OBJID_CLIENT) == obj_id;
if (is_uia_active && disconnecting_fragment_root_) {
// An application that calls UiaDisconnectProvider should not respond to a
// re-entrant WM_GETOBJECT message by returning a pointer to the provider
// that it is trying to disconnect.
// https://learn.microsoft.com/en-us/windows/win32/api/uiautomationcoreapi/nf-uiautomationcoreapi-uiadisconnectprovider
return 0;
}
if (is_uia_request) {
CHECK_DEREF(CHECK_DEREF(GetContentClient()).browser())
.OnUiaProviderRequested(is_uia_active);
}
if (is_uia_active || is_msaa_request) {
gfx::NativeViewAccessible root =
GetOrCreateWindowRootAccessible(is_uia_request);
if (is_uia_active) {
Microsoft::WRL::ComPtr<IRawElementProviderSimple> root_uia;
root->QueryInterface(IID_PPV_ARGS(&root_uia));
// Return the UIA object via UiaReturnRawElementProvider(). See:
// https://docs.microsoft.com/en-us/windows/win32/winauto/wm-getobject
did_return_uia_object_ = true;
return UiaReturnRawElementProvider(hwnd(), w_param, l_param,
root_uia.Get());
} else {
if (!root) {
return 0;
}
Microsoft::WRL::ComPtr<IAccessible> root_msaa(root);
return LresultFromObject(IID_IAccessible, w_param, root_msaa.Get());
}
}
if (static_cast<DWORD>(OBJID_CARET) == obj_id && host_->HasFocus()) {
DCHECK(ax_system_caret_);
Microsoft::WRL::ComPtr<IAccessible> ax_system_caret_accessible =
ax_system_caret_->GetCaret();
return LresultFromObject(IID_IAccessible, w_param,
ax_system_caret_accessible.Get());
}
return 0;
}
// We send keyboard/mouse/touch messages to the parent window via SendMessage.
// While this works, this has the side effect of converting input messages into
// sent messages which changes their priority and could technically result
// in these messages starving other messages in the queue. Additionally
// keyboard/mouse hooks would not see these messages. The alternative approach
// is to set and release capture as needed on the parent to ensure that it
// receives all mouse events. However that was shelved due to possible issues
// with capture changes.
LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
WPARAM w_param,
LPARAM l_param,
BOOL& handled) {
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return 0;
}
bool msg_handled = false;
LRESULT ret = event_target->HandleKeyboardMessage(message, w_param, l_param,
&msg_handled);
handled = msg_handled;
return ret;
}
LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
WPARAM w_param,
LPARAM l_param,
BOOL& handled) {
if (message == WM_MOUSEMOVE) {
if (!mouse_tracking_enabled_) {
mouse_tracking_enabled_ = true;
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd();
tme.dwHoverTime = 0;
TrackMouseEvent(&tme);
}
}
// The offsets for WM_NCXXX and WM_MOUSEWHEEL and WM_MOUSEHWHEEL messages are
// in screen coordinates. We should not be converting them to parent
// coordinates.
if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) &&
(message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL)) {
POINT mouse_coords;
mouse_coords.x = GET_X_LPARAM(l_param);
mouse_coords.y = GET_Y_LPARAM(l_param);
::MapWindowPoints(hwnd(), GetParent(), &mouse_coords, 1);
l_param = MAKELPARAM(mouse_coords.x, mouse_coords.y);
}
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return 0;
}
bool msg_handled = false;
LRESULT ret =
event_target->HandleMouseMessage(message, w_param, l_param, &msg_handled);
handled = msg_handled;
// If the parent did not handle non-client mouse messages, call
// DefWindowProc() on the message with the parent window handle. This ensures
// that WM_SYSCOMMAND is generated for the parent and this class is out of
// the picture.
if (!handled &&
(message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) {
ret = ::DefWindowProc(GetParent(), message, w_param, l_param);
handled = TRUE;
}
return ret;
}
LRESULT LegacyRenderWidgetHostHWND::OnMouseLeave(UINT message,
WPARAM w_param,
LPARAM l_param) {
mouse_tracking_enabled_ = false;
HWND capture_window = ::GetCapture();
if (capture_window == GetParent()) {
return 0;
}
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return 0;
}
// We should send a WM_MOUSELEAVE to the parent window only if the mouse
// has moved outside the bounds of the parent.
POINT cursor_pos;
::GetCursorPos(&cursor_pos);
// WindowFromPoint returns the top-most HWND. As hwnd() may not respond
// with HTTRANSPARENT to a WM_NCHITTEST message, it may be returned.
HWND window_from_point = ::WindowFromPoint(cursor_pos);
if (window_from_point == GetParent()) {
return 0;
}
if (!capture_window && window_from_point == hwnd()) {
return 0;
}
bool msg_handled = false;
LRESULT ret =
event_target->HandleMouseMessage(message, w_param, l_param, &msg_handled);
SetMsgHandled(msg_handled);
return ret;
}
LRESULT LegacyRenderWidgetHostHWND::OnMouseActivate(UINT message,
WPARAM w_param,
LPARAM l_param) {
// Don't pass this to DefWindowProc. That results in the WM_MOUSEACTIVATE
// message going all the way to the parent which then messes up state
// related to focused views, etc. This is because it treats this as if
// it lost activation.
// Our dummy window should not interfere with focus and activation in
// the parent. Return MA_ACTIVATE here ensures that focus state in the parent
// is preserved. The only exception is if the parent was created with the
// WS_EX_NOACTIVATE style.
if (::GetWindowLong(GetParent(), GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
return MA_NOACTIVATE;
}
// On Windows, if we select the menu item by touch and if the window at the
// location is another window on the same thread, that window gets a
// WM_MOUSEACTIVATE message and ends up activating itself, which is not
// correct. We workaround this by setting a property on the window at the
// current cursor location. We check for this property in our
// WM_MOUSEACTIVATE handler and don't activate the window if the property is
// set.
if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
return MA_NOACTIVATE;
}
return MA_ACTIVATE;
}
LRESULT LegacyRenderWidgetHostHWND::OnPointer(UINT message,
WPARAM w_param,
LPARAM l_param) {
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return 0;
}
bool msg_handled = false;
LRESULT ret = event_target->HandlePointerMessage(message, w_param, l_param,
&msg_handled);
SetMsgHandled(msg_handled);
return ret;
}
LRESULT LegacyRenderWidgetHostHWND::OnTouch(UINT message,
WPARAM w_param,
LPARAM l_param) {
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return 0;
}
bool msg_handled = false;
LRESULT ret =
event_target->HandleTouchMessage(message, w_param, l_param, &msg_handled);
SetMsgHandled(msg_handled);
return ret;
}
LRESULT LegacyRenderWidgetHostHWND::OnInput(UINT message,
WPARAM w_param,
LPARAM l_param) {
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return 0;
}
bool msg_handled = false;
LRESULT ret =
event_target->HandleInputMessage(message, w_param, l_param, &msg_handled);
SetMsgHandled(msg_handled);
return ret;
}
LRESULT LegacyRenderWidgetHostHWND::OnScroll(UINT message,
WPARAM w_param,
LPARAM l_param) {
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return 0;
}
bool msg_handled = false;
LRESULT ret = event_target->HandleScrollMessage(message, w_param, l_param,
&msg_handled);
SetMsgHandled(msg_handled);
return ret;
}
LRESULT LegacyRenderWidgetHostHWND::OnNCHitTest(UINT message,
WPARAM w_param,
LPARAM l_param) {
auto* event_target = GetWindowEventTarget(GetParent());
if (!event_target) {
return HTNOWHERE;
}
bool msg_handled = false;
LRESULT hit_test = event_target->HandleNcHitTestMessage(
message, w_param, l_param, &msg_handled);
if (hit_test == HTNOWHERE) {
// If the parent returns HTNOWHERE which can happen for popup windows, etc,
// return HTCLIENT.
return HTCLIENT;
}
return hit_test;
}
LRESULT LegacyRenderWidgetHostHWND::OnNCPaint(UINT message,
WPARAM w_param,
LPARAM l_param) {
return 0;
}
LRESULT LegacyRenderWidgetHostHWND::OnPaint(UINT message,
WPARAM w_param,
LPARAM l_param) {
PAINTSTRUCT ps = {0};
::BeginPaint(hwnd(), &ps);
::EndPaint(hwnd(), &ps);
return 0;
}
LRESULT LegacyRenderWidgetHostHWND::OnSetCursor(UINT message,
WPARAM w_param,
LPARAM l_param) {
return 0;
}
LRESULT LegacyRenderWidgetHostHWND::OnNCCalcSize(UINT message,
WPARAM w_param,
LPARAM l_param) {
// Prevent scrollbars, etc from drawing.
return 0;
}
LRESULT LegacyRenderWidgetHostHWND::OnSize(UINT message,
WPARAM w_param,
LPARAM l_param) {
// Certain trackpad drivers on Windows have bugs where in they don't generate
// WM_MOUSEWHEEL messages for the trackpoint and trackpad scrolling gestures
// unless there is an entry for Chrome with the class name of the Window.
// Additionally others check if the window WS_VSCROLL/WS_HSCROLL styles and
// generate the legacy WM_VSCROLL/WM_HSCROLL messages.
// We add these styles to ensure that trackpad/trackpoint scrolling
// work.
long current_style = ::GetWindowLong(hwnd(), GWL_STYLE);
::SetWindowLong(hwnd(), GWL_STYLE, current_style | WS_VSCROLL | WS_HSCROLL);
return 0;
}
LRESULT LegacyRenderWidgetHostHWND::OnDestroy(UINT message,
WPARAM w_param,
LPARAM l_param) {
if (ax_fragment_root_ &&
base::FeatureList::IsEnabled(features::kUiaDisconnectRootProviders)) {
// Note that the fragment root's element provider is being disconnected so
// that re-entrant WM_GETOBJECT messages are not serviced.
disconnecting_fragment_root_ = true;
// Clean up UIA resources associated with this window's fragment root; see
// https://learn.microsoft.com/en-us/windows/win32/api/uiautomationcoreapi/nf-uiautomationcoreapi-uiadisconnectprovider.
::UiaDisconnectProvider(ax_fragment_root_->GetProvider());
}
if (did_return_uia_object_) {
// Disassociate this window from MSAA clients that are observing events; see
// https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcoreapi/nf-uiautomationcoreapi-uiareturnrawelementprovider#remarks
::UiaReturnRawElementProvider(hwnd(), 0, 0, nullptr);
}
return 0;
}
LRESULT LegacyRenderWidgetHostHWND::OnPointerHitTest(UINT message,
WPARAM w_param,
LPARAM l_param) {
if (direct_manipulation_helper_) {
direct_manipulation_helper_->OnPointerHitTest(w_param);
}
return 0;
}
gfx::NativeViewAccessible
LegacyRenderWidgetHostHWND::GetChildOfAXFragmentRoot() {
return GetOrCreateBrowserAccessibilityRoot();
}
gfx::NativeViewAccessible
LegacyRenderWidgetHostHWND::GetParentOfAXFragmentRoot() {
return host_ ? host_->GetParentNativeViewAccessible() : nullptr;
}
bool LegacyRenderWidgetHostHWND::IsAXFragmentRootAControlElement() {
// Treat LegacyRenderWidgetHostHWND as a non-control element so that clients
// don't read out "Chrome Legacy Window" for it.
return false;
}
gfx::NativeViewAccessible
LegacyRenderWidgetHostHWND::GetOrCreateWindowRootAccessible(
bool is_uia_request) {
if (is_uia_request) {
DCHECK(::ui::AXPlatform::GetInstance().IsUiaProviderEnabled());
return ax_fragment_root_->GetNativeViewAccessible();
}
return GetOrCreateBrowserAccessibilityRoot();
}
gfx::NativeViewAccessible
LegacyRenderWidgetHostHWND::GetOrCreateBrowserAccessibilityRoot() {
if (!host_) {
return nullptr;
}
RenderWidgetHostImpl* rwhi =
RenderWidgetHostImpl::From(host_->GetRenderWidgetHost());
if (!rwhi) {
return nullptr;
}
auto* manager = static_cast<ui::BrowserAccessibilityManagerWin*>(
rwhi->GetOrCreateRootBrowserAccessibilityManager());
if (!manager || !manager->GetBrowserAccessibilityRoot()) {
return nullptr;
}
ui::BrowserAccessibility* root_node = manager->GetBrowserAccessibilityRoot();
// Popups with HTML content (such as <input type="date">) will create a new
// HWND with its own fragment root, but will also inject accessible nodes into
// the main document's accessibility tree, thus sharing a
// BrowserAccessibilityManager with the main document (see documentation for
// BrowserAccessibilityManager::child_root_id_). We can't return the same root
// node as the main document, as that will cause a cardinality problem - there
// would be two different HWND's pointing to the same root. The popup HWND
// should return the root of the popup, not the root of the main document
if (host_->GetWidgetType() == WidgetType::kPopup) {
// Check to see if the manager has a child root (it's expected that there
// won't be in popups without HTML-based content such as <select> controls).
ui::BrowserAccessibility* child_root = manager->GetPopupRoot();
if (child_root) {
return child_root->GetNativeViewAccessible();
}
}
return root_node->GetNativeViewAccessible();
}
} // namespace content