chromium/chrome/browser/win/ui_automation_util.cc

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

#include "chrome/browser/win/ui_automation_util.h"

#include <iterator>

#include "base/logging.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/win/scoped_safearray.h"
#include "base/win/scoped_variant.h"

std::wstring GetCachedBstrValue(IUIAutomationElement* element,
                                PROPERTYID property_id) {
  HRESULT result = S_OK;
  base::win::ScopedVariant var;

  result = element->GetCachedPropertyValueEx(property_id, TRUE, var.Receive());
  if (FAILED(result))
    return std::wstring();

  if (V_VT(var.ptr()) != VT_BSTR) {
    LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
        << __func__ << " property is not a BSTR: " << V_VT(var.ptr());
    return std::wstring();
  }

  return std::wstring(V_BSTR(var.ptr()));
}

bool GetCachedBoolValue(IUIAutomationElement* element, PROPERTYID property_id) {
#if DCHECK_IS_ON()
  base::win::ScopedVariant var;

  if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
                                               var.Receive()))) {
    return false;
  }

  if (V_VT(var.ptr()) != VT_BOOL) {
    LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
        << __func__ << " property is not a BOOL: " << V_VT(var.ptr());
    return false;
  }

  return V_BOOL(var.ptr()) != 0;
#else   // DCHECK_IS_ON()
  return false;
#endif  // !DCHECK_IS_ON()
}

int32_t GetCachedInt32Value(IUIAutomationElement* element,
                            PROPERTYID property_id) {
#if DCHECK_IS_ON()
  base::win::ScopedVariant var;

  if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
                                               var.Receive()))) {
    return false;
  }

  if (V_VT(var.ptr()) != VT_I4) {
    LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
        << __func__ << " property is not an I4: " << V_VT(var.ptr());
    return false;
  }

  return V_I4(var.ptr());
#else   // DCHECK_IS_ON()
  return 0;
#endif  // !DCHECK_IS_ON()
}

std::vector<int32_t> GetCachedInt32ArrayValue(IUIAutomationElement* element,
                                              PROPERTYID property_id) {
  std::vector<int32_t> values;
#if DCHECK_IS_ON()
  base::win::ScopedVariant var;

  if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
                                               var.Receive()))) {
    return values;
  }

  if (V_VT(var.ptr()) != (VT_I4 | VT_ARRAY)) {
    LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
        << __func__ << " property is not an I4 array: " << V_VT(var.ptr());
    return values;
  }

  // Convert to ScopedSafearray for convenient access to the data.
  base::win::ScopedSafearray scoped_array(var.Release().parray);

  auto lock_scope = scoped_array.CreateLockScope<VT_I4>();
  if (!lock_scope) {
    return values;
  }

  values.assign(lock_scope->begin(), lock_scope->end());
#endif  // DCHECK_IS_ON()
  return values;
}

std::string IntArrayToString(const std::vector<int32_t>& values) {
#if DCHECK_IS_ON()
  std::vector<std::string> value_strings;
  base::ranges::transform(
      values, std::back_inserter(value_strings),
      [](int32_t value) { return base::NumberToString(value); });
  return base::JoinString(value_strings, ", ");
#else   // DCHECK_IS_ON()
  return std::string();
#endif  // !DCHECK_IS_ON()
}

const char* GetEventName(EVENTID event_id) {
#if DCHECK_IS_ON()
  switch (event_id) {
    case UIA_ToolTipOpenedEventId:
      return "UIA_ToolTipOpenedEventId";
    case UIA_ToolTipClosedEventId:
      return "UIA_ToolTipClosedEventId";
    case UIA_StructureChangedEventId:
      return "UIA_StructureChangedEventId";
    case UIA_MenuOpenedEventId:
      return "UIA_MenuOpenedEventId";
    case UIA_AutomationPropertyChangedEventId:
      return "UIA_AutomationPropertyChangedEventId";
    case UIA_AutomationFocusChangedEventId:
      return "UIA_AutomationFocusChangedEventId";
    case UIA_AsyncContentLoadedEventId:
      return "UIA_AsyncContentLoadedEventId";
    case UIA_MenuClosedEventId:
      return "UIA_MenuClosedEventId";
    case UIA_LayoutInvalidatedEventId:
      return "UIA_LayoutInvalidatedEventId";
    case UIA_Invoke_InvokedEventId:
      return "UIA_Invoke_InvokedEventId";
    case UIA_SelectionItem_ElementAddedToSelectionEventId:
      return "UIA_SelectionItem_ElementAddedToSelectionEventId";
    case UIA_SelectionItem_ElementRemovedFromSelectionEventId:
      return "UIA_SelectionItem_ElementRemovedFromSelectionEventId";
    case UIA_SelectionItem_ElementSelectedEventId:
      return "UIA_SelectionItem_ElementSelectedEventId";
    case UIA_Selection_InvalidatedEventId:
      return "UIA_Selection_InvalidatedEventId";
    case UIA_Text_TextSelectionChangedEventId:
      return "UIA_Text_TextSelectionChangedEventId";
    case UIA_Text_TextChangedEventId:
      return "UIA_Text_TextChangedEventId";
    case UIA_Window_WindowOpenedEventId:
      return "UIA_Window_WindowOpenedEventId";
    case UIA_Window_WindowClosedEventId:
      return "UIA_Window_WindowClosedEventId";
    case UIA_MenuModeStartEventId:
      return "UIA_MenuModeStartEventId";
    case UIA_MenuModeEndEventId:
      return "UIA_MenuModeEndEventId";
    case UIA_InputReachedTargetEventId:
      return "UIA_InputReachedTargetEventId";
    case UIA_InputReachedOtherElementEventId:
      return "UIA_InputReachedOtherElementEventId";
    case UIA_InputDiscardedEventId:
      return "UIA_InputDiscardedEventId";
    case UIA_SystemAlertEventId:
      return "UIA_SystemAlertEventId";
    case UIA_LiveRegionChangedEventId:
      return "UIA_LiveRegionChangedEventId";
    case UIA_HostedFragmentRootsInvalidatedEventId:
      return "UIA_HostedFragmentRootsInvalidatedEventId";
    case UIA_Drag_DragStartEventId:
      return "UIA_Drag_DragStartEventId";
    case UIA_Drag_DragCancelEventId:
      return "UIA_Drag_DragCancelEventId";
    case UIA_Drag_DragCompleteEventId:
      return "UIA_Drag_DragCompleteEventId";
    case UIA_DropTarget_DragEnterEventId:
      return "UIA_DropTarget_DragEnterEventId";
    case UIA_DropTarget_DragLeaveEventId:
      return "UIA_DropTarget_DragLeaveEventId";
    case UIA_DropTarget_DroppedEventId:
      return "UIA_DropTarget_DroppedEventId";
    case UIA_TextEdit_TextChangedEventId:
      return "UIA_TextEdit_TextChangedEventId";
    case UIA_TextEdit_ConversionTargetChangedEventId:
      return "UIA_TextEdit_ConversionTargetChangedEventId";
    case UIA_ActiveTextPositionChangedEventId:
      return "UIA_ActiveTextPositionChangedEventId";
  }
#endif  // DCHECK_IS_ON()
  return "";
}

