chromium/ui/accessibility/platform/inspect/ax_inspect_utils_win.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "ui/accessibility/platform/inspect/ax_inspect_utils_win.h"

#include <map>
#include <string>

#include "base/containers/heap_array.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/strings/pattern.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_bstr.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "ui/accessibility/platform/inspect/ax_inspect.h"

#include <uiautomation.h>

namespace ui {
namespace {

constexpr wchar_t kChromeTitle[] = L"Google Chrome";
constexpr wchar_t kChromiumTitle[] = L"Chromium";
constexpr wchar_t kEdgeTitle[] = L"Edge";
constexpr wchar_t kFirefoxTitle[] = L"Mozilla Firefox";

struct PlatformConstantToNameEntry {
  int32_t value;
  const char* name;
};

std::wstring GetNameForPlatformConstant(
    const PlatformConstantToNameEntry table[],
    size_t table_size,
    int32_t value) {
  for (size_t i = 0; i < table_size; ++i) {
    auto& entry = table[i];
    if (entry.value == value)
      return base::ASCIIToWide(entry.name);
  }
  return std::wstring();
}

struct HwndWithProcId {
  HwndWithProcId(const base::ProcessId id) : pid(id), hwnd(nullptr) {}
  const base::ProcessId pid;
  HWND hwnd;
};

BOOL CALLBACK EnumWindowsProcPid(HWND hwnd, LPARAM lParam) {
  DWORD process_id;
  GetWindowThreadProcessId(hwnd, &process_id);
  HwndWithProcId* hwnd_with_proc_id = (HwndWithProcId*)lParam;
  if (process_id == static_cast<DWORD>(hwnd_with_proc_id->pid)) {
    hwnd_with_proc_id->hwnd = hwnd;
    return FALSE;
  }
  return TRUE;
}

}  // namespace

#define QUOTE(X) \
  { X, #X }

COMPONENT_EXPORT(AX_PLATFORM)
std::wstring IAccessibleRoleToString(int32_t ia_role) {
  // MSAA / IAccessible roles. Each one of these is also a valid
  // IAccessible2 role.
  static const PlatformConstantToNameEntry ia_table[] = {
      QUOTE(ROLE_SYSTEM_ALERT),          QUOTE(ROLE_SYSTEM_ANIMATION),
      QUOTE(ROLE_SYSTEM_APPLICATION),    QUOTE(ROLE_SYSTEM_BORDER),
      QUOTE(ROLE_SYSTEM_BUTTONDROPDOWN), QUOTE(ROLE_SYSTEM_BUTTONDROPDOWNGRID),
      QUOTE(ROLE_SYSTEM_BUTTONMENU),     QUOTE(ROLE_SYSTEM_CARET),
      QUOTE(ROLE_SYSTEM_CELL),           QUOTE(ROLE_SYSTEM_CHARACTER),
      QUOTE(ROLE_SYSTEM_CHART),          QUOTE(ROLE_SYSTEM_CHECKBUTTON),
      QUOTE(ROLE_SYSTEM_CLIENT),         QUOTE(ROLE_SYSTEM_CLOCK),
      QUOTE(ROLE_SYSTEM_COLUMN),         QUOTE(ROLE_SYSTEM_COLUMNHEADER),
      QUOTE(ROLE_SYSTEM_COMBOBOX),       QUOTE(ROLE_SYSTEM_CURSOR),
      QUOTE(ROLE_SYSTEM_DIAGRAM),        QUOTE(ROLE_SYSTEM_DIAL),
      QUOTE(ROLE_SYSTEM_DIALOG),         QUOTE(ROLE_SYSTEM_DOCUMENT),
      QUOTE(ROLE_SYSTEM_DROPLIST),       QUOTE(ROLE_SYSTEM_EQUATION),
      QUOTE(ROLE_SYSTEM_GRAPHIC),        QUOTE(ROLE_SYSTEM_GRIP),
      QUOTE(ROLE_SYSTEM_GROUPING),       QUOTE(ROLE_SYSTEM_HELPBALLOON),
      QUOTE(ROLE_SYSTEM_HOTKEYFIELD),    QUOTE(ROLE_SYSTEM_INDICATOR),
      QUOTE(ROLE_SYSTEM_IPADDRESS),      QUOTE(ROLE_SYSTEM_LINK),
      QUOTE(ROLE_SYSTEM_LIST),           QUOTE(ROLE_SYSTEM_LISTITEM),
      QUOTE(ROLE_SYSTEM_MENUBAR),        QUOTE(ROLE_SYSTEM_MENUITEM),
      QUOTE(ROLE_SYSTEM_MENUPOPUP),      QUOTE(ROLE_SYSTEM_OUTLINE),
      QUOTE(ROLE_SYSTEM_OUTLINEBUTTON),  QUOTE(ROLE_SYSTEM_OUTLINEITEM),
      QUOTE(ROLE_SYSTEM_PAGETAB),        QUOTE(ROLE_SYSTEM_PAGETABLIST),
      QUOTE(ROLE_SYSTEM_PANE),           QUOTE(ROLE_SYSTEM_PROGRESSBAR),
      QUOTE(ROLE_SYSTEM_PROPERTYPAGE),   QUOTE(ROLE_SYSTEM_PUSHBUTTON),
      QUOTE(ROLE_SYSTEM_RADIOBUTTON),    QUOTE(ROLE_SYSTEM_ROW),
      QUOTE(ROLE_SYSTEM_ROWHEADER),      QUOTE(ROLE_SYSTEM_SCROLLBAR),
      QUOTE(ROLE_SYSTEM_SEPARATOR),      QUOTE(ROLE_SYSTEM_SLIDER),
      QUOTE(ROLE_SYSTEM_SOUND),          QUOTE(ROLE_SYSTEM_SPINBUTTON),
      QUOTE(ROLE_SYSTEM_SPLITBUTTON),    QUOTE(ROLE_SYSTEM_STATICTEXT),
      QUOTE(ROLE_SYSTEM_STATUSBAR),      QUOTE(ROLE_SYSTEM_TABLE),
      QUOTE(ROLE_SYSTEM_TEXT),           QUOTE(ROLE_SYSTEM_TITLEBAR),
      QUOTE(ROLE_SYSTEM_TOOLBAR),        QUOTE(ROLE_SYSTEM_TOOLTIP),
      QUOTE(ROLE_SYSTEM_WHITESPACE),     QUOTE(ROLE_SYSTEM_WINDOW),
  };

  return GetNameForPlatformConstant(ia_table, std::size(ia_table), ia_role);
}

COMPONENT_EXPORT(AX_PLATFORM)
std::wstring IAccessible2RoleToString(int32_t ia2_role) {
  std::wstring result = IAccessibleRoleToString(ia2_role);
  if (!result.empty())
    return result;

  static const PlatformConstantToNameEntry ia2_table[] = {
      QUOTE(IA2_ROLE_CANVAS),
      QUOTE(IA2_ROLE_CAPTION),
      QUOTE(IA2_ROLE_CHECK_MENU_ITEM),
      QUOTE(IA2_ROLE_COLOR_CHOOSER),
      QUOTE(IA2_ROLE_DATE_EDITOR),
      QUOTE(IA2_ROLE_DESKTOP_ICON),
      QUOTE(IA2_ROLE_DESKTOP_PANE),
      QUOTE(IA2_ROLE_DIRECTORY_PANE),
      QUOTE(IA2_ROLE_EDITBAR),
      QUOTE(IA2_ROLE_EMBEDDED_OBJECT),
      QUOTE(IA2_ROLE_ENDNOTE),
      QUOTE(IA2_ROLE_FILE_CHOOSER),
      QUOTE(IA2_ROLE_FONT_CHOOSER),
      QUOTE(IA2_ROLE_FOOTER),
      QUOTE(IA2_ROLE_FOOTNOTE),
      QUOTE(IA2_ROLE_FORM),
      QUOTE(IA2_ROLE_FRAME),
      QUOTE(IA2_ROLE_GLASS_PANE),
      QUOTE(IA2_ROLE_HEADER),
      QUOTE(IA2_ROLE_HEADING),
      QUOTE(IA2_ROLE_ICON),
      QUOTE(IA2_ROLE_IMAGE_MAP),
      QUOTE(IA2_ROLE_INPUT_METHOD_WINDOW),
      QUOTE(IA2_ROLE_INTERNAL_FRAME),
      QUOTE(IA2_ROLE_LABEL),
      QUOTE(IA2_ROLE_LAYERED_PANE),
      QUOTE(IA2_ROLE_NOTE),
      QUOTE(IA2_ROLE_OPTION_PANE),
      QUOTE(IA2_ROLE_PAGE),
      QUOTE(IA2_ROLE_PARAGRAPH),
      QUOTE(IA2_ROLE_RADIO_MENU_ITEM),
      QUOTE(IA2_ROLE_REDUNDANT_OBJECT),
      QUOTE(IA2_ROLE_ROOT_PANE),
      QUOTE(IA2_ROLE_RULER),
      QUOTE(IA2_ROLE_SCROLL_PANE),
      QUOTE(IA2_ROLE_SECTION),
      QUOTE(IA2_ROLE_SHAPE),
      QUOTE(IA2_ROLE_SPLIT_PANE),
      QUOTE(IA2_ROLE_TEAR_OFF_MENU),
      QUOTE(IA2_ROLE_TERMINAL),
      QUOTE(IA2_ROLE_TEXT_FRAME),
      QUOTE(IA2_ROLE_TOGGLE_BUTTON),
      QUOTE(IA2_ROLE_UNKNOWN),
      QUOTE(IA2_ROLE_VIEW_PORT),
      QUOTE(IA2_ROLE_COMPLEMENTARY_CONTENT),
      QUOTE(IA2_ROLE_LANDMARK),
      QUOTE(IA2_ROLE_LEVEL_BAR),
      QUOTE(IA2_ROLE_CONTENT_DELETION),
      QUOTE(IA2_ROLE_CONTENT_INSERTION),
      QUOTE(IA2_ROLE_BLOCK_QUOTE),
      QUOTE(IA2_ROLE_MARK),
      QUOTE(IA2_ROLE_SUGGESTION),
      QUOTE(IA2_ROLE_COMMENT),
  };

  return GetNameForPlatformConstant(ia2_table, std::size(ia2_table), ia2_role);
}

COMPONENT_EXPORT(AX_PLATFORM)
std::wstring AccessibilityEventToString(int32_t event) {
  static const PlatformConstantToNameEntry event_table[] = {
      QUOTE(EVENT_OBJECT_CREATE),
      QUOTE(EVENT_OBJECT_DESTROY),
      QUOTE(EVENT_OBJECT_SHOW),
      QUOTE(EVENT_OBJECT_HIDE),
      QUOTE(EVENT_OBJECT_REORDER),
      QUOTE(EVENT_OBJECT_FOCUS),
      QUOTE(EVENT_OBJECT_SELECTION),
      QUOTE(EVENT_OBJECT_SELECTIONADD),
      QUOTE(EVENT_OBJECT_SELECTIONREMOVE),
      QUOTE(EVENT_OBJECT_SELECTIONWITHIN),
      QUOTE(EVENT_OBJECT_STATECHANGE),
      QUOTE(EVENT_OBJECT_LOCATIONCHANGE),
      QUOTE(EVENT_OBJECT_NAMECHANGE),
      QUOTE(EVENT_OBJECT_DESCRIPTIONCHANGE),
      QUOTE(EVENT_OBJECT_VALUECHANGE),
      QUOTE(EVENT_OBJECT_PARENTCHANGE),
      QUOTE(EVENT_OBJECT_HELPCHANGE),
      QUOTE(EVENT_OBJECT_DEFACTIONCHANGE),
      QUOTE(EVENT_OBJECT_ACCELERATORCHANGE),
      QUOTE(EVENT_OBJECT_INVOKED),
      QUOTE(EVENT_OBJECT_TEXTSELECTIONCHANGED),
      QUOTE(EVENT_OBJECT_CONTENTSCROLLED),
      QUOTE(EVENT_OBJECT_LIVEREGIONCHANGED),
      QUOTE(EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED),
      QUOTE(EVENT_OBJECT_DRAGSTART),
      QUOTE(EVENT_OBJECT_DRAGCANCEL),
      QUOTE(EVENT_OBJECT_DRAGCOMPLETE),
      QUOTE(EVENT_OBJECT_DRAGENTER),
      QUOTE(EVENT_OBJECT_DRAGLEAVE),
      QUOTE(EVENT_OBJECT_DRAGDROPPED),
      QUOTE(EVENT_SYSTEM_ALERT),
      QUOTE(EVENT_SYSTEM_MENUPOPUPSTART),
      QUOTE(EVENT_SYSTEM_MENUPOPUPEND),
      QUOTE(EVENT_SYSTEM_SCROLLINGSTART),
      QUOTE(EVENT_SYSTEM_SCROLLINGEND),
      QUOTE(IA2_EVENT_ACTION_CHANGED),
      QUOTE(IA2_EVENT_ACTIVE_DESCENDANT_CHANGED),
      QUOTE(IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED),
      QUOTE(IA2_EVENT_DOCUMENT_CONTENT_CHANGED),
      QUOTE(IA2_EVENT_DOCUMENT_LOAD_COMPLETE),
      QUOTE(IA2_EVENT_DOCUMENT_LOAD_STOPPED),
      QUOTE(IA2_EVENT_DOCUMENT_RELOAD),
      QUOTE(IA2_EVENT_HYPERLINK_END_INDEX_CHANGED),
      QUOTE(IA2_EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED),
      QUOTE(IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED),
      QUOTE(IA2_EVENT_HYPERTEXT_LINK_ACTIVATED),
      QUOTE(IA2_EVENT_HYPERTEXT_LINK_SELECTED),
      QUOTE(IA2_EVENT_HYPERLINK_START_INDEX_CHANGED),
      QUOTE(IA2_EVENT_HYPERTEXT_CHANGED),
      QUOTE(IA2_EVENT_HYPERTEXT_NLINKS_CHANGED),
      QUOTE(IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED),
      QUOTE(IA2_EVENT_PAGE_CHANGED),
      QUOTE(IA2_EVENT_SECTION_CHANGED),
      QUOTE(IA2_EVENT_TABLE_CAPTION_CHANGED),
      QUOTE(IA2_EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED),
      QUOTE(IA2_EVENT_TABLE_COLUMN_HEADER_CHANGED),
      QUOTE(IA2_EVENT_TABLE_MODEL_CHANGED),
      QUOTE(IA2_EVENT_TABLE_ROW_DESCRIPTION_CHANGED),
      QUOTE(IA2_EVENT_TABLE_ROW_HEADER_CHANGED),
      QUOTE(IA2_EVENT_TABLE_SUMMARY_CHANGED),
      QUOTE(IA2_EVENT_TEXT_ATTRIBUTE_CHANGED),
      QUOTE(IA2_EVENT_TEXT_CARET_MOVED),
      QUOTE(IA2_EVENT_TEXT_CHANGED),
      QUOTE(IA2_EVENT_TEXT_COLUMN_CHANGED),
      QUOTE(IA2_EVENT_TEXT_INSERTED),
      QUOTE(IA2_EVENT_TEXT_REMOVED),
      QUOTE(IA2_EVENT_TEXT_UPDATED),
      QUOTE(IA2_EVENT_TEXT_SELECTION_CHANGED),
      QUOTE(IA2_EVENT_VISIBLE_DATA_CHANGED),
      QUOTE(IA2_EVENT_ROLE_CHANGED),
  };

  return GetNameForPlatformConstant(event_table, std::size(event_table), event);
}

void IAccessibleStateToStringVector(int32_t ia_state,
                                    std::vector<std::wstring>* result) {
#define QUOTE_STATE(X) \
  { STATE_SYSTEM_##X, #X }
  // MSAA / IAccessible states. Unlike roles, these are not also IA2 states.
  //
  // Note: for historical reasons these are in numerical order rather than
  // alphabetical order. Changing the order would change the order in which
  // the states are printed, which would affect a bunch of tests.
  static const PlatformConstantToNameEntry ia_table[] = {
      QUOTE_STATE(UNAVAILABLE),     QUOTE_STATE(SELECTED),
      QUOTE_STATE(FOCUSED),         QUOTE_STATE(PRESSED),
      QUOTE_STATE(CHECKED),         QUOTE_STATE(MIXED),
      QUOTE_STATE(READONLY),        QUOTE_STATE(HOTTRACKED),
      QUOTE_STATE(DEFAULT),         QUOTE_STATE(EXPANDED),
      QUOTE_STATE(COLLAPSED),       QUOTE_STATE(BUSY),
      QUOTE_STATE(FLOATING),        QUOTE_STATE(MARQUEED),
      QUOTE_STATE(ANIMATED),        QUOTE_STATE(INVISIBLE),
      QUOTE_STATE(OFFSCREEN),       QUOTE_STATE(SIZEABLE),
      QUOTE_STATE(MOVEABLE),        QUOTE_STATE(SELFVOICING),
      QUOTE_STATE(FOCUSABLE),       QUOTE_STATE(SELECTABLE),
      QUOTE_STATE(LINKED),          QUOTE_STATE(TRAVERSED),
      QUOTE_STATE(MULTISELECTABLE), QUOTE_STATE(EXTSELECTABLE),
      QUOTE_STATE(ALERT_LOW),       QUOTE_STATE(ALERT_MEDIUM),
      QUOTE_STATE(ALERT_HIGH),      QUOTE_STATE(PROTECTED),
      QUOTE_STATE(HASPOPUP),
  };
  for (auto& entry : ia_table) {
    if (entry.value & ia_state)
      result->push_back(base::ASCIIToWide(entry.name));
  }
}

std::wstring IAccessibleStateToString(int32_t ia_state) {
  std::vector<std::wstring> strings;
  IAccessibleStateToStringVector(ia_state, &strings);
  return base::JoinString(strings, L",");
}

void IAccessible2StateToStringVector(int32_t ia2_state,
                                     std::vector<std::wstring>* result) {
  // Note: for historical reasons these are in numerical order rather than
  // alphabetical order. Changing the order would change the order in which
  // the states are printed, which would affect a bunch of tests.
  static const PlatformConstantToNameEntry ia2_table[] = {
      QUOTE(IA2_STATE_ACTIVE),
      QUOTE(IA2_STATE_ARMED),
      QUOTE(IA2_STATE_CHECKABLE),
      QUOTE(IA2_STATE_DEFUNCT),
      QUOTE(IA2_STATE_EDITABLE),
      QUOTE(IA2_STATE_HORIZONTAL),
      QUOTE(IA2_STATE_ICONIFIED),
      QUOTE(IA2_STATE_INVALID_ENTRY),
      QUOTE(IA2_STATE_MANAGES_DESCENDANTS),
      QUOTE(IA2_STATE_MODAL),
      QUOTE(IA2_STATE_MULTI_LINE),
      QUOTE(IA2_STATE_REQUIRED),
      QUOTE(IA2_STATE_SELECTABLE_TEXT),
      QUOTE(IA2_STATE_SINGLE_LINE),
      QUOTE(IA2_STATE_STALE),
      QUOTE(IA2_STATE_SUPPORTS_AUTOCOMPLETION),
      QUOTE(IA2_STATE_TRANSIENT),
      QUOTE(IA2_STATE_VERTICAL),
      // Untested states include those that would be repeated on nearly
      // every node or would vary based on window size.
      // QUOTE(IA2_STATE_OPAQUE) // Untested.
  };

  for (auto& entry : ia2_table) {
    if (entry.value & ia2_state)
      result->push_back(base::ASCIIToWide(entry.name));
  }
}

std::wstring IAccessible2StateToString(int32_t ia2_state) {
  std::vector<std::wstring> strings;
  IAccessible2StateToStringVector(ia2_state, &strings);
  return base::JoinString(strings, L",");
}

COMPONENT_EXPORT(AX_PLATFORM)
std::wstring UiaIdentifierToString(int32_t identifier) {
  static const PlatformConstantToNameEntry id_table[] = {
      // Patterns
      QUOTE(UIA_InvokePatternId),
      QUOTE(UIA_SelectionPatternId),
      QUOTE(UIA_ValuePatternId),
      QUOTE(UIA_RangeValuePatternId),
      QUOTE(UIA_ScrollPatternId),
      QUOTE(UIA_ExpandCollapsePatternId),
      QUOTE(UIA_GridPatternId),
      QUOTE(UIA_GridItemPatternId),
      QUOTE(UIA_MultipleViewPatternId),
      QUOTE(UIA_WindowPatternId),
      QUOTE(UIA_SelectionItemPatternId),
      QUOTE(UIA_DockPatternId),
      QUOTE(UIA_TablePatternId),
      QUOTE(UIA_TableItemPatternId),
      QUOTE(UIA_TextPatternId),
      QUOTE(UIA_TogglePatternId),
      QUOTE(UIA_TransformPatternId),
      QUOTE(UIA_ScrollItemPatternId),
      QUOTE(UIA_LegacyIAccessiblePatternId),
      QUOTE(UIA_ItemContainerPatternId),
      QUOTE(UIA_VirtualizedItemPatternId),
      QUOTE(UIA_SynchronizedInputPatternId),
      QUOTE(UIA_ObjectModelPatternId),
      QUOTE(UIA_AnnotationPatternId),
      QUOTE(UIA_TextPattern2Id),
      QUOTE(UIA_StylesPatternId),
      QUOTE(UIA_SpreadsheetPatternId),
      QUOTE(UIA_SpreadsheetItemPatternId),
      QUOTE(UIA_TransformPattern2Id),
      QUOTE(UIA_TextChildPatternId),
      QUOTE(UIA_DragPatternId),
      QUOTE(UIA_DropTargetPatternId),
      QUOTE(UIA_TextEditPatternId),
      QUOTE(UIA_CustomNavigationPatternId),
      QUOTE(UIA_SelectionPattern2Id),
      // Events
      QUOTE(UIA_ActiveTextPositionChangedEventId),
      QUOTE(UIA_ToolTipOpenedEventId),
      QUOTE(UIA_ToolTipClosedEventId),
      QUOTE(UIA_StructureChangedEventId),
      QUOTE(UIA_MenuOpenedEventId),
      QUOTE(UIA_AutomationPropertyChangedEventId),
      QUOTE(UIA_AutomationFocusChangedEventId),
      QUOTE(UIA_AsyncContentLoadedEventId),
      QUOTE(UIA_MenuClosedEventId),
      QUOTE(UIA_LayoutInvalidatedEventId),
      QUOTE(UIA_Invoke_InvokedEventId),
      QUOTE(UIA_SelectionItem_ElementAddedToSelectionEventId),
      QUOTE(UIA_SelectionItem_ElementRemovedFromSelectionEventId),
      QUOTE(UIA_SelectionItem_ElementSelectedEventId),
      QUOTE(UIA_Selection_InvalidatedEventId),
      QUOTE(UIA_Text_TextSelectionChangedEventId),
      QUOTE(UIA_Text_TextChangedEventId),
      QUOTE(UIA_Window_WindowOpenedEventId),
      QUOTE(UIA_Window_WindowClosedEventId),
      QUOTE(UIA_MenuModeStartEventId),
      QUOTE(UIA_MenuModeEndEventId),
      QUOTE(UIA_InputReachedTargetEventId),
      QUOTE(UIA_InputReachedOtherElementEventId),
      QUOTE(UIA_InputDiscardedEventId),
      QUOTE(UIA_SystemAlertEventId),
      QUOTE(UIA_LiveRegionChangedEventId),
      QUOTE(UIA_HostedFragmentRootsInvalidatedEventId),
      QUOTE(UIA_Drag_DragStartEventId),
      QUOTE(UIA_Drag_DragCancelEventId),
      QUOTE(UIA_Drag_DragCompleteEventId),
      QUOTE(UIA_DropTarget_DragEnterEventId),
      QUOTE(UIA_DropTarget_DragLeaveEventId),
      QUOTE(UIA_DropTarget_DroppedEventId),
      QUOTE(UIA_TextEdit_TextChangedEventId),
      QUOTE(UIA_TextEdit_ConversionTargetChangedEventId),
      QUOTE(UIA_ChangesEventId),
      QUOTE(UIA_NotificationEventId),
      // Properties
      QUOTE(UIA_RuntimeIdPropertyId),
      QUOTE(UIA_BoundingRectanglePropertyId),
      QUOTE(UIA_ProcessIdPropertyId),
      QUOTE(UIA_ControlTypePropertyId),
      QUOTE(UIA_LocalizedControlTypePropertyId),
      QUOTE(UIA_NamePropertyId),
      QUOTE(UIA_AcceleratorKeyPropertyId),
      QUOTE(UIA_AccessKeyPropertyId),
      QUOTE(UIA_HasKeyboardFocusPropertyId),
      QUOTE(UIA_IsKeyboardFocusablePropertyId),
      QUOTE(UIA_IsEnabledPropertyId),
      QUOTE(UIA_AutomationIdPropertyId),
      QUOTE(UIA_ClassNamePropertyId),
      QUOTE(UIA_HelpTextPropertyId),
      QUOTE(UIA_ClickablePointPropertyId),
      QUOTE(UIA_CulturePropertyId),
      QUOTE(UIA_IsControlElementPropertyId),
      QUOTE(UIA_IsContentElementPropertyId),
      QUOTE(UIA_LabeledByPropertyId),
      QUOTE(UIA_IsPasswordPropertyId),
      QUOTE(UIA_NativeWindowHandlePropertyId),
      QUOTE(UIA_ItemTypePropertyId),
      QUOTE(UIA_IsOffscreenPropertyId),
      QUOTE(UIA_OrientationPropertyId),
      QUOTE(UIA_FrameworkIdPropertyId),
      QUOTE(UIA_IsRequiredForFormPropertyId),
      QUOTE(UIA_ItemStatusPropertyId),
      QUOTE(UIA_IsDockPatternAvailablePropertyId),
      QUOTE(UIA_IsExpandCollapsePatternAvailablePropertyId),
      QUOTE(UIA_IsGridItemPatternAvailablePropertyId),
      QUOTE(UIA_IsGridPatternAvailablePropertyId),
      QUOTE(UIA_IsInvokePatternAvailablePropertyId),
      QUOTE(UIA_IsMultipleViewPatternAvailablePropertyId),
      QUOTE(UIA_IsRangeValuePatternAvailablePropertyId),
      QUOTE(UIA_IsScrollPatternAvailablePropertyId),
      QUOTE(UIA_IsScrollItemPatternAvailablePropertyId),
      QUOTE(UIA_IsSelectionItemPatternAvailablePropertyId),
      QUOTE(UIA_IsSelectionPatternAvailablePropertyId),
      QUOTE(UIA_IsTablePatternAvailablePropertyId),
      QUOTE(UIA_IsTableItemPatternAvailablePropertyId),
      QUOTE(UIA_IsTextPatternAvailablePropertyId),
      QUOTE(UIA_IsTogglePatternAvailablePropertyId),
      QUOTE(UIA_IsTransformPatternAvailablePropertyId),
      QUOTE(UIA_IsValuePatternAvailablePropertyId),
      QUOTE(UIA_IsWindowPatternAvailablePropertyId),
      QUOTE(UIA_ValueValuePropertyId),
      QUOTE(UIA_ValueIsReadOnlyPropertyId),
      QUOTE(UIA_RangeValueValuePropertyId),
      QUOTE(UIA_RangeValueIsReadOnlyPropertyId),
      QUOTE(UIA_RangeValueMinimumPropertyId),
      QUOTE(UIA_RangeValueMaximumPropertyId),
      QUOTE(UIA_RangeValueLargeChangePropertyId),
      QUOTE(UIA_RangeValueSmallChangePropertyId),
      QUOTE(UIA_ScrollHorizontalScrollPercentPropertyId),
      QUOTE(UIA_ScrollHorizontalViewSizePropertyId),
      QUOTE(UIA_ScrollVerticalScrollPercentPropertyId),
      QUOTE(UIA_ScrollVerticalViewSizePropertyId),
      QUOTE(UIA_ScrollHorizontallyScrollablePropertyId),
      QUOTE(UIA_ScrollVerticallyScrollablePropertyId),
      QUOTE(UIA_SelectionSelectionPropertyId),
      QUOTE(UIA_SelectionCanSelectMultiplePropertyId),
      QUOTE(UIA_SelectionIsSelectionRequiredPropertyId),
      QUOTE(UIA_GridRowCountPropertyId),
      QUOTE(UIA_GridColumnCountPropertyId),
      QUOTE(UIA_GridItemRowPropertyId),
      QUOTE(UIA_GridItemColumnPropertyId),
      QUOTE(UIA_GridItemRowSpanPropertyId),
      QUOTE(UIA_GridItemColumnSpanPropertyId),
      QUOTE(UIA_GridItemContainingGridPropertyId),
      QUOTE(UIA_DockDockPositionPropertyId),
      QUOTE(UIA_ExpandCollapseExpandCollapseStatePropertyId),
      QUOTE(UIA_MultipleViewCurrentViewPropertyId),
      QUOTE(UIA_MultipleViewSupportedViewsPropertyId),
      QUOTE(UIA_WindowCanMaximizePropertyId),
      QUOTE(UIA_WindowCanMinimizePropertyId),
      QUOTE(UIA_WindowWindowVisualStatePropertyId),
      QUOTE(UIA_WindowWindowInteractionStatePropertyId),
      QUOTE(UIA_WindowIsModalPropertyId),
      QUOTE(UIA_WindowIsTopmostPropertyId),
      QUOTE(UIA_SelectionItemIsSelectedPropertyId),
      QUOTE(UIA_SelectionItemSelectionContainerPropertyId),
      QUOTE(UIA_TableRowHeadersPropertyId),
      QUOTE(UIA_TableColumnHeadersPropertyId),
      QUOTE(UIA_TableRowOrColumnMajorPropertyId),
      QUOTE(UIA_TableItemRowHeaderItemsPropertyId),
      QUOTE(UIA_TableItemColumnHeaderItemsPropertyId),
      QUOTE(UIA_ToggleToggleStatePropertyId),
      QUOTE(UIA_TransformCanMovePropertyId),
      QUOTE(UIA_TransformCanResizePropertyId),
      QUOTE(UIA_TransformCanRotatePropertyId),
      QUOTE(UIA_IsLegacyIAccessiblePatternAvailablePropertyId),
      QUOTE(UIA_LegacyIAccessibleChildIdPropertyId),
      QUOTE(UIA_LegacyIAccessibleNamePropertyId),
      QUOTE(UIA_LegacyIAccessibleValuePropertyId),
      QUOTE(UIA_LegacyIAccessibleDescriptionPropertyId),
      QUOTE(UIA_LegacyIAccessibleRolePropertyId),
      QUOTE(UIA_LegacyIAccessibleStatePropertyId),
      QUOTE(UIA_LegacyIAccessibleHelpPropertyId),
      QUOTE(UIA_LegacyIAccessibleKeyboardShortcutPropertyId),
      QUOTE(UIA_LegacyIAccessibleSelectionPropertyId),
      QUOTE(UIA_LegacyIAccessibleDefaultActionPropertyId),
      QUOTE(UIA_AriaRolePropertyId),
      QUOTE(UIA_AriaPropertiesPropertyId),
      QUOTE(UIA_IsDataValidForFormPropertyId),
      QUOTE(UIA_ControllerForPropertyId),
      QUOTE(UIA_DescribedByPropertyId),
      QUOTE(UIA_FlowsToPropertyId),
      QUOTE(UIA_ProviderDescriptionPropertyId),
      QUOTE(UIA_IsItemContainerPatternAvailablePropertyId),
      QUOTE(UIA_IsVirtualizedItemPatternAvailablePropertyId),
      QUOTE(UIA_IsSynchronizedInputPatternAvailablePropertyId),
      QUOTE(UIA_OptimizeForVisualContentPropertyId),
      QUOTE(UIA_IsObjectModelPatternAvailablePropertyId),
      QUOTE(UIA_AnnotationAnnotationTypeIdPropertyId),
      QUOTE(UIA_AnnotationAnnotationTypeNamePropertyId),
      QUOTE(UIA_AnnotationAuthorPropertyId),
      QUOTE(UIA_AnnotationDateTimePropertyId),
      QUOTE(UIA_AnnotationTargetPropertyId),
      QUOTE(UIA_IsAnnotationPatternAvailablePropertyId),
      QUOTE(UIA_IsTextPattern2AvailablePropertyId),
      QUOTE(UIA_StylesStyleIdPropertyId),
      QUOTE(UIA_StylesStyleNamePropertyId),
      QUOTE(UIA_StylesFillColorPropertyId),
      QUOTE(UIA_StylesFillPatternStylePropertyId),
      QUOTE(UIA_StylesShapePropertyId),
      QUOTE(UIA_StylesFillPatternColorPropertyId),
      QUOTE(UIA_StylesExtendedPropertiesPropertyId),
      QUOTE(UIA_IsStylesPatternAvailablePropertyId),
      QUOTE(UIA_IsSpreadsheetPatternAvailablePropertyId),
      QUOTE(UIA_SpreadsheetItemFormulaPropertyId),
      QUOTE(UIA_SpreadsheetItemAnnotationObjectsPropertyId),
      QUOTE(UIA_SpreadsheetItemAnnotationTypesPropertyId),
      QUOTE(UIA_IsSpreadsheetItemPatternAvailablePropertyId),
      QUOTE(UIA_Transform2CanZoomPropertyId),
      QUOTE(UIA_IsTransformPattern2AvailablePropertyId),
      QUOTE(UIA_LiveSettingPropertyId),
      QUOTE(UIA_IsTextChildPatternAvailablePropertyId),
      QUOTE(UIA_IsDragPatternAvailablePropertyId),
      QUOTE(UIA_DragIsGrabbedPropertyId),
      QUOTE(UIA_DragDropEffectPropertyId),
      QUOTE(UIA_DragDropEffectsPropertyId),
      QUOTE(UIA_IsDropTargetPatternAvailablePropertyId),
      QUOTE(UIA_DropTargetDropTargetEffectPropertyId),
      QUOTE(UIA_DropTargetDropTargetEffectsPropertyId),
      QUOTE(UIA_DragGrabbedItemsPropertyId),
      QUOTE(UIA_Transform2ZoomLevelPropertyId),
      QUOTE(UIA_Transform2ZoomMinimumPropertyId),
      QUOTE(UIA_Transform2ZoomMaximumPropertyId),
      QUOTE(UIA_FlowsFromPropertyId),
      QUOTE(UIA_IsTextEditPatternAvailablePropertyId),
      QUOTE(UIA_IsPeripheralPropertyId),
      QUOTE(UIA_IsCustomNavigationPatternAvailablePropertyId),
      QUOTE(UIA_PositionInSetPropertyId),
      QUOTE(UIA_SizeOfSetPropertyId),
      QUOTE(UIA_LevelPropertyId),
      QUOTE(UIA_AnnotationTypesPropertyId),
      QUOTE(UIA_AnnotationObjectsPropertyId),
      QUOTE(UIA_LandmarkTypePropertyId),
      QUOTE(UIA_LocalizedLandmarkTypePropertyId),
      QUOTE(UIA_FullDescriptionPropertyId),
      QUOTE(UIA_FillColorPropertyId),
      QUOTE(UIA_OutlineColorPropertyId),
      QUOTE(UIA_FillTypePropertyId),
      QUOTE(UIA_VisualEffectsPropertyId),
      QUOTE(UIA_OutlineThicknessPropertyId),
      QUOTE(UIA_CenterPointPropertyId),
      QUOTE(UIA_RotationPropertyId),
      QUOTE(UIA_SizePropertyId),
      QUOTE(UIA_IsSelectionPattern2AvailablePropertyId),
      QUOTE(UIA_Selection2FirstSelectedItemPropertyId),
      QUOTE(UIA_Selection2LastSelectedItemPropertyId),
      QUOTE(UIA_Selection2CurrentSelectedItemPropertyId),
      QUOTE(UIA_Selection2ItemCountPropertyId),
      QUOTE(UIA_HeadingLevelPropertyId),
      // Attributes
      QUOTE(UIA_AnimationStyleAttributeId),
      QUOTE(UIA_BackgroundColorAttributeId),
      QUOTE(UIA_BulletStyleAttributeId),
      QUOTE(UIA_CapStyleAttributeId),
      QUOTE(UIA_CultureAttributeId),
      QUOTE(UIA_FontNameAttributeId),
      QUOTE(UIA_FontSizeAttributeId),
      QUOTE(UIA_FontWeightAttributeId),
      QUOTE(UIA_ForegroundColorAttributeId),
      QUOTE(UIA_HorizontalTextAlignmentAttributeId),
      QUOTE(UIA_IndentationFirstLineAttributeId),
      QUOTE(UIA_IndentationLeadingAttributeId),
      QUOTE(UIA_IndentationTrailingAttributeId),
      QUOTE(UIA_IsHiddenAttributeId),
      QUOTE(UIA_IsItalicAttributeId),
      QUOTE(UIA_IsReadOnlyAttributeId),
      QUOTE(UIA_IsSubscriptAttributeId),
      QUOTE(UIA_IsSuperscriptAttributeId),
      QUOTE(UIA_MarginBottomAttributeId),
      QUOTE(UIA_MarginLeadingAttributeId),
      QUOTE(UIA_MarginTopAttributeId),
      QUOTE(UIA_MarginTrailingAttributeId),
      QUOTE(UIA_OutlineStylesAttributeId),
      QUOTE(UIA_OverlineColorAttributeId),
      QUOTE(UIA_OverlineStyleAttributeId),
      QUOTE(UIA_StrikethroughColorAttributeId),
      QUOTE(UIA_StrikethroughStyleAttributeId),
      QUOTE(UIA_TabsAttributeId),
      QUOTE(UIA_TextFlowDirectionsAttributeId),
      QUOTE(UIA_UnderlineColorAttributeId),
      QUOTE(UIA_UnderlineStyleAttributeId),
      QUOTE(UIA_AnnotationTypesAttributeId),
      QUOTE(UIA_AnnotationObjectsAttributeId),
      QUOTE(UIA_StyleNameAttributeId),
      QUOTE(UIA_StyleIdAttributeId),
      QUOTE(UIA_LinkAttributeId),
      QUOTE(UIA_IsActiveAttributeId),
      QUOTE(UIA_SelectionActiveEndAttributeId),
      QUOTE(UIA_CaretPositionAttributeId),
      QUOTE(UIA_CaretBidiModeAttributeId),
      QUOTE(UIA_LineSpacingAttributeId),
      QUOTE(UIA_BeforeParagraphSpacingAttributeId),
      QUOTE(UIA_AfterParagraphSpacingAttributeId),
      QUOTE(UIA_SayAsInterpretAsAttributeId),
      // Control Types
      QUOTE(UIA_ButtonControlTypeId),
      QUOTE(UIA_CalendarControlTypeId),
      QUOTE(UIA_CheckBoxControlTypeId),
      QUOTE(UIA_ComboBoxControlTypeId),
      QUOTE(UIA_EditControlTypeId),
      QUOTE(UIA_HyperlinkControlTypeId),
      QUOTE(UIA_ImageControlTypeId),
      QUOTE(UIA_ListItemControlTypeId),
      QUOTE(UIA_ListControlTypeId),
      QUOTE(UIA_MenuControlTypeId),
      QUOTE(UIA_MenuBarControlTypeId),
      QUOTE(UIA_MenuItemControlTypeId),
      QUOTE(UIA_ProgressBarControlTypeId),
      QUOTE(UIA_RadioButtonControlTypeId),
      QUOTE(UIA_ScrollBarControlTypeId),
      QUOTE(UIA_SliderControlTypeId),
      QUOTE(UIA_SpinnerControlTypeId),
      QUOTE(UIA_StatusBarControlTypeId),
      QUOTE(UIA_TabControlTypeId),
      QUOTE(UIA_TabItemControlTypeId),
      QUOTE(UIA_TextControlTypeId),
      QUOTE(UIA_ToolBarControlTypeId),
      QUOTE(UIA_ToolTipControlTypeId),
      QUOTE(UIA_TreeControlTypeId),
      QUOTE(UIA_TreeItemControlTypeId),
      QUOTE(UIA_CustomControlTypeId),
      QUOTE(UIA_GroupControlTypeId),
      QUOTE(UIA_ThumbControlTypeId),
      QUOTE(UIA_DataGridControlTypeId),
      QUOTE(UIA_DataItemControlTypeId),
      QUOTE(UIA_DocumentControlTypeId),
      QUOTE(UIA_SplitButtonControlTypeId),
      QUOTE(UIA_WindowControlTypeId),
      QUOTE(UIA_PaneControlTypeId),
      QUOTE(UIA_HeaderControlTypeId),
      QUOTE(UIA_HeaderItemControlTypeId),
      QUOTE(UIA_TableControlTypeId),
      QUOTE(UIA_TitleBarControlTypeId),
      QUOTE(UIA_SeparatorControlTypeId),
      QUOTE(UIA_SemanticZoomControlTypeId),
      QUOTE(UIA_AppBarControlTypeId),
  };

  return GetNameForPlatformConstant(id_table, std::size(id_table), identifier);
}

COMPONENT_EXPORT(AX_PLATFORM)
std::wstring UiaOrientationToString(int32_t identifier) {
  static const PlatformConstantToNameEntry id_table[] = {
      QUOTE(OrientationType_None), QUOTE(OrientationType_Horizontal),
      QUOTE(OrientationType_Vertical)};
  return GetNameForPlatformConstant(id_table, std::size(id_table), identifier);
}

COMPONENT_EXPORT(AX_PLATFORM)
std::wstring UiaLiveSettingToString(int32_t identifier) {
  static const PlatformConstantToNameEntry id_table[] = {
      QUOTE(LiveSetting::Off), QUOTE(LiveSetting::Polite),
      QUOTE(LiveSetting::Assertive)};
  return GetNameForPlatformConstant(id_table, std::size(id_table), identifier);
}

COMPONENT_EXPORT(AX_PLATFORM) std::string BstrToUTF8(BSTR bstr) {
  std::wstring str(bstr, SysStringLen(bstr));
  return base::WideToUTF8(str);
}

COMPONENT_EXPORT(AX_PLATFORM)
std::string UiaIdentifierToStringUTF8(int32_t id) {
  return base::WideToUTF8(UiaIdentifierToString(id));
}

COMPONENT_EXPORT(AX_PLATFORM) HWND GetHwndForProcess(base::ProcessId pid) {
  HwndWithProcId hwnd_with_proc_id(pid);
  EnumWindows(&EnumWindowsProcPid, (LPARAM)&hwnd_with_proc_id);
  return hwnd_with_proc_id.hwnd;
}

struct HWNDSearchInfo {
  std::wstring title;
  std::wstring pattern;
  HWND matched_hwnd{nullptr};
  std::vector<std::wstring> matched_titles;
};

BOOL CALLBACK MatchWindow(HWND hwnd, LPARAM lParam) {
  int length = ::GetWindowTextLength(hwnd);
  if (length == 0) {
    return TRUE;
  }

  std::wstring title(length, '\0');
  int actual_length = ::GetWindowText(hwnd, &title.front(), title.size() + 1);
  if (length > actual_length)
    title.erase(actual_length);

  auto* info = reinterpret_cast<HWNDSearchInfo*>(lParam);
  if (base::EndsWith(title, info->title) &&
      (info->pattern.empty() ||
       base::MatchPattern(base::AsStringPiece16(title),
                          base::AsStringPiece16(info->pattern)))) {
    info->matched_titles.push_back(title);
    info->matched_hwnd = hwnd;
  }
  return TRUE;
}

COMPONENT_EXPORT(AX_PLATFORM)
HWND GetHWNDBySelector(const AXTreeSelector& selector) {
  if (selector.widget) {
    return selector.widget;
  }

  HWNDSearchInfo info;
  if (selector.types & AXTreeSelector::Chrome) {
    info.title = kChromeTitle;
  } else if (selector.types & AXTreeSelector::Chromium) {
    info.title = kChromiumTitle;
  } else if (selector.types & AXTreeSelector::Edge) {
    info.title = kEdgeTitle;
  } else if (selector.types & AXTreeSelector::Firefox) {
    info.title = kFirefoxTitle;
  }

  if (!selector.pattern.empty()) {
    info.pattern = base::UTF8ToWide(selector.pattern);
  } else if (info.title.empty()) {
    LOG(ERROR) << selector.AppName()
               << " application is not supported on the system";
    return NULL;
  }

  // A match is found when the window title ends with the search title
  // and matches the search pattern when provided.
  // Note that both the search title and pattern are optional, but at
  // least one should be provided.
  DCHECK(!info.title.empty() || !info.pattern.empty());

  ::EnumWindows(MatchWindow, reinterpret_cast<LPARAM>(&info));

  // Fail if multiple matches are found.
  if (info.matched_titles.size() > 1) {
    LOG(ERROR) << "Ambiguous name: multiple windows matched:";
    for (auto title : info.matched_titles) {
      LOG(ERROR) << "  " << title;
      // Extra empty log to avoid jamming titles together. Apparently titles
      // contain special characters that prevent from printing anything coming
      // afterwards.
      LOG(ERROR) << "";
    }
    return NULL;
  }

  return info.matched_hwnd;
}

COMPONENT_EXPORT(AX_PLATFORM)
std::u16string RoleVariantToU16String(const base::win::ScopedVariant& role) {
  if (role.type() == VT_I4)
    return base::WideToUTF16(IAccessible2RoleToString(V_I4(role.ptr())));
  if (role.type() == VT_BSTR)
    return base::WideToUTF16(V_BSTR(role.ptr()));
  return std::u16string();
}

COMPONENT_EXPORT(AX_PLATFORM)
std::string RoleVariantToString(const base::win::ScopedVariant& role) {
  if (role.type() == VT_I4)
    return base::WideToUTF8(IAccessible2RoleToString(V_I4(role.ptr())));
  if (role.type() == VT_BSTR)
    return base::WideToUTF8(
        std::wstring(V_BSTR(role.ptr()), SysStringLen(V_BSTR(role.ptr()))));
  return std::string();
}

COMPONENT_EXPORT(AX_PLATFORM)
std::optional<std::string> GetIAccessible2Attribute(
    Microsoft::WRL::ComPtr<IAccessible2> element,
    std::string attribute) {
  base::win::ScopedBstr bstr;
  if (element->get_attributes(bstr.Receive()) == S_OK) {
    std::vector<std::string> ia2_attributes =
        base::SplitString(base::WideToUTF8(bstr.Get()), std::string(1, ';'),
                          base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);

    for (const auto& str : ia2_attributes) {
      std::vector<std::string> ia2_attribute =
          base::SplitString(str, std::string(1, ':'), base::KEEP_WHITESPACE,
                            base::SPLIT_WANT_ALL);

      if (ia2_attribute.size() != 2)
        continue;
      if (ia2_attribute[0] == attribute)
        return ia2_attribute[1];
    }
  }
  return std::nullopt;
}

COMPONENT_EXPORT(AX_PLATFORM)
std::string GetDOMId(Microsoft::WRL::ComPtr<IAccessible> element) {
  Microsoft::WRL::ComPtr<IAccessible2> ia2;
  if (S_OK != IA2QueryInterface<IAccessible2>(element.Get(), &ia2))
    return "";

  std::optional<std::string> id = GetIAccessible2Attribute(ia2, "id");
  if (id) {
    return *id;
  }
  return "";
}

COMPONENT_EXPORT(AX_PLATFORM)
std::vector<Microsoft::WRL::ComPtr<IAccessible>> IAccessibleChildrenOf(
    Microsoft::WRL::ComPtr<IAccessible> parent) {
  auto children = std::vector<Microsoft::WRL::ComPtr<IAccessible>>();
  for (const MSAAChild& msaa_child : MSAAChildren(parent)) {
    Microsoft::WRL::ComPtr<IAccessible> child = msaa_child.AsIAccessible();
    if (child) {
      children.emplace_back(child);
    }
  }
  return children;
}

MSAAChild::MSAAChild() = default;
MSAAChild::MSAAChild(IAccessible* parent, VARIANT&& variant) : parent_(parent) {
  child_variant_.Reset(variant);

  Microsoft::WRL::ComPtr<IDispatch> dispatch;
  if (child_variant_.type() == VT_DISPATCH) {
    dispatch = V_DISPATCH(child_variant_.ptr());
  } else if (child_variant_.type() == VT_I4) {
    if (FAILED(parent->get_accChild(child_variant_, &dispatch))) {
      child_variant_.Reset();
    }
  }

  if (dispatch) {
    if (FAILED(dispatch.As(&child_))) {
      child_ = nullptr;
    }
  }
}
MSAAChild::MSAAChild(MSAAChild&&) = default;
MSAAChild::~MSAAChild() = default;

MSAAChildren::MSAAChildren(IAccessible* parent) {
  if (FAILED(parent->get_accChildCount(&count_)))
    return;

  auto children_variants = base::HeapArray<VARIANT>::Uninit(count_);
  if (FAILED(AccessibleChildren(parent, 0, count_, children_variants.data(),
                                &count_))) {
    count_ = 0;
    return;
  }

  children_.reserve(count_);
  for (LONG i = 0; i < count_; i++)
    children_.emplace_back(parent, std::move(children_variants[i]));
}

MSAAChildren::MSAAChildren(const Microsoft::WRL::ComPtr<IAccessible>& parent)
    : MSAAChildren(parent.Get()) {}
MSAAChildren::~MSAAChildren() = default;

MSAAChildren::Iterator::Iterator(MSAAChildren* children)
    : children_(children) {}
MSAAChildren::Iterator::Iterator(MSAAChildren* children, LONG index)
    : index_(index), children_(children) {}
MSAAChildren::Iterator::Iterator(const Iterator&) = default;
MSAAChildren::Iterator::~Iterator() = default;

}  // namespace ui