| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/accessibility/accessibility_tree_formatter_uia_win.h" |
| |
| #include <math.h> |
| #include <oleacc.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <uiautomation.h> |
| #include <wrl/client.h> |
| |
| #include <iostream> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "base/win/com_init_util.h" |
| #include "base/win/scoped_bstr.h" |
| #include "base/win/scoped_variant.h" |
| #include "base/win/windows_version.h" |
| #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h" |
| #include "content/browser/accessibility/browser_accessibility_manager.h" |
| #include "ui/accessibility/accessibility_switches.h" |
| #include "ui/gfx/win/hwnd_util.h" |
| |
| namespace { |
| |
| base::string16 UiaIdentifierToCondensedString16(int32_t id) { |
| base::string16 identifier = content::UiaIdentifierToString(id); |
| if (id >= UIA_RuntimeIdPropertyId && id <= UIA_HeadingLevelPropertyId) { |
| // remove leading 'UIA_' and trailing 'PropertyId' |
| return identifier.substr(4, identifier.size() - 14); |
| } |
| return identifier; |
| } |
| |
| std::string UiaIdentifierToCondensedString(int32_t id) { |
| return base::UTF16ToUTF8(UiaIdentifierToCondensedString16(id)); |
| } |
| |
| } // namespace |
| |
| namespace content { |
| |
| // This is the list of interesting properties to dump. |
| // |
| // Certain properties are skipped because they are known to cause crashes if the |
| // underlying pattern isn't implemented (e.g., LegacyIAccessibleSelection will |
| // crash on Win7 if the LegacyIAccessible pattern isn't implemented). |
| // |
| // Other properties aren't interesting in a tree-dump context (e.g., ProcessId). |
| // |
| // Finally, certain properties are dumped as part of a pattern, and don't need |
| // to be dumped a second time here (e.g., Grid*, GridItem*, RangeValue*, etc.). |
| |
| // static |
| const long AccessibilityTreeFormatterUia::properties_[] = { |
| // UIA_RuntimeIdPropertyId // 30000 |
| UIA_BoundingRectanglePropertyId, // 30001 |
| // UIA_ProcessIdPropertyId // 30002 |
| UIA_ControlTypePropertyId, // 30003 |
| UIA_LocalizedControlTypePropertyId, // 30004 |
| UIA_NamePropertyId, // 30005 |
| UIA_AcceleratorKeyPropertyId, // 30006 |
| UIA_AccessKeyPropertyId, // 30007 |
| UIA_HasKeyboardFocusPropertyId, // 30008 |
| UIA_IsKeyboardFocusablePropertyId, // 30009 |
| UIA_IsEnabledPropertyId, // 30010 |
| UIA_AutomationIdPropertyId, // 30011 |
| UIA_ClassNamePropertyId, // 30012 |
| UIA_HelpTextPropertyId, // 30013 |
| UIA_ClickablePointPropertyId, // 30014 |
| UIA_CulturePropertyId, // 30015 |
| UIA_IsControlElementPropertyId, // 30016 |
| UIA_IsContentElementPropertyId, // 30017 |
| UIA_LabeledByPropertyId, // 30018 |
| UIA_IsPasswordPropertyId, // 30019 |
| // UIA_NativeWindowHandlePropertyId // 30020 |
| UIA_ItemTypePropertyId, // 30021 |
| UIA_IsOffscreenPropertyId, // 30022 |
| UIA_OrientationPropertyId, // 30023 |
| UIA_FrameworkIdPropertyId, // 30024 |
| UIA_IsRequiredForFormPropertyId, // 30025 |
| UIA_ItemStatusPropertyId, // 30026 |
| UIA_IsDockPatternAvailablePropertyId, // 30027 |
| UIA_IsExpandCollapsePatternAvailablePropertyId, // 30028 |
| UIA_IsGridItemPatternAvailablePropertyId, // 30029 |
| UIA_IsGridPatternAvailablePropertyId, // 30030 |
| UIA_IsInvokePatternAvailablePropertyId, // 30031 |
| UIA_IsMultipleViewPatternAvailablePropertyId, // 30032 |
| UIA_IsRangeValuePatternAvailablePropertyId, // 30033 |
| UIA_IsScrollPatternAvailablePropertyId, // 30034 |
| UIA_IsScrollItemPatternAvailablePropertyId, // 30035 |
| UIA_IsSelectionItemPatternAvailablePropertyId, // 30036 |
| UIA_IsSelectionPatternAvailablePropertyId, // 30037 |
| UIA_IsTablePatternAvailablePropertyId, // 30038 |
| UIA_IsTableItemPatternAvailablePropertyId, // 30039 |
| UIA_IsTextPatternAvailablePropertyId, // 30040 |
| UIA_IsTogglePatternAvailablePropertyId, // 30041 |
| UIA_IsTransformPatternAvailablePropertyId, // 30042 |
| UIA_IsValuePatternAvailablePropertyId, // 30043 |
| UIA_IsWindowPatternAvailablePropertyId, // 30044 |
| // UIA_Value* // 30045-30046 |
| // UIA_RangeValue* // 30047-30052 |
| // UIA_Scroll* // 30053-30058 |
| // UIA_Selection* // 30059-30061 |
| // UIA_Grid* // 30062-30068 |
| // UIA_DockDockPositionPropertyId, // 30069 |
| // UIA_ExpandCollapseExpandCollapseStatePropertyId, // 30070 |
| // UIA_MultipleViewCurrentViewPropertyId, // 30071 |
| // UIA_MultipleViewSupportedViewsPropertyId, // 30072 |
| // UIA_WindowCanMaximizePropertyId, // 30073 |
| // UIA_WindowCanMinimizePropertyId, // 30074 |
| // UIA_WindowWindowVisualStatePropertyId, // 30075 |
| // UIA_WindowWindowInteractionStatePropertyId, // 30076 |
| // UIA_WindowIsModalPropertyId // 30077 |
| // UIA_WindowIsTopmostPropertyId, // 30078 |
| // UIA_SelectionItem* // 30079-30080 |
| // UIA_TableRowHeadersPropertyId, // 30081 |
| // UIA_TableColumnHeadersPropertyId, // 30082 |
| // UIA_TableRowOrColumnMajorPropertyId // 30083 |
| // UIA_TableItemRowHeaderItemsPropertyId, // 30084 |
| // UIA_TableItemColumnHeaderItemsPropertyId, // 30085 |
| // UIA_ToggleToggleStatePropertyId // 30086 |
| // UIA_TransformCanMovePropertyId, // 30087 |
| // UIA_TransformCanResizePropertyId, // 30088 |
| // UIA_TransformCanRotatePropertyId, // 30089 |
| UIA_IsLegacyIAccessiblePatternAvailablePropertyId, // 30090 |
| // UIA_LegacyIAccessible* // 30091-30100 |
| UIA_AriaRolePropertyId, // 30101 |
| UIA_AriaPropertiesPropertyId, // 30102 |
| UIA_IsDataValidForFormPropertyId, // 30103 |
| UIA_ControllerForPropertyId, // 30104 |
| UIA_DescribedByPropertyId, // 30105 |
| UIA_FlowsToPropertyId, // 30106 |
| // UIA_ProviderDescriptionPropertyId // 30107 |
| UIA_IsItemContainerPatternAvailablePropertyId, // 30108 |
| UIA_IsVirtualizedItemPatternAvailablePropertyId, // 30109 |
| UIA_IsSynchronizedInputPatternAvailablePropertyId, // 30110 |
| UIA_OptimizeForVisualContentPropertyId, // 30111 |
| UIA_IsObjectModelPatternAvailablePropertyId, // 30112 |
| UIA_AnnotationAnnotationTypeIdPropertyId, // 30113 |
| UIA_AnnotationAnnotationTypeNamePropertyId, // 30114 |
| UIA_AnnotationAuthorPropertyId, // 30115 |
| UIA_AnnotationDateTimePropertyId, // 30116 |
| UIA_AnnotationTargetPropertyId, // 30117 |
| UIA_IsAnnotationPatternAvailablePropertyId, // 30118 |
| UIA_IsTextPattern2AvailablePropertyId, // 30119 |
| UIA_StylesStyleIdPropertyId, // 30120 |
| UIA_StylesStyleNamePropertyId, // 30121 |
| UIA_StylesFillColorPropertyId, // 30122 |
| UIA_StylesFillPatternStylePropertyId, // 30123 |
| UIA_StylesShapePropertyId, // 30124 |
| UIA_StylesFillPatternColorPropertyId, // 30125 |
| UIA_StylesExtendedPropertiesPropertyId, // 30126 |
| UIA_IsStylesPatternAvailablePropertyId, // 30127 |
| UIA_IsSpreadsheetPatternAvailablePropertyId, // 30128 |
| UIA_SpreadsheetItemFormulaPropertyId, // 30129 |
| UIA_SpreadsheetItemAnnotationObjectsPropertyId, // 30130 |
| UIA_SpreadsheetItemAnnotationTypesPropertyId, // 30131 |
| UIA_IsSpreadsheetItemPatternAvailablePropertyId, // 30132 |
| UIA_Transform2CanZoomPropertyId, // 30133 |
| UIA_IsTransformPattern2AvailablePropertyId, // 30134 |
| UIA_LiveSettingPropertyId, // 30135 |
| UIA_IsTextChildPatternAvailablePropertyId, // 30136 |
| UIA_IsDragPatternAvailablePropertyId, // 30137 |
| UIA_DragIsGrabbedPropertyId, // 30138 |
| UIA_DragDropEffectPropertyId, // 30139 |
| UIA_DragDropEffectsPropertyId, // 30140 |
| UIA_IsDropTargetPatternAvailablePropertyId, // 30141 |
| UIA_DropTargetDropTargetEffectPropertyId, // 30142 |
| UIA_DropTargetDropTargetEffectsPropertyId, // 30143 |
| UIA_DragGrabbedItemsPropertyId, // 30144 |
| UIA_Transform2ZoomLevelPropertyId, // 30145 |
| UIA_Transform2ZoomMinimumPropertyId, // 30146 |
| UIA_Transform2ZoomMaximumPropertyId, // 30147 |
| UIA_FlowsFromPropertyId, // 30148 |
| UIA_IsTextEditPatternAvailablePropertyId, // 30149 |
| UIA_IsPeripheralPropertyId, // 30150 |
| UIA_IsCustomNavigationPatternAvailablePropertyId, // 30151 |
| UIA_PositionInSetPropertyId, // 30152 |
| UIA_SizeOfSetPropertyId, // 30153 |
| UIA_LevelPropertyId, // 30154 |
| UIA_AnnotationTypesPropertyId, // 30155 |
| UIA_AnnotationObjectsPropertyId, // 30156 |
| UIA_LandmarkTypePropertyId, // 30157 |
| UIA_LocalizedLandmarkTypePropertyId, // 30158 |
| UIA_FullDescriptionPropertyId, // 30159 |
| UIA_FillColorPropertyId, // 30160 |
| UIA_OutlineColorPropertyId, // 30161 |
| UIA_FillTypePropertyId, // 30162 |
| UIA_VisualEffectsPropertyId, // 30163 |
| UIA_OutlineThicknessPropertyId, // 30164 |
| UIA_CenterPointPropertyId, // 30165 |
| UIA_RotationPropertyId, // 30166 |
| UIA_SizePropertyId, // 30167 |
| UIA_IsSelectionPattern2AvailablePropertyId, // 30168 |
| UIA_Selection2FirstSelectedItemPropertyId, // 30169 |
| UIA_Selection2LastSelectedItemPropertyId, // 30170 |
| UIA_Selection2CurrentSelectedItemPropertyId, // 30171 |
| UIA_Selection2ItemCountPropertyId, // 30172 |
| UIA_HeadingLevelPropertyId, // 30173 |
| }; |
| |
| const long AccessibilityTreeFormatterUia::patterns_[] = { |
| UIA_SelectionPatternId, // 10001 |
| UIA_ValuePatternId, // 10002 |
| UIA_RangeValuePatternId, // 10003 |
| UIA_ScrollPatternId, // 10004 |
| UIA_ExpandCollapsePatternId, // 10005 |
| UIA_GridPatternId, // 10006 |
| UIA_GridItemPatternId, // 10007 |
| UIA_WindowPatternId, // 10009 |
| UIA_SelectionItemPatternId, // 10010 |
| UIA_TablePatternId, // 10012 |
| UIA_TogglePatternId, // 10015 |
| }; |
| |
| const long AccessibilityTreeFormatterUia::pattern_properties_[] = { |
| UIA_ValueValuePropertyId, // 30045 |
| UIA_ValueIsReadOnlyPropertyId, // 30046 |
| UIA_RangeValueValuePropertyId, // 30047 |
| UIA_RangeValueIsReadOnlyPropertyId, // 30048 |
| UIA_RangeValueMinimumPropertyId, // 30049 |
| UIA_RangeValueMaximumPropertyId, // 30050 |
| UIA_RangeValueLargeChangePropertyId, // 30051 |
| UIA_RangeValueSmallChangePropertyId, // 30052 |
| UIA_ScrollHorizontalScrollPercentPropertyId, // 30053 |
| UIA_ScrollHorizontalViewSizePropertyId, // 30054 |
| UIA_ScrollVerticalScrollPercentPropertyId, // 30055 |
| UIA_ScrollVerticalViewSizePropertyId, // 30056 |
| UIA_ScrollHorizontallyScrollablePropertyId, // 30057 |
| UIA_ScrollVerticallyScrollablePropertyId, // 30058 |
| UIA_SelectionCanSelectMultiplePropertyId, // 30060 |
| UIA_SelectionIsSelectionRequiredPropertyId, // 30061 |
| UIA_GridRowCountPropertyId, // 30062 |
| UIA_GridColumnCountPropertyId, // 30063 |
| UIA_GridItemRowPropertyId, // 30064 |
| UIA_GridItemColumnPropertyId, // 30065 |
| UIA_GridItemRowSpanPropertyId, // 30066 |
| UIA_GridItemColumnSpanPropertyId, // 30067 |
| UIA_GridItemContainingGridPropertyId, // 30068 |
| UIA_ExpandCollapseExpandCollapseStatePropertyId, // 30070 |
| UIA_WindowIsModalPropertyId, // 30077 |
| UIA_SelectionItemIsSelectedPropertyId, // 30079 |
| UIA_SelectionItemSelectionContainerPropertyId, // 30080 |
| UIA_TableRowOrColumnMajorPropertyId, // 30083 |
| UIA_ToggleToggleStatePropertyId, // 30086 |
| }; |
| // static |
| std::unique_ptr<AccessibilityTreeFormatter> |
| AccessibilityTreeFormatterUia::CreateUia() { |
| base::win::AssertComInitialized(); |
| return std::make_unique<AccessibilityTreeFormatterUia>(); |
| } |
| |
| AccessibilityTreeFormatterUia::AccessibilityTreeFormatterUia() { |
| // Create an instance of the CUIAutomation class. |
| CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, |
| IID_IUIAutomation, &uia_); |
| CHECK(uia_.Get()); |
| BuildCacheRequests(); |
| } |
| |
| AccessibilityTreeFormatterUia::~AccessibilityTreeFormatterUia() {} |
| |
| void AccessibilityTreeFormatterUia::AddDefaultFilters( |
| std::vector<PropertyFilter>* property_filters) { |
| // Too noisy: IsKeyboardFocusable, IsDataValidForForm, UIA_ScrollPatternId, |
| // Value.IsReadOnly |
| |
| // properties not exposed through a pattern |
| AddPropertyFilter(property_filters, "Name=*"); |
| AddPropertyFilter(property_filters, "ItemStatus=*"); |
| AddPropertyFilter(property_filters, "Orientation=OrientationType_Horizontal"); |
| AddPropertyFilter(property_filters, "IsPassword=true"); |
| AddPropertyFilter(property_filters, "IsControlElement=false"); |
| AddPropertyFilter(property_filters, "IsEnabled=false"); |
| AddPropertyFilter(property_filters, "IsRequiredForForm=true"); |
| |
| // UIA_ExpandCollapsePatternId |
| AddPropertyFilter(property_filters, "ExpandCollapse.ExpandCollapseState=*"); |
| |
| // UIA_GridPatternId |
| AddPropertyFilter(property_filters, "Grid.ColumnCount=*"); |
| AddPropertyFilter(property_filters, "Grid.RowCount=*"); |
| // UIA_GridItemPatternId |
| AddPropertyFilter(property_filters, "GridItem.Column=*"); |
| AddPropertyFilter(property_filters, "GridItem.ColumnSpan=*"); |
| AddPropertyFilter(property_filters, "GridItem.Row=*"); |
| AddPropertyFilter(property_filters, "GridItem.RowSpan=*"); |
| AddPropertyFilter(property_filters, "GridItem.ContainingGrid=*"); |
| // UIA_RangeValuePatternId |
| AddPropertyFilter(property_filters, "RangeValue.IsReadOnly=*"); |
| AddPropertyFilter(property_filters, "RangeValue.LargeChange=*"); |
| AddPropertyFilter(property_filters, "RangeValue.SmallChange=*"); |
| AddPropertyFilter(property_filters, "RangeValue.Maximum=*"); |
| AddPropertyFilter(property_filters, "RangeValue.Minimum=*"); |
| AddPropertyFilter(property_filters, "RangeValue.Value=*"); |
| // UIA_SelectionPatternId |
| AddPropertyFilter(property_filters, "Selection.CanSelectMultiple=*"); |
| AddPropertyFilter(property_filters, "Selection.IsSelectionRequired=*"); |
| // UIA_SelectionItemPatternId |
| AddPropertyFilter(property_filters, "SelectionItem.IsSelected=*"); |
| AddPropertyFilter(property_filters, "SelectionItem.SelectionContainer=*"); |
| // UIA_TablePatternId |
| AddPropertyFilter(property_filters, "Table.RowOrColumnMajor=*"); |
| // UIA_TogglePatternId |
| AddPropertyFilter(property_filters, "Toggle.ToggleState=*"); |
| // UIA_ValuePatternId |
| AddPropertyFilter(property_filters, "Value.Value=*"); |
| AddPropertyFilter(property_filters, "Value.Value='http*'", |
| PropertyFilter::DENY); |
| // UIA_WindowPatternId |
| AddPropertyFilter(property_filters, "Window.IsModal=*"); |
| } |
| |
| // static |
| void AccessibilityTreeFormatterUia::SetUpCommandLineForTestPass( |
| base::CommandLine* command_line) { |
| command_line->AppendSwitch(::switches::kEnableExperimentalUIAutomation); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| AccessibilityTreeFormatterUia::BuildAccessibilityTree( |
| BrowserAccessibility* start) { |
| // Find the root IUIAutomationElement for the content window. |
| HWND hwnd = |
| start->manager()->GetRoot()->GetTargetForNativeAccessibilityEvent(); |
| return BuildAccessibilityTreeForWindow(hwnd); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| AccessibilityTreeFormatterUia::BuildAccessibilityTreeForProcess( |
| base::ProcessId pid) { |
| std::unique_ptr<base::DictionaryValue> tree; |
| // Get HWND for process id. |
| HWND hwnd = GetHwndForProcess(pid); |
| return BuildAccessibilityTreeForWindow(hwnd); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| AccessibilityTreeFormatterUia::BuildAccessibilityTreeForWindow( |
| gfx::AcceleratedWidget hwnd) { |
| CHECK(hwnd); |
| |
| Microsoft::WRL::ComPtr<IUIAutomationElement> root; |
| uia_->ElementFromHandle(hwnd, &root); |
| CHECK(root.Get()); |
| |
| std::unique_ptr<base::DictionaryValue> tree = |
| std::make_unique<base::DictionaryValue>(); |
| RecursiveBuildAccessibilityTree(root.Get(), tree.get()); |
| return tree; |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| AccessibilityTreeFormatterUia::BuildAccessibilityTreeForPattern( |
| const base::StringPiece& pattern) { |
| LOG(ERROR) << "Windows does not yet support building accessibility trees for " |
| "patterns"; |
| return nullptr; |
| } |
| |
| void AccessibilityTreeFormatterUia::RecursiveBuildAccessibilityTree( |
| IUIAutomationElement* uncached_node, |
| base::DictionaryValue* dict) { |
| // Process this node. |
| AddProperties(uncached_node, dict); |
| |
| // Update the cache to get children |
| Microsoft::WRL::ComPtr<IUIAutomationElement> parent; |
| uncached_node->BuildUpdatedCache(children_cache_request_.Get(), &parent); |
| |
| Microsoft::WRL::ComPtr<IUIAutomationElementArray> children; |
| if (!SUCCEEDED(parent->GetCachedChildren(&children)) || !children) |
| return; |
| // Process children. |
| auto child_list = std::make_unique<base::ListValue>(); |
| int child_count; |
| children->get_Length(&child_count); |
| for (int i = 0; i < child_count; i++) { |
| Microsoft::WRL::ComPtr<IUIAutomationElement> child; |
| std::unique_ptr<base::DictionaryValue> child_dict = |
| std::make_unique<base::DictionaryValue>(); |
| if (SUCCEEDED(children->GetElement(i, &child))) { |
| RecursiveBuildAccessibilityTree(child.Get(), child_dict.get()); |
| } else { |
| child_dict->SetString("error", L"[Error retrieving child]"); |
| } |
| child_list->Append(std::move(child_dict)); |
| } |
| dict->Set(kChildrenDictAttr, std::move(child_list)); |
| } |
| |
| void AccessibilityTreeFormatterUia::AddProperties( |
| IUIAutomationElement* uncached_node, |
| base::DictionaryValue* dict) { |
| // Update the cache for this node's information. |
| Microsoft::WRL::ComPtr<IUIAutomationElement> node; |
| uncached_node->BuildUpdatedCache(element_cache_request_.Get(), &node); |
| |
| // Get all properties that may be on this node. |
| for (long i : properties_) { |
| base::win::ScopedVariant variant; |
| if (SUCCEEDED(node->GetCachedPropertyValue(i, variant.Receive()))) { |
| WriteProperty(i, variant, dict); |
| } |
| } |
| // Add control pattern specific properties |
| AddExpandCollapseProperties(node.Get(), dict); |
| AddGridProperties(node.Get(), dict); |
| AddGridItemProperties(node.Get(), dict); |
| AddRangeValueProperties(node.Get(), dict); |
| AddScrollProperties(node.Get(), dict); |
| AddSelectionProperties(node.Get(), dict); |
| AddSelectionItemProperties(node.Get(), dict); |
| AddTableProperties(node.Get(), dict); |
| AddToggleProperties(node.Get(), dict); |
| AddValueProperties(node.Get(), dict); |
| AddValueProperties(node.Get(), dict); |
| AddWindowProperties(node.Get(), dict); |
| } |
| |
| void AccessibilityTreeFormatterUia::AddExpandCollapseProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationExpandCollapsePattern> |
| expand_collapse_pattern; |
| if (SUCCEEDED( |
| node->GetCachedPatternAs(UIA_ExpandCollapsePatternId, |
| IID_PPV_ARGS(&expand_collapse_pattern))) && |
| expand_collapse_pattern) { |
| ExpandCollapseState current_state; |
| if (SUCCEEDED(expand_collapse_pattern->get_CachedExpandCollapseState( |
| ¤t_state))) { |
| base::string16 state; |
| switch (current_state) { |
| case ExpandCollapseState_Collapsed: |
| state = L"Collapsed"; |
| break; |
| case ExpandCollapseState_Expanded: |
| state = L"Expanded"; |
| break; |
| case ExpandCollapseState_PartiallyExpanded: |
| state = L"PartiallyExpanded"; |
| break; |
| case ExpandCollapseState_LeafNode: |
| state = L"LeafNode"; |
| break; |
| } |
| dict->SetString("ExpandCollapse.ExpandCollapseState", state); |
| } |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddGridProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationGridPattern> grid_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_GridPatternId, |
| IID_PPV_ARGS(&grid_pattern))) && |
| grid_pattern) { |
| int column_count; |
| if (SUCCEEDED(grid_pattern->get_CachedColumnCount(&column_count))) |
| dict->SetInteger("Grid.ColumnCount", column_count); |
| |
| int row_count; |
| if (SUCCEEDED(grid_pattern->get_CachedRowCount(&row_count))) |
| dict->SetInteger("Grid.RowCount", row_count); |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddGridItemProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationGridItemPattern> grid_item_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_GridItemPatternId, |
| IID_PPV_ARGS(&grid_item_pattern))) && |
| grid_item_pattern) { |
| int column; |
| if (SUCCEEDED(grid_item_pattern->get_CachedColumn(&column))) |
| dict->SetInteger("GridItem.Column", column); |
| |
| int column_span; |
| if (SUCCEEDED(grid_item_pattern->get_CachedColumnSpan(&column_span))) |
| dict->SetInteger("GridItem.ColumnSpan", column_span); |
| |
| int row; |
| if (SUCCEEDED(grid_item_pattern->get_CachedRow(&row))) |
| dict->SetInteger("GridItem.Row", row); |
| |
| int row_span; |
| if (SUCCEEDED(grid_item_pattern->get_CachedRowSpan(&row_span))) |
| dict->SetInteger("GridItem.RowSpan", row_span); |
| Microsoft::WRL::ComPtr<IUIAutomationElement> containing_grid; |
| if (SUCCEEDED( |
| grid_item_pattern->get_CachedContainingGrid(&containing_grid))) { |
| dict->SetString("GridItem.ContainingGrid", |
| GetNodeName(containing_grid.Get())); |
| ; |
| } |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddRangeValueProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationRangeValuePattern> range_value_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_RangeValuePatternId, |
| IID_PPV_ARGS(&range_value_pattern))) && |
| range_value_pattern) { |
| BOOL is_read_only; |
| if (SUCCEEDED(range_value_pattern->get_CachedIsReadOnly(&is_read_only))) |
| dict->SetBoolean("RangeValue.IsReadOnly", is_read_only); |
| |
| double large_change; |
| if (SUCCEEDED(range_value_pattern->get_CachedLargeChange(&large_change))) |
| dict->SetDouble("RangeValue.LargeChange", large_change); |
| |
| double small_change; |
| if (SUCCEEDED(range_value_pattern->get_CachedSmallChange(&small_change))) |
| dict->SetDouble("RangeValue.SmallChange", small_change); |
| |
| double maximum; |
| if (SUCCEEDED(range_value_pattern->get_CachedMaximum(&maximum))) |
| dict->SetDouble("RangeValue.Maximum", maximum); |
| |
| double minimum; |
| if (SUCCEEDED(range_value_pattern->get_CachedMinimum(&minimum))) |
| dict->SetDouble("RangeValue.Minimum", minimum); |
| |
| double value; |
| if (SUCCEEDED(range_value_pattern->get_CachedValue(&value))) |
| dict->SetDouble("RangeValue.Value", value); |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddScrollProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationScrollPattern> scroll_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_ScrollPatternId, |
| IID_PPV_ARGS(&scroll_pattern))) && |
| scroll_pattern) { |
| double horizontal_scroll_percent; |
| if (SUCCEEDED(scroll_pattern->get_CachedHorizontalScrollPercent( |
| &horizontal_scroll_percent))) { |
| dict->SetDouble("Scroll.HorizontalScrollPercent", |
| horizontal_scroll_percent); |
| } |
| |
| double horizontal_view_size; |
| if (SUCCEEDED(scroll_pattern->get_CachedHorizontalViewSize( |
| &horizontal_view_size))) |
| dict->SetDouble("Scroll.HorizontalViewSize", horizontal_view_size); |
| |
| BOOL horizontally_scrollable; |
| if (SUCCEEDED(scroll_pattern->get_CachedHorizontallyScrollable( |
| &horizontally_scrollable))) { |
| dict->SetBoolean("Scroll.HorizontallyScrollable", |
| horizontally_scrollable); |
| } |
| |
| double vertical_scroll_percent; |
| if (SUCCEEDED(scroll_pattern->get_CachedVerticalScrollPercent( |
| &vertical_scroll_percent))) |
| dict->SetDouble("Scroll.VerticalScrollPercent", vertical_scroll_percent); |
| |
| double vertical_view_size; |
| if (SUCCEEDED( |
| scroll_pattern->get_CachedVerticalViewSize(&vertical_view_size))) |
| dict->SetDouble("Scroll.VerticalViewSize", vertical_view_size); |
| |
| BOOL vertically_scrollable; |
| if (SUCCEEDED(scroll_pattern->get_CachedVerticallyScrollable( |
| &vertically_scrollable))) |
| dict->SetBoolean("Scroll.VerticallyScrollable", vertically_scrollable); |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddSelectionProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationSelectionPattern> selection_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_SelectionPatternId, |
| IID_PPV_ARGS(&selection_pattern))) && |
| selection_pattern) { |
| BOOL can_select_multiple; |
| if (SUCCEEDED(selection_pattern->get_CachedCanSelectMultiple( |
| &can_select_multiple))) |
| dict->SetBoolean("Selection.CanSelectMultiple", can_select_multiple); |
| |
| BOOL is_selection_required; |
| if (SUCCEEDED(selection_pattern->get_CachedIsSelectionRequired( |
| &is_selection_required))) |
| dict->SetBoolean("Selection.IsSelectionRequired", is_selection_required); |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddSelectionItemProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationSelectionItemPattern> |
| selection_item_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs( |
| UIA_SelectionItemPatternId, IID_PPV_ARGS(&selection_item_pattern))) && |
| selection_item_pattern) { |
| BOOL is_selected; |
| if (SUCCEEDED(selection_item_pattern->get_CachedIsSelected(&is_selected))) |
| dict->SetBoolean("SelectionItem.IsSelected", is_selected); |
| |
| Microsoft::WRL::ComPtr<IUIAutomationElement> selection_container; |
| if (SUCCEEDED(selection_item_pattern->get_CachedSelectionContainer( |
| &selection_container))) { |
| dict->SetString("SelectionItem.SelectionContainer", |
| GetNodeName(selection_container.Get())); |
| } |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddTableProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationTablePattern> table_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_TablePatternId, |
| IID_PPV_ARGS(&table_pattern))) && |
| table_pattern) { |
| RowOrColumnMajor row_or_column_major; |
| if (SUCCEEDED( |
| table_pattern->get_CachedRowOrColumnMajor(&row_or_column_major))) { |
| base::string16 row_or_column_string; |
| switch (row_or_column_major) { |
| case RowOrColumnMajor_RowMajor: |
| row_or_column_string = L"RowMajor"; |
| break; |
| case RowOrColumnMajor_ColumnMajor: |
| row_or_column_string = L"ColumnMajor"; |
| break; |
| case RowOrColumnMajor_Indeterminate: |
| row_or_column_string = L"Indeterminate"; |
| break; |
| } |
| dict->SetString("Table.RowOrColumnMajor", row_or_column_string); |
| } |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddToggleProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationTogglePattern> toggle_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_TogglePatternId, |
| IID_PPV_ARGS(&toggle_pattern))) && |
| toggle_pattern) { |
| ToggleState toggle_state; |
| if (SUCCEEDED(toggle_pattern->get_CachedToggleState(&toggle_state))) { |
| base::string16 toggle_state_string; |
| switch (toggle_state) { |
| case ToggleState_Off: |
| toggle_state_string = L"Off"; |
| break; |
| case ToggleState_On: |
| toggle_state_string = L"On"; |
| break; |
| case ToggleState_Indeterminate: |
| toggle_state_string = L"Indeterminate"; |
| break; |
| } |
| dict->SetString("Toggle.ToggleState", toggle_state_string); |
| } |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddValueProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationValuePattern> value_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_ValuePatternId, |
| IID_PPV_ARGS(&value_pattern))) && |
| value_pattern) { |
| BOOL is_read_only; |
| if (SUCCEEDED(value_pattern->get_CachedIsReadOnly(&is_read_only))) |
| dict->SetBoolean("Value.IsReadOnly", is_read_only); |
| |
| base::win::ScopedBstr value; |
| if (SUCCEEDED(value_pattern->get_CachedValue(value.Receive()))) |
| dict->SetString("Value.Value", BstrToUTF8(value)); |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::AddWindowProperties( |
| IUIAutomationElement* node, |
| base::DictionaryValue* dict) { |
| Microsoft::WRL::ComPtr<IUIAutomationWindowPattern> window_pattern; |
| if (SUCCEEDED(node->GetCachedPatternAs(UIA_WindowPatternId, |
| IID_PPV_ARGS(&window_pattern))) && |
| window_pattern) { |
| BOOL is_modal; |
| if (SUCCEEDED(window_pattern->get_CachedIsModal(&is_modal))) |
| dict->SetBoolean("Window.IsModal", is_modal); |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::WriteProperty( |
| long propertyId, |
| const base::win::ScopedVariant& var, |
| base::DictionaryValue* dict) { |
| switch (var.type()) { |
| case VT_EMPTY: |
| case VT_NULL: |
| break; |
| case VT_I2: |
| dict->SetInteger(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->iVal); |
| break; |
| case VT_I4: |
| WriteI4Property(propertyId, var.ptr()->lVal, dict); |
| break; |
| case VT_R4: |
| dict->SetDouble(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->fltVal); |
| break; |
| case VT_R8: |
| dict->SetDouble(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->dblVal); |
| break; |
| case VT_I1: |
| dict->SetInteger(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->cVal); |
| break; |
| case VT_UI1: |
| dict->SetInteger(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->bVal); |
| break; |
| case VT_UI2: |
| dict->SetInteger(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->uiVal); |
| break; |
| case VT_UI4: |
| dict->SetInteger(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->ulVal); |
| break; |
| break; |
| case VT_BSTR: |
| dict->SetString(UiaIdentifierToCondensedString(propertyId), |
| BstrToUTF8(var.ptr()->bstrVal)); |
| break; |
| case VT_BOOL: |
| dict->SetBoolean(UiaIdentifierToCondensedString(propertyId), |
| var.ptr()->boolVal == VARIANT_TRUE ? true : false); |
| break; |
| case VT_UNKNOWN: |
| WriteUnknownProperty(propertyId, var.ptr()->punkVal, dict); |
| break; |
| case VT_DISPATCH: |
| case VT_ERROR: |
| case VT_CY: |
| case VT_DATE: |
| case VT_VARIANT: |
| case VT_DECIMAL: |
| case VT_INT: |
| case VT_UINT: |
| case VT_ARRAY: |
| case VT_BYREF: |
| default: |
| break; |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::WriteI4Property( |
| long propertyId, |
| long lval, |
| base::DictionaryValue* dict) { |
| switch (propertyId) { |
| case UIA_ControlTypePropertyId: |
| dict->SetString(UiaIdentifierToCondensedString(propertyId), |
| UiaIdentifierToCondensedString16(lval)); |
| break; |
| case UIA_OrientationPropertyId: |
| dict->SetString(UiaIdentifierToCondensedString(propertyId), |
| UiaOrientationToString(lval)); |
| break; |
| case UIA_LiveSettingPropertyId: |
| dict->SetString(UiaIdentifierToCondensedString(propertyId), |
| UiaLiveSettingToString(lval)); |
| break; |
| default: |
| dict->SetInteger(UiaIdentifierToCondensedString(propertyId), lval); |
| break; |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::WriteUnknownProperty( |
| long propertyId, |
| IUnknown* unk, |
| base::DictionaryValue* dict) { |
| switch (propertyId) { |
| case UIA_ControllerForPropertyId: |
| case UIA_DescribedByPropertyId: |
| case UIA_FlowsFromPropertyId: |
| case UIA_FlowsToPropertyId: { |
| Microsoft::WRL::ComPtr<IUIAutomationElementArray> array; |
| if (unk && SUCCEEDED(unk->QueryInterface(IID_PPV_ARGS(&array)))) |
| WriteElementArray(propertyId, array.Get(), dict); |
| break; |
| } |
| case UIA_LabeledByPropertyId: { |
| Microsoft::WRL::ComPtr<IUIAutomationElement> node; |
| if (unk && SUCCEEDED(unk->QueryInterface(IID_PPV_ARGS(&node)))) { |
| dict->SetString(UiaIdentifierToCondensedString(propertyId), |
| GetNodeName(node.Get())); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| void AccessibilityTreeFormatterUia::WriteElementArray( |
| long propertyId, |
| IUIAutomationElementArray* array, |
| base::DictionaryValue* dict) { |
| int count; |
| array->get_Length(&count); |
| base::string16 element_list = L""; |
| for (int i = 0; i < count; i++) { |
| Microsoft::WRL::ComPtr<IUIAutomationElement> element; |
| if (SUCCEEDED(array->GetElement(i, &element))) { |
| if (element_list != L"") { |
| element_list += L", "; |
| } |
| element_list += GetNodeName(element.Get()); |
| } |
| } |
| dict->SetString(UiaIdentifierToCondensedString(propertyId), element_list); |
| } |
| |
| base::string16 AccessibilityTreeFormatterUia::GetNodeName( |
| IUIAutomationElement* uncached_node) { |
| // Update the cache for this node. |
| if (uncached_node) { |
| Microsoft::WRL::ComPtr<IUIAutomationElement> node; |
| uncached_node->BuildUpdatedCache(element_cache_request_.Get(), &node); |
| |
| base::win::ScopedBstr name; |
| base::win::ScopedVariant variant; |
| if (SUCCEEDED(node->GetCachedPropertyValue(UIA_NamePropertyId, |
| variant.Receive())) && |
| variant.type() == VT_BSTR) { |
| return base::string16(variant.ptr()->bstrVal, |
| SysStringLen(variant.ptr()->bstrVal)); |
| } |
| } |
| return L""; |
| } |
| |
| void AccessibilityTreeFormatterUia::BuildCacheRequests() { |
| // Create cache request for requesting children of a node. |
| uia_->CreateCacheRequest(&children_cache_request_); |
| CHECK(children_cache_request_.Get()); |
| children_cache_request_->put_TreeScope(TreeScope_Children); |
| |
| // Create cache request for requesting information about a node. |
| uia_->CreateCacheRequest(&element_cache_request_); |
| CHECK(element_cache_request_.Get()); |
| element_cache_request_->put_TreeScope(TreeScope_Element); |
| |
| // Caching properties allows us to use GetCachedPropertyValue. |
| // The non-cached version (GetCurrentPropertyValue) may cross |
| // the process boundary for each call. |
| |
| // Cache all properties. |
| for (long i : properties_) { |
| element_cache_request_->AddProperty(i); |
| } |
| // Cache all patterns. |
| for (long i : patterns_) { |
| element_cache_request_->AddPattern(i); |
| } |
| // Cache pattern properties |
| for (long i : pattern_properties_) { |
| element_cache_request_->AddProperty(i); |
| } |
| } |
| |
| base::string16 AccessibilityTreeFormatterUia::ProcessTreeForOutput( |
| const base::DictionaryValue& dict, |
| base::DictionaryValue* filtered_result) { |
| std::unique_ptr<base::DictionaryValue> tree; |
| base::string16 line; |
| |
| // Always show role, and show it first. |
| base::string16 role_value; |
| dict.GetString(UiaIdentifierToCondensedString(UIA_AriaRolePropertyId), |
| &role_value); |
| WriteAttribute(true, base::UTF16ToUTF8(role_value), &line); |
| if (filtered_result) { |
| filtered_result->SetString( |
| UiaIdentifierToStringUTF8(UIA_AriaRolePropertyId), role_value); |
| } |
| |
| // properties |
| for (long i : properties_) { |
| ProcessPropertyForOutput(UiaIdentifierToCondensedString(i), dict, line, |
| filtered_result); |
| } |
| |
| // patterns |
| const std::string pattern_property_names[] = { |
| // UIA_ExpandCollapsePatternId |
| "ExpandCollapse.ExpandCollapseState", |
| // UIA_GridPatternId |
| "Grid.ColumnCount", "Grid.RowCount", |
| // UIA_GridItemPatternId |
| "GridItem.Column", "GridItem.ColumnSpan", "GridItem.Row", |
| "GridItem.RowSpan", "GridItem.ContainingGrid", |
| // UIA_RangeValuePatternId |
| "RangeValue.IsReadOnly", "RangeValue.LargeChange", |
| "RangeValue.SmallChange", "RangeValue.Maximum", "RangeValue.Minimum", |
| "RangeValue.Value", |
| // UIA_ScrollPatternId |
| "Scroll.HorizontalScrollPercent", "Scroll.HorizontalViewSize", |
| "Scroll.HorizontallyScrollable", "Scroll.VerticalScrollPercent", |
| "Scroll.VerticalViewSize", "Scroll.VerticallyScrollable", |
| // UIA_SelectionPatternId |
| "Selection.CanSelectMultiple", "Selection.IsSelectionRequired", |
| // UIA_SelectionItemPatternId |
| "SelectionItem.IsSelected", "SelectionItem.SelectionContainer", |
| // UIA_TablePatternId |
| "Table.RowOrColumnMajor", |
| // UIA_TogglePatternId |
| "Toggle.ToggleState", |
| // UIA_ValuePatternId |
| "Value.IsReadOnly", "Value.Value", |
| // UIA_WindowPatternId |
| "Window.IsModal"}; |
| |
| for (const std::string& pattern_property_name : pattern_property_names) { |
| ProcessPropertyForOutput(pattern_property_name, dict, line, |
| filtered_result); |
| } |
| |
| return line; |
| } |
| |
| void AccessibilityTreeFormatterUia::ProcessPropertyForOutput( |
| const std::string& property_name, |
| const base::DictionaryValue& dict, |
| base::string16& line, |
| base::DictionaryValue* filtered_result) { |
| // |
| const base::Value* value; |
| if (dict.Get(property_name, &value)) |
| ProcessValueForOutput(property_name, value, line, filtered_result); |
| } |
| |
| void AccessibilityTreeFormatterUia::ProcessValueForOutput( |
| const std::string& name, |
| const base::Value* value, |
| base::string16& line, |
| base::DictionaryValue* filtered_result) { |
| switch (value->type()) { |
| case base::Value::Type::STRING: { |
| base::string16 string_value; |
| value->GetAsString(&string_value); |
| bool did_pass_filters = WriteAttribute( |
| false, |
| base::StringPrintf(L"%ls='%ls'", base::UTF8ToUTF16(name).c_str(), |
| string_value.c_str()), |
| &line); |
| if (filtered_result && did_pass_filters) |
| filtered_result->SetString(name, string_value); |
| break; |
| } |
| case base::Value::Type::BOOLEAN: { |
| bool bool_value = 0; |
| value->GetAsBoolean(&bool_value); |
| bool did_pass_filters = WriteAttribute( |
| false, |
| base::StringPrintf(L"%ls=%ls", base::UTF8ToUTF16(name).c_str(), |
| (bool_value ? L"true" : L"false")), |
| &line); |
| if (filtered_result && did_pass_filters) |
| filtered_result->SetBoolean(name, bool_value); |
| break; |
| } |
| case base::Value::Type::INTEGER: { |
| int int_value = 0; |
| value->GetAsInteger(&int_value); |
| bool did_pass_filters = WriteAttribute( |
| false, |
| base::StringPrintf(L"%ls=%d", base::UTF8ToUTF16(name).c_str(), |
| int_value), |
| &line); |
| if (filtered_result && did_pass_filters) |
| filtered_result->SetInteger(name, int_value); |
| break; |
| } |
| case base::Value::Type::DOUBLE: { |
| double double_value = 0.0; |
| value->GetAsDouble(&double_value); |
| bool did_pass_filters = WriteAttribute( |
| false, |
| base::StringPrintf(L"%ls=%.2f", base::UTF8ToUTF16(name).c_str(), |
| double_value), |
| &line); |
| if (filtered_result && did_pass_filters) |
| filtered_result->SetDouble(name, double_value); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| const base::FilePath::StringType |
| AccessibilityTreeFormatterUia::GetExpectedFileSuffix() { |
| return FILE_PATH_LITERAL("-expected-uia-win.txt"); |
| } |
| |
| const base::FilePath::StringType |
| AccessibilityTreeFormatterUia::GetVersionSpecificExpectedFileSuffix() { |
| if (base::win::GetVersion() == base::win::Version::WIN7) { |
| return FILE_PATH_LITERAL("-expected-uia-win7.txt"); |
| } |
| return FILE_PATH_LITERAL(""); |
| } |
| |
| const std::string AccessibilityTreeFormatterUia::GetAllowEmptyString() { |
| return "@UIA-WIN-ALLOW-EMPTY:"; |
| } |
| |
| const std::string AccessibilityTreeFormatterUia::GetAllowString() { |
| return "@UIA-WIN-ALLOW:"; |
| } |
| |
| const std::string AccessibilityTreeFormatterUia::GetDenyString() { |
| return "@UIA-WIN-DENY:"; |
| } |
| |
| const std::string AccessibilityTreeFormatterUia::GetDenyNodeString() { |
| return "@UIA-WIN-DENY-NODE:"; |
| } |
| |
| } // namespace content |