const char* GetControlType(long control_type) {
#if DCHECK_IS_ON()
  switch (control_type) {
    case UIA_ButtonControlTypeId:
      return "UIA_ButtonControlTypeId";
    case UIA_CalendarControlTypeId:
      return "UIA_CalendarControlTypeId";
    case UIA_CheckBoxControlTypeId:
      return "UIA_CheckBoxControlTypeId";
    case UIA_ComboBoxControlTypeId:
      return "UIA_ComboBoxControlTypeId";
    case UIA_EditControlTypeId:
      return "UIA_EditControlTypeId";
    case UIA_HyperlinkControlTypeId:
      return "UIA_HyperlinkControlTypeId";
    case UIA_ImageControlTypeId:
      return "UIA_ImageControlTypeId";
    case UIA_ListItemControlTypeId:
      return "UIA_ListItemControlTypeId";
    case UIA_ListControlTypeId:
      return "UIA_ListControlTypeId";
    case UIA_MenuControlTypeId:
      return "UIA_MenuControlTypeId";
    case UIA_MenuBarControlTypeId:
      return "UIA_MenuBarControlTypeId";
    case UIA_MenuItemControlTypeId:
      return "UIA_MenuItemControlTypeId";
    case UIA_ProgressBarControlTypeId:
      return "UIA_ProgressBarControlTypeId";
    case UIA_RadioButtonControlTypeId:
      return "UIA_RadioButtonControlTypeId";
    case UIA_ScrollBarControlTypeId:
      return "UIA_ScrollBarControlTypeId";
    case UIA_SliderControlTypeId:
      return "UIA_SliderControlTypeId";
    case UIA_SpinnerControlTypeId:
      return "UIA_SpinnerControlTypeId";
    case UIA_StatusBarControlTypeId:
      return "UIA_StatusBarControlTypeId";
    case UIA_TabControlTypeId:
      return "UIA_TabControlTypeId";
    case UIA_TabItemControlTypeId:
      return "UIA_TabItemControlTypeId";
    case UIA_TextControlTypeId:
      return "UIA_TextControlTypeId";
    case UIA_ToolBarControlTypeId:
      return "UIA_ToolBarControlTypeId";
    case UIA_ToolTipControlTypeId:
      return "UIA_ToolTipControlTypeId";
    case UIA_TreeControlTypeId:
      return "UIA_TreeControlTypeId";
    case UIA_TreeItemControlTypeId:
      return "UIA_TreeItemControlTypeId";
    case UIA_CustomControlTypeId:
      return "UIA_CustomControlTypeId";
    case UIA_GroupControlTypeId:
      return "UIA_GroupControlTypeId";
    case UIA_ThumbControlTypeId:
      return "UIA_ThumbControlTypeId";
    case UIA_DataGridControlTypeId:
      return "UIA_DataGridControlTypeId";
    case UIA_DataItemControlTypeId:
      return "UIA_DataItemControlTypeId";
    case UIA_DocumentControlTypeId:
      return "UIA_DocumentControlTypeId";
    case UIA_SplitButtonControlTypeId:
      return "UIA_SplitButtonControlTypeId";
    case UIA_WindowControlTypeId:
      return "UIA_WindowControlTypeId";
    case UIA_PaneControlTypeId:
      return "UIA_PaneControlTypeId";
    case UIA_HeaderControlTypeId:
      return "UIA_HeaderControlTypeId";
    case UIA_HeaderItemControlTypeId:
      return "UIA_HeaderItemControlTypeId";
    case UIA_TableControlTypeId:
      return "UIA_TableControlTypeId";
    case UIA_TitleBarControlTypeId:
      return "UIA_TitleBarControlTypeId";
    case UIA_SeparatorControlTypeId:
      return "UIA_SeparatorControlTypeId";
    case UIA_SemanticZoomControlTypeId:
      return "UIA_SemanticZoomControlTypeId";
    case UIA_AppBarControlTypeId:
      return "UIA_AppBarControlTypeId";
  }
#endif  // DCHECK_IS_ON()
  return "";
}