未验证 提交 2e5833b7 编写于 作者: C chunhtai 提交者: GitHub

Apply local patch to chromium accessibility code (#23110)

上级 1758eaa4
......@@ -103,6 +103,12 @@ group("flutter") {
"//flutter/third_party/txt:txt_unittests",
]
# The accessibility library only supports Mac for now.
if (is_mac) {
public_deps +=
[ "//flutter/third_party/accessibility:accessibility_unittests" ]
}
if (is_fuchsia) {
public_deps += [ "//flutter/shell/platform/fuchsia:tests" ]
}
......
......@@ -122,7 +122,7 @@ function verify_licenses() (
local actualLicenseCount
actualLicenseCount="$(tail -n 1 flutter/ci/licenses_golden/licenses_flutter | tr -dc '0-9')"
local expectedLicenseCount=17 # When changing this number: Update the error message below as well describing all expected license types.
local expectedLicenseCount=14 # When changing this number: Update the error message below as well describing all expected license types.
if [[ $actualLicenseCount -ne $expectedLicenseCount ]]; then
echo "=============================== ERROR ==============================="
......
此差异已折叠。
......@@ -150,6 +150,10 @@ def RunCCTests(build_dir, filter):
RunEngineExecutable(build_dir, 'testing_unittests', filter, shuffle_flags)
# The accessibility library only supports Mac for now.
if IsMac():
RunEngineExecutable(build_dir, 'accessibility_unittests', filter, shuffle_flags)
# These unit-tests are Objective-C and can only run on Darwin.
if IsMac():
RunEngineExecutable(build_dir, 'flutter_channels_unittests', filter, shuffle_flags)
......
# Copyright 2014 The Chromium Authors. All rights reserved.
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/features.gni")
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni")
import("//mojo/public/tools/bindings/mojom.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni")
import("//tools/json_schema_compiler/json_schema_api.gni")
import("//ui/base/ui_features.gni")
import("//flutter/common/config.gni")
if (is_android) {
import("//build/config/android/rules.gni")
}
if (is_win) {
import("//build/toolchain/win/midl.gni")
}
# Reset sources_assignment_filter for the BUILD.gn file to prevent
# regression during the migration of Chromium away from the feature.
# See docs/no_sources_assignment_filter.md for more information.
# TODO(crbug.com/1018739): remove this when migration is done.
set_sources_assignment_filter([])
mojom("ax_constants_mojo") {
sources = [ "ax_constants.mojom" ]
}
mojom_component("ax_enums_mojo") {
sources = [ "ax_enums.mojom" ]
macro_prefix = "UI_ACCESSIBILITY_AX_MOJOM"
output_prefix = "ui_accessibility_ax_mojom"
}
# A tiny subset of accessibility code that's allowed to be
# included by Blink. The rule of thumb (for now) is that it's
# anything platform-neutral (no platform/ directory) that
# relates to a single accessibility node (no trees, etc.).
component("ax_base") {
defines = [ "AX_BASE_IMPLEMENTATION" ]
source_set("ax") {
visibility = [ "//flutter/third_party/accessibility/*" ]
include_dirs = [ "//flutter/third_party/accessibility" ]
sources = [
"accessibility_features.cc",
"accessibility_features.h",
"accessibility_switches.cc",
"accessibility_switches.h",
"ax_base_export.h",
"ax_enum_util.cc",
"ax_enum_util.h",
"ax_event.cc",
"ax_event.h",
"ax_event_intent.cc",
"ax_event_intent.h",
"ax_mode.cc",
"ax_mode.h",
"ax_node_data.cc",
"ax_node_data.h",
"ax_node_text_styles.cc",
"ax_node_text_styles.h",
"ax_relative_bounds.cc",
"ax_relative_bounds.h",
"ax_role_properties.cc",
"ax_role_properties.h",
"ax_tree_id.cc",
"ax_tree_id.h",
]
public_deps = [
":ax_constants_mojo",
":ax_enums_mojo",
"//base",
"//base:i18n",
"//base/util/values:values_util",
"//ui/base",
"//ui/gfx",
"//ui/gfx/geometry",
"//ui/strings",
"platform/ax_platform_node.cc",
"platform/ax_platform_node.h",
"platform/ax_platform_node_base.cc",
"platform/ax_platform_node_base.h",
"platform/ax_platform_node_delegate.h",
"platform/ax_platform_node_delegate_base.cc",
"platform/ax_platform_node_delegate_base.h",
"platform/ax_platform_text_boundary.cc",
"platform/ax_platform_text_boundary.h",
"platform/ax_unique_id.cc",
"platform/ax_unique_id.h",
"platform/compute_attributes.cc",
"platform/compute_attributes.h",
]
}
#if (is_win) {
# midl("ichromeaccessible") {
# sources = [
# "platform/ichromeaccessible.idl",
# ]
# }
#}
component("accessibility") {
defines = [ "AX_IMPLEMENTATION" ]
sources = [
sources += [
"ax_action_data.cc",
"ax_action_data.h",
"ax_action_handler.cc",
"ax_action_handler.h",
"ax_action_handler_base.cc",
"ax_action_handler_base.h",
"ax_action_target.h",
"ax_active_popup.cc",
"ax_active_popup.h",
"ax_base_export.h",
"ax_clipping_behavior.h",
"ax_constants.h",
"ax_coordinate_system.h",
"ax_event_bundle_sink.h",
"ax_enum_util.cc",
"ax_enum_util.h",
"ax_enums.h",
"ax_event_generator.cc",
"ax_event_generator.h",
"ax_event_intent.cc",
"ax_event_intent.h",
"ax_export.h",
"ax_language_detection.cc",
"ax_language_detection.h",
"ax_mode.cc",
"ax_mode.h",
"ax_mode_observer.h",
"ax_node.cc",
"ax_node.h",
"ax_node_data.cc",
"ax_node_data.h",
"ax_node_position.cc",
"ax_node_position.h",
"ax_node_text_styles.cc",
"ax_node_text_styles.h",
"ax_offscreen_result.h",
"ax_position.h",
"ax_range.h",
"ax_serializable_tree.cc",
"ax_serializable_tree.h",
"ax_relative_bounds.cc",
"ax_relative_bounds.h",
"ax_role_properties.cc",
"ax_role_properties.h",
"ax_table_info.cc",
"ax_table_info.h",
"ax_text_utils.cc",
"ax_text_utils.h",
"ax_tree.cc",
"ax_tree.h",
"ax_tree_combiner.cc",
"ax_tree_combiner.h",
"ax_tree_data.cc",
"ax_tree_data.h",
"ax_tree_id.cc",
"ax_tree_id.h",
"ax_tree_id_registry.cc",
"ax_tree_id_registry.h",
"ax_tree_manager.h",
......@@ -138,194 +76,19 @@ component("accessibility") {
"ax_tree_manager_map.h",
"ax_tree_observer.cc",
"ax_tree_observer.h",
"ax_tree_serializer.cc",
"ax_tree_serializer.h",
"ax_tree_source.h",
"ax_tree_source_checker.h",
"ax_tree_update.h",
"ax_tree_update_forward.h",
"null_ax_action_target.cc",
"null_ax_action_target.h",
]
deps = [
"//base/util/values:values_util",
"//third_party/cld_3/src/src:cld_3",
]
public_deps = [
":ax_base",
"//ui/accessibility/platform",
]
# Allows the files from //ui/accessibility/platform includes headers
# from this directory.
allow_circular_includes_from = [ "//ui/accessibility/platform" ]
if (!is_ios) {
sources += [
"ax_param_traits.cc",
"ax_param_traits.h",
"ax_param_traits_macros.h",
]
public_deps += [
"//ipc",
"//ui/gfx/ipc/skia",
]
}
if (use_aura) {
sources += [
"aura/aura_window_properties.cc",
"aura/aura_window_properties.h",
]
public_deps += [ "//ui/aura" ]
}
}
source_set("ax_assistant") {
sources = [
"ax_assistant_structure.cc",
"ax_assistant_structure.h",
]
deps = [ ":accessibility" ]
}
static_library("test_support") {
testonly = true
sources = [
"test_ax_node_helper.cc",
"test_ax_node_helper.h",
"test_ax_tree_manager.cc",
"test_ax_tree_manager.h",
"tree_generator.cc",
"tree_generator.h",
]
if (has_native_accessibility) {
if (is_mac) {
sources += [
"platform/test_ax_node_wrapper.cc",
"platform/test_ax_node_wrapper.h",
"platform/ax_platform_node_mac.h",
"platform/ax_platform_node_mac.mm",
]
}
deps = [ ":accessibility" ]
}
test("accessibility_unittests") {
testonly = true
sources = [
"ax_enum_util_unittest.cc",
"ax_event_generator_unittest.cc",
"ax_generated_tree_unittest.cc",
"ax_language_detection_unittest.cc",
"ax_node_data_unittest.cc",
"ax_node_position_unittest.cc",
"ax_range_unittest.cc",
"ax_role_properties_unittest.cc",
"ax_table_info_unittest.cc",
"ax_text_utils_unittest.cc",
"ax_tree_combiner_unittest.cc",
"ax_tree_serializer_unittest.cc",
"ax_tree_source_checker_unittest.cc",
"ax_tree_unittest.cc",
"mojom/ax_action_data_mojom_traits_unittest.cc",
"mojom/ax_event_intent_mojom_traits_unittest.cc",
"mojom/ax_event_mojom_traits_unittest.cc",
"mojom/ax_node_data_mojom_traits_unittest.cc",
"mojom/ax_relative_bounds_mojom_traits_unittest.cc",
"mojom/ax_tree_data_mojom_traits_unittest.cc",
"mojom/ax_tree_id_mojom_traits_unittest.cc",
"mojom/ax_tree_update_mojom_traits_unittest.cc",
"null_ax_action_target_unittest.cc",
"platform/ax_platform_node_unittest.cc",
"platform/ax_platform_node_unittest.h",
"platform/ax_unique_id_unittest.cc",
"run_all_unittests.cc",
]
deps = [
":accessibility",
":test_support",
"//base/test:test_support",
"//ipc",
"//mojo/core/embedder",
"//mojo/core/test:test_support",
"//mojo/public/cpp/test_support:test_utils",
"//skia",
"//testing/gmock",
"//testing/gtest",
"//ui/accessibility/mojom",
"//ui/gfx:test_support",
]
if (has_native_accessibility) {
# This test depends heavily on NativeViewAccessible, which is only
# implemented on these platforms.
sources += [ "platform/ax_platform_node_base_unittest.cc" ]
if (is_win) {
sources += [
"platform/ax_fragment_root_win_unittest.cc",
"platform/ax_platform_node_textchildprovider_win_unittest.cc",
"platform/ax_platform_node_textprovider_win_unittest.cc",
"platform/ax_platform_node_textrangeprovider_win_unittest.cc",
"platform/ax_platform_node_win_unittest.cc",
"platform/ax_platform_node_win_unittest.h",
]
deps += [
"//third_party/iaccessible2",
"//ui/accessibility/platform:ichromeaccessible",
]
libs = [
"oleacc.lib",
"uiautomationcore.lib",
]
}
if (use_atk) {
sources += [
"platform/atk_util_auralinux_unittest.cc",
"platform/ax_platform_node_auralinux_unittest.cc",
]
configs += [ "//build/config/linux/atk" ]
}
}
}
fuzzer_test("ax_tree_fuzzer") {
sources = [ "ax_tree_fuzzer.cc" ]
deps = [ ":accessibility" ]
}
fuzzer_test("ax_table_fuzzer") {
sources = [ "ax_table_fuzzer.cc" ]
deps = [ ":accessibility" ]
seed_corpus = "fuzz_corpus"
}
test("accessibility_perftests") {
testonly = true
sources = [ "ax_node_position_perftest.cc" ]
deps = [
":test_support",
"//base",
"//base/test:test_support",
"//mojo/core/test:run_all_unittests",
"//skia",
"//testing/gmock",
"//testing/gtest",
"//testing/perf",
"//ui/accessibility/mojom",
public_deps = [
"//flutter/third_party/accessibility/ax_build",
"//flutter/third_party/accessibility/base",
"//flutter/third_party/accessibility/gfx",
]
}
dmazzoni@chromium.org
dtseng@chromium.org
aboxhall@chromium.org
nektar@chromium.org
dougt@chromium.org
aleventhal@chromium.org
katie@chromium.org
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
per-file *_param_traits*.*=set noparent
per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS
per-file ax_language_detection*=chrishall@chromium.org
# TEAM: chromium-accessibility@chromium.org
# COMPONENT: Internals>Accessibility
# Copyright 2016 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.
"""Presubmit script for ui/accessibility."""
import os, re, json
AX_MOJOM = 'ui/accessibility/ax_enums.mojom'
AUTOMATION_IDL = 'extensions/common/api/automation.idl'
AX_JS_FILE = 'chrome/browser/resources/accessibility/accessibility.js'
AX_MODE_HEADER = 'ui/accessibility/ax_mode.h'
def InitialLowerCamelCase(unix_name):
words = unix_name.split('_')
return words[0] + ''.join(word.capitalize() for word in words[1:])
def CamelToLowerHacker(str):
out = ''
for i in range(len(str)):
if str[i] >= 'A' and str[i] <= 'Z' and out:
out += '_'
out += str[i]
return out.lower()
# Given a full path to an IDL or MOJOM file containing enum definitions,
# parse the file for enums and return a dict mapping the enum name
# to a list of values for that enum.
def GetEnumsFromFile(fullpath):
enum_name = None
enums = {}
for line in open(fullpath).readlines():
# Strip out comments
line = re.sub('//.*', '', line)
# Look for lines of the form "enum ENUM_NAME {" and get the enum_name
m = re.search('enum ([\w]+) {', line)
if m:
enum_name = m.group(1)
continue
# Look for a "}" character signifying the end of an enum
if line.find('}') >= 0:
enum_name = None
continue
if not enum_name:
continue
# If we're inside an enum definition, add the first string consisting of
# alphanumerics plus underscore ("\w") to the list of values for that enum.
m = re.search('([\w]+)', line)
if m:
enums.setdefault(enum_name, [])
enum_value = m.group(1)
if (enum_value[0] == 'k' and
enum_value[1] == enum_value[1].upper()):
enum_value = CamelToLowerHacker(enum_value[1:])
if enum_value == 'none' or enum_value == 'last':
continue
if enum_value == 'active_descendant_changed':
enum_value = 'activedescendantchanged'
enums[enum_name].append(enum_value)
return enums
def CheckMatchingEnum(ax_enums,
ax_enum_name,
automation_enums,
automation_enum_name,
errs,
output_api,
strict_ordering=False):
if ax_enum_name not in ax_enums:
errs.append(output_api.PresubmitError(
'Expected %s to have an enum named %s' % (AX_MOJOM, ax_enum_name)))
return
if automation_enum_name not in automation_enums:
errs.append(output_api.PresubmitError(
'Expected %s to have an enum named %s' % (
AUTOMATION_IDL, automation_enum_name)))
return
src = ax_enums[ax_enum_name]
dst = automation_enums[automation_enum_name]
if strict_ordering and len(src) != len(dst):
errs.append(output_api.PresubmitError(
'Expected %s to have the same number of items as %s' % (
automation_enum_name, ax_enum_name)))
return
if strict_ordering:
for index, value in enumerate(src):
lower_value = InitialLowerCamelCase(value)
if lower_value != dst[index]:
errs.append(output_api.PresubmitError(
('At index %s in enums, unexpected ordering around %s.%s ' +
'and %s.%s in %s and %s') % (
index, ax_enum_name, lower_value,
automation_enum_name, dst[index],
AX_MOJOM, AUTOMATION_IDL)))
return
return
for value in src:
lower_value = InitialLowerCamelCase(value)
if lower_value in dst:
dst.remove(lower_value) # Any remaining at end are extra and a mismatch.
else:
errs.append(output_api.PresubmitError(
'Found %s.%s in %s, but did not find %s.%s in %s' % (
ax_enum_name, value, AX_MOJOM,
automation_enum_name, InitialLowerCamelCase(value),
AUTOMATION_IDL)))
# Should be no remaining items
for value in dst:
errs.append(output_api.PresubmitError(
'Found %s.%s in %s, but did not find %s.%s in %s' % (
automation_enum_name, value, AUTOMATION_IDL,
ax_enum_name, InitialLowerCamelCase(value),
AX_MOJOM)))
def CheckEnumsMatch(input_api, output_api):
repo_root = input_api.change.RepositoryRoot()
ax_enums = GetEnumsFromFile(os.path.join(repo_root, AX_MOJOM))
automation_enums = GetEnumsFromFile(os.path.join(repo_root, AUTOMATION_IDL))
# Focused state only exists in automation.
automation_enums['StateType'].remove('focused')
# Offscreen state only exists in automation.
automation_enums['StateType'].remove('offscreen')
errs = []
CheckMatchingEnum(ax_enums, 'Role', automation_enums, 'RoleType', errs,
output_api)
CheckMatchingEnum(ax_enums, 'State', automation_enums, 'StateType', errs,
output_api, strict_ordering=True)
CheckMatchingEnum(ax_enums, 'Action', automation_enums, 'ActionType', errs,
output_api, strict_ordering=True)
CheckMatchingEnum(ax_enums, 'Event', automation_enums, 'EventType', errs,
output_api)
CheckMatchingEnum(ax_enums, 'NameFrom', automation_enums, 'NameFromType',
errs, output_api)
CheckMatchingEnum(ax_enums, 'DescriptionFrom', automation_enums,
'DescriptionFromType', errs, output_api)
CheckMatchingEnum(ax_enums, 'Restriction', automation_enums,
'Restriction', errs, output_api)
CheckMatchingEnum(ax_enums, 'DefaultActionVerb', automation_enums,
'DefaultActionVerb', errs, output_api)
CheckMatchingEnum(ax_enums, 'MarkerType', automation_enums,
'MarkerType', errs, output_api)
CheckMatchingEnum(ax_enums, 'Command', automation_enums,
'EventCommandType', errs, output_api)
CheckMatchingEnum(ax_enums, 'TextBoundary', automation_enums,
'EventTextBoundaryType', errs, output_api)
CheckMatchingEnum(ax_enums, 'MoveDirection', automation_enums,
'EventMoveDirectionType', errs, output_api)
CheckMatchingEnum(ax_enums, 'SortDirection', automation_enums,
'SortDirectionType', errs, output_api)
return errs
# Given a full path to c++ header, return an array of the first static
# constexpr defined. (Note there can be more than one defined in a C++
# header)
def GetConstexprFromFile(fullpath):
values = []
for line in open(fullpath).readlines():
# Strip out comments
line = re.sub('//.*', '', line)
# Look for lines of the form "static constexpr <type> NAME "
m = re.search('static constexpr [\w]+ ([\w]+)', line)
if m:
value = m.group(1)
# Skip first/last sentinels
if value == 'kFirstModeFlag' or value == 'kLastModeFlag':
continue
values.append(value)
return values
# Given a full path to js file, return the AXMode consts
# defined
def GetAccessibilityModesFromFile(fullpath):
values = []
inside = False
for line in open(fullpath).readlines():
# Strip out comments
line = re.sub('//.*', '', line)
# Look for the block of code that defines AXMode
m = re.search('const AXMode = {', line)
if m:
inside = True
continue
# Look for a "}" character signifying the end of an enum
if line.find('};') >= 0:
return values
if not inside:
continue
m = re.search('([\w]+):', line)
if m:
values.append(m.group(1))
continue
# getters
m = re.search('get ([\w]+)\(\)', line)
if m:
values.append(m.group(1))
return values
# Make sure that the modes defined in the C++ header match those defined in
# the js file. Note that this doesn't guarantee that the values are the same,
# but does make sure if we add or remove we can signal to the developer that
# they should be aware that this dependency exists.
def CheckModesMatch(input_api, output_api):
errs = []
repo_root = input_api.change.RepositoryRoot()
ax_modes_in_header = GetConstexprFromFile(
os.path.join(repo_root,AX_MODE_HEADER))
ax_modes_in_js = GetAccessibilityModesFromFile(
os.path.join(repo_root, AX_JS_FILE))
for value in ax_modes_in_header:
if value not in ax_modes_in_js:
errs.append(output_api.PresubmitError(
'Found %s in %s, but did not find %s in %s' % (
value, AX_MODE_HEADER, value, AX_JS_FILE)))
return errs
def CheckChangeOnUpload(input_api, output_api):
errs = []
for path in input_api.LocalPaths():
path = path.replace('\\', '/')
if AX_MOJOM == path:
errs.extend(CheckEnumsMatch(input_api, output_api))
if AX_MODE_HEADER == path:
errs.extend(CheckModesMatch(input_api, output_api))
return errs
def CheckChangeOnCommit(input_api, output_api):
errs = []
for path in input_api.LocalPaths():
path = path.replace('\\', '/')
if AX_MOJOM == path:
errs.extend(CheckEnumsMatch(input_api, output_api))
if AX_MODE_HEADER == path:
errs.extend(CheckModesMatch(input_api, output_api))
return errs
// Copyright (c) 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 "ui/accessibility/accessibility_features.h"
#include "base/feature_list.h"
#include "build/build_config.h"
namespace features {
// Allow use of ARIA roles from https://github.com/w3c/annotation-aria draft.
const base::Feature kEnableAccessibilityExposeARIAAnnotations{
"AccessibilityExposeARIAAnnotations", base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAccessibilityExposeARIAAnnotationsEnabled() {
return base::FeatureList::IsEnabled(
::features::kEnableAccessibilityExposeARIAAnnotations);
}
// Enable exposing "display: none" nodes to the browser process AXTree
const base::Feature kEnableAccessibilityExposeDisplayNone{
"AccessibilityExposeDisplayNone", base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAccessibilityExposeDisplayNoneEnabled() {
return base::FeatureList::IsEnabled(
::features::kEnableAccessibilityExposeDisplayNone);
}
// Enable exposing the <html> element to the browser process AXTree
// (as an ignored node).
const base::Feature kEnableAccessibilityExposeHTMLElement{
"AccessibilityExposeHTMLElement", base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAccessibilityExposeHTMLElementEnabled() {
return base::FeatureList::IsEnabled(
::features::kEnableAccessibilityExposeHTMLElement);
}
// Enable language detection to determine language used in page text, exposed
// on the browser process AXTree.
const base::Feature kEnableAccessibilityLanguageDetection{
"AccessibilityLanguageDetection", base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAccessibilityLanguageDetectionEnabled() {
return base::FeatureList::IsEnabled(
::features::kEnableAccessibilityLanguageDetection);
}
// Serializes accessibility information from the Views tree and deserializes it
// into an AXTree in the browser process.
const base::Feature kEnableAccessibilityTreeForViews{
"AccessibilityTreeForViews", base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAccessibilityTreeForViewsEnabled() {
return base::FeatureList::IsEnabled(
::features::kEnableAccessibilityTreeForViews);
}
const base::Feature kAccessibilityFocusHighlight{
"AccessibilityFocusHighlight", base::FEATURE_ENABLED_BY_DEFAULT};
bool IsAccessibilityFocusHighlightEnabled() {
return base::FeatureList::IsEnabled(::features::kAccessibilityFocusHighlight);
}
#if defined(OS_WIN)
const base::Feature kIChromeAccessible{"IChromeAccessible",
base::FEATURE_DISABLED_BY_DEFAULT};
bool IsIChromeAccessibleEnabled() {
return base::FeatureList::IsEnabled(::features::kIChromeAccessible);
}
#endif // defined(OS_WIN)
#if defined(OS_CHROMEOS)
const base::Feature kAccessibilityCursorColor{"AccessibilityCursorColor",
base::FEATURE_ENABLED_BY_DEFAULT};
bool IsAccessibilityCursorColorEnabled() {
return base::FeatureList::IsEnabled(::features::kAccessibilityCursorColor);
}
#endif // defined(OS_CHROMEOS)
const base::Feature kAugmentExistingImageLabels{
"AugmentExistingImageLabels", base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAugmentExistingImageLabelsEnabled() {
return base::FeatureList::IsEnabled(::features::kAugmentExistingImageLabels);
}
} // namespace features
// Copyright (c) 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.
// Define all the base::Features used by ui/accessibility.
#ifndef UI_ACCESSIBILITY_ACCESSIBILITY_FEATURES_H_
#define UI_ACCESSIBILITY_ACCESSIBILITY_FEATURES_H_
#include "base/feature_list.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_base_export.h"
namespace features {
AX_BASE_EXPORT extern const base::Feature
kEnableAccessibilityExposeARIAAnnotations;
// Returns true if ARIA annotations should be exposed to the browser AX Tree.
AX_BASE_EXPORT bool IsAccessibilityExposeARIAAnnotationsEnabled();
AX_BASE_EXPORT extern const base::Feature kEnableAccessibilityExposeDisplayNone;
// Returns true if "display: none" nodes should be exposed to the
// browser process AXTree.
AX_BASE_EXPORT bool IsAccessibilityExposeDisplayNoneEnabled();
AX_BASE_EXPORT extern const base::Feature kEnableAccessibilityExposeHTMLElement;
// Returns true if the <html> element should be exposed to the
// browser process AXTree (as an ignored node).
AX_BASE_EXPORT bool IsAccessibilityExposeHTMLElementEnabled();
AX_BASE_EXPORT extern const base::Feature kEnableAccessibilityLanguageDetection;
// Return true if language detection should be used to determine the language
// of text content in page and exposed to the browser process AXTree.
AX_BASE_EXPORT bool IsAccessibilityLanguageDetectionEnabled();
// Serializes accessibility information from the Views tree and deserializes it
// into an AXTree in the browser process.
AX_BASE_EXPORT extern const base::Feature kEnableAccessibilityTreeForViews;
// Returns true if the Views tree is exposed using an AXTree in the browser
// process. Returns false if the Views tree is exposed to accessibility
// directly.
AX_BASE_EXPORT bool IsAccessibilityTreeForViewsEnabled();
AX_BASE_EXPORT extern const base::Feature kAccessibilityFocusHighlight;
// Returns true if the accessibility focus highlight feature is enabled,
// which draws a visual highlight around the focused element on the page
// briefly whenever focus changes.
AX_BASE_EXPORT bool IsAccessibilityFocusHighlightEnabled();
#if defined(OS_WIN)
// Enables an experimental Chrome-specific accessibility COM API
AX_BASE_EXPORT extern const base::Feature kIChromeAccessible;
// Returns true if the IChromeAccessible COM API is enabled.
AX_BASE_EXPORT bool IsIChromeAccessibleEnabled();
#endif // defined(OS_WIN)
#if defined(OS_CHROMEOS)
AX_BASE_EXPORT extern const base::Feature kAccessibilityCursorColor;
// Returns true if the accessibility cursor color feature is enabled, letting
// users pick a custom cursor color.
AX_BASE_EXPORT bool IsAccessibilityCursorColorEnabled();
#endif // defined(OS_CHROMEOS)
// Enables Get Image Descriptions to augment existing images labels,
// rather than only provide descriptions for completely unlabeled images.
AX_BASE_EXPORT extern const base::Feature kAugmentExistingImageLabels;
// Returns true if augmenting existing image labels is enabled.
AX_BASE_EXPORT bool IsAugmentExistingImageLabelsEnabled();
} // namespace features
#endif // UI_ACCESSIBILITY_ACCESSIBILITY_FEATURES_H_
// Copyright (c) 2018 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 "ui/accessibility/accessibility_switches.h"
#include "base/command_line.h"
#include "build/build_config.h"
namespace switches {
// Shows additional automatic click features that haven't launched yet.
const char kEnableExperimentalAccessibilityAutoclick[] =
"enable-experimental-accessibility-autoclick";
// Enables support for visually debugging the accessibility labels
// feature, which provides images descriptions for screen reader users.
const char kEnableExperimentalAccessibilityLabelsDebugging[] =
"enable-experimental-accessibility-labels-debugging";
// Enables language detection on in-page text content which is then exposed to
// assistive technology such as screen readers.
const char kEnableExperimentalAccessibilityLanguageDetection[] =
"enable-experimental-accessibility-language-detection";
// Enables language detection for dynamic content which is then exposed to
// assistive technology such as screen readers.
const char kEnableExperimentalAccessibilityLanguageDetectionDynamic[] =
"enable-experimental-accessibility-language-detection-dynamic";
// Enables in progress Switch Access features for text input.
const char kEnableExperimentalAccessibilitySwitchAccessText[] =
"enable-experimental-accessibility-switch-access-text";
// Enables annotations feature that hasn't launched yet.
const char kEnableExperimentalAccessibilityChromeVoxAnnotations[] =
"enable-experimental-accessibility-chromevox-annotations";
// Disables ChromeVox language switching feature.
const char kDisableExperimentalAccessibilityChromeVoxLanguageSwitching[] =
"disable-experimental-accessibility-chromevox-language-switching";
// Disables ChromeVox search menus feature.
const char kDisableExperimentalAccessibilityChromeVoxSearchMenus[] =
"disable-experimental-accessibility-chromevox-search-menus";
// Enables interactive tutorial for ChromeVox.
const char kEnableExperimentalAccessibilityChromeVoxTutorial[] =
"enable-experimental-accessibility-chromevox-tutorial";
// Enables Switch Access point scanning. This feature hasn't launched yet.
const char kEnableSwitchAccessPointScanning[] =
"enable-switch-access-point-scanning";
bool IsExperimentalAccessibilityLanguageDetectionEnabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetection);
}
bool IsExperimentalAccessibilityLanguageDetectionDynamicEnabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
}
bool IsExperimentalAccessibilitySwitchAccessTextEnabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kEnableExperimentalAccessibilitySwitchAccessText);
}
bool IsSwitchAccessPointScanningEnabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kEnableSwitchAccessPointScanning);
}
#if defined(OS_WIN)
// Enables UI Automation platform API in addition to the IAccessible API.
const char kEnableExperimentalUIAutomation[] =
"enable-experimental-ui-automation";
#endif
bool IsExperimentalAccessibilityPlatformUIAEnabled() {
#if defined(OS_WIN)
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kEnableExperimentalUIAutomation);
#else
return false;
#endif
}
// Optionally disable AXMenuList, which makes the internal pop-up menu
// UI for a select element directly accessible.
const char kDisableAXMenuList[] = "disable-ax-menu-list";
} // namespace switches
// Copyright (c) 2018 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.
// Define all the command-line switches used by ui/accessibility.
#ifndef UI_ACCESSIBILITY_ACCESSIBILITY_SWITCHES_H_
#define UI_ACCESSIBILITY_ACCESSIBILITY_SWITCHES_H_
#include "build/build_config.h"
#include "ui/accessibility/ax_base_export.h"
namespace switches {
AX_BASE_EXPORT extern const char kEnableExperimentalAccessibilityAutoclick[];
AX_BASE_EXPORT extern const char
kEnableExperimentalAccessibilityLabelsDebugging[];
AX_BASE_EXPORT extern const char
kEnableExperimentalAccessibilityLanguageDetection[];
AX_BASE_EXPORT extern const char
kEnableExperimentalAccessibilityLanguageDetectionDynamic[];
AX_BASE_EXPORT extern const char
kEnableExperimentalAccessibilitySwitchAccessText[];
AX_BASE_EXPORT extern const char
kEnableExperimentalAccessibilityChromeVoxAnnotations[];
AX_BASE_EXPORT extern const char
kDisableExperimentalAccessibilityChromeVoxLanguageSwitching[];
AX_BASE_EXPORT extern const char
kDisableExperimentalAccessibilityChromeVoxSearchMenus[];
AX_BASE_EXPORT extern const char
kEnableExperimentalAccessibilityChromeVoxTutorial[];
AX_BASE_EXPORT extern const char kEnableSwitchAccessPointScanning[];
// Returns true if experimental accessibility language detection is enabled.
AX_BASE_EXPORT bool IsExperimentalAccessibilityLanguageDetectionEnabled();
// Returns true if experimental accessibility language detection support for
// dynamic content is enabled.
AX_BASE_EXPORT bool
IsExperimentalAccessibilityLanguageDetectionDynamicEnabled();
// Returns true if experimental accessibility Switch Access text is enabled.
AX_BASE_EXPORT bool IsExperimentalAccessibilitySwitchAccessTextEnabled();
#if defined(OS_WIN)
AX_BASE_EXPORT extern const char kEnableExperimentalUIAutomation[];
#endif
// Returns true if experimental support for UIAutomation is enabled.
AX_BASE_EXPORT bool IsExperimentalAccessibilityPlatformUIAEnabled();
// Returns true if Switch Access point scanning is enabled.
AX_BASE_EXPORT bool IsSwitchAccessPointScanningEnabled();
// Optionally disable AXMenuList, which makes the internal pop-up menu
// UI for a select element directly accessible.
AX_BASE_EXPORT extern const char kDisableAXMenuList[];
} // namespace switches
#endif // UI_ACCESSIBILITY_ACCESSIBILITY_SWITCHES_H_
// Copyright 2017 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 "ui/accessibility/aura/aura_window_properties.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/base/class_property.h"
DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AX_EXPORT, ax::mojom::Role)
namespace ui {
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kChildAXTreeID, nullptr)
DEFINE_UI_CLASS_PROPERTY_KEY(ax::mojom::Role,
kAXRoleOverride,
ax::mojom::Role::kNone)
} // namespace ui
// Copyright 2017 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.
#ifndef UI_ACCESSIBILITY_AURA_AURA_WINDOW_PROPERTIES_H_
#define UI_ACCESSIBILITY_AURA_AURA_WINDOW_PROPERTIES_H_
#include <string>
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/accessibility/ax_export.h"
#include "ui/aura/window.h"
namespace ui {
// Value is a serialized |ui::AXTreeID| because code in //ui/aura/mus needs
// to serialize the window property, but //ui/aura cannot depend on
// //ui/accessibility and hence cannot know about the type ui::AXTreeID.
// TODO(dmazzoni): Convert from string to base::UnguessableToken.
AX_EXPORT extern const aura::WindowProperty<std::string*>* const kChildAXTreeID;
AX_EXPORT extern const aura::WindowProperty<ax::mojom::Role>* const
kAXRoleOverride;
} // namespace ui
#endif // UI_ACCESSIBILITY_AURA_AURA_WINDOW_PROPERTIES_H_
......@@ -2,14 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_action_data.h"
#include "ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ax_enums.h"
namespace ui {
// Mojo enums are initialized here so the header can include the much smaller
// mojom-forward.h header.
AXActionData::AXActionData()
: action(ax::mojom::Action::kNone),
hit_test_event_to_fire(ax::mojom::Event::kNone),
......
......@@ -5,10 +5,11 @@
#ifndef UI_ACCESSIBILITY_AX_ACTION_DATA_H_
#define UI_ACCESSIBILITY_AX_ACTION_DATA_H_
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/gfx/geometry/rect.h"
#include "ax_enums.h"
#include "ax_export.h"
#include "ax_tree_id.h"
#include "gfx/geometry/point.h"
#include "gfx/geometry/rect.h"
namespace ui {
......
......@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_action_handler.h"
#include "ax_action_handler.h"
#include "ui/accessibility/ax_tree_id_registry.h"
#include "ax_tree_id_registry.h"
namespace ui {
AXActionHandler::AXActionHandler()
: AXActionHandlerBase(
AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(this)) {}
AXTreeIDRegistry::GetInstance().GetOrCreateAXTreeID(this)) {}
} // namespace ui
......@@ -5,8 +5,8 @@
#ifndef UI_ACCESSIBILITY_AX_ACTION_HANDLER_H_
#define UI_ACCESSIBILITY_AX_ACTION_HANDLER_H_
#include "ui/accessibility/ax_action_handler_base.h"
#include "ui/accessibility/ax_export.h"
#include "ax_action_handler_base.h"
#include "ax_export.h"
namespace ui {
......
......@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_action_handler_base.h"
#include "ax_action_handler_base.h"
#include "ui/accessibility/ax_tree_id_registry.h"
#include "ax_tree_id_registry.h"
#include "base/logging.h"
namespace ui {
......@@ -19,14 +20,14 @@ AXActionHandlerBase::AXActionHandlerBase(const AXTreeID& ax_tree_id)
: tree_id_(ax_tree_id) {}
AXActionHandlerBase::~AXActionHandlerBase() {
AXTreeIDRegistry::GetInstance()->RemoveAXTreeID(tree_id_);
AXTreeIDRegistry::GetInstance().RemoveAXTreeID(tree_id_);
}
void AXActionHandlerBase::SetAXTreeID(AXTreeID new_ax_tree_id) {
DCHECK_NE(new_ax_tree_id, ui::AXTreeIDUnknown());
AXTreeIDRegistry::GetInstance()->RemoveAXTreeID(tree_id_);
BASE_DCHECK(new_ax_tree_id != ui::AXTreeIDUnknown());
AXTreeIDRegistry::GetInstance().RemoveAXTreeID(tree_id_);
tree_id_ = new_ax_tree_id;
AXTreeIDRegistry::GetInstance()->SetAXTreeID(tree_id_, this);
AXTreeIDRegistry::GetInstance().SetAXTreeID(tree_id_, this);
}
} // namespace ui
......@@ -5,8 +5,8 @@
#ifndef UI_ACCESSIBILITY_AX_ACTION_HANDLER_BASE_H_
#define UI_ACCESSIBILITY_AX_ACTION_HANDLER_BASE_H_
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ax_export.h"
#include "ax_tree_id.h"
namespace ui {
......
// 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.
#ifndef UI_ACCESSIBILITY_AX_ACTION_TARGET_H_
#define UI_ACCESSIBILITY_AX_ACTION_TARGET_H_
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
namespace ui {
// AXActionTarget is an abstract interface that can be used to carry out
// accessibility actions on nodes from an AXTreeSource without knowing the
// concrete class of that AXTreeSource.
class AXActionTarget {
public:
virtual ~AXActionTarget() = default;
enum class Type { kNull, kBlink, kPdf };
virtual Type GetType() const = 0;
virtual bool ClearAccessibilityFocus() const = 0;
virtual bool Click() const = 0;
virtual bool Decrement() const = 0;
virtual bool Increment() const = 0;
virtual bool Focus() const = 0;
virtual gfx::Rect GetRelativeBounds() const = 0;
virtual gfx::Point GetScrollOffset() const = 0;
virtual gfx::Point MinimumScrollOffset() const = 0;
virtual gfx::Point MaximumScrollOffset() const = 0;
virtual bool SetAccessibilityFocus() const = 0;
virtual void SetScrollOffset(const gfx::Point& point) const = 0;
virtual bool SetSelected(bool selected) const = 0;
virtual bool SetSelection(const AXActionTarget* anchor_object,
int anchor_offset,
const AXActionTarget* focus_object,
int focus_offset) const = 0;
virtual bool SetSequentialFocusNavigationStartingPoint() const = 0;
virtual bool SetValue(const std::string& value) const = 0;
virtual bool ShowContextMenu() const = 0;
// Make this object visible by scrolling as many nested scrollable views as
// needed.
virtual bool ScrollToMakeVisible() const = 0;
// Same, but if the whole object can't be made visible, try for this subrect,
// in local coordinates.
virtual bool ScrollToMakeVisibleWithSubFocus(
const gfx::Rect& rect,
ax::mojom::ScrollAlignment horizontal_scroll_alignment,
ax::mojom::ScrollAlignment vertical_scroll_alignment,
ax::mojom::ScrollBehavior scroll_behavior) const = 0;
// Scroll this object to a given point in global coordinates of the top-level
// window.
virtual bool ScrollToGlobalPoint(const gfx::Point& point) const = 0;
protected:
AXActionTarget() = default;
};
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_ACTION_TARGET_H_
// 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 "ui/accessibility/ax_active_popup.h"
namespace ui {
// Represents a global storage for the view accessibility for an
// autofill popup. It is a singleton wrapper around the ax unique id of the
// autofill popup. This singleton is used for communicating the live status of
// the autofill popup between web contents and views.
// The assumption here is that only one autofill popup can exist at a time.
static base::NoDestructor<base::Optional<int32_t>> g_active_popup_ax_unique_id;
base::Optional<int32_t> GetActivePopupAxUniqueId() {
return *g_active_popup_ax_unique_id;
}
void SetActivePopupAxUniqueId(base::Optional<int32_t> ax_unique_id) {
// When an instance of autofill popup hides, the caller of popup hide should
// make sure g_active_popup_ax_unique_id is cleared. The assumption is that
// there can only be one active autofill popup existing at a time. If on
// popup showing, we encounter g_active_popup_ax_unique_id is already set,
// this would indicate two autofill popups are showing at the same time or
// previous on popup hide call did not clear the variable, so we should fail
// DCHECK here.
DCHECK(!GetActivePopupAxUniqueId());
*g_active_popup_ax_unique_id = ax_unique_id;
}
void ClearActivePopupAxUniqueId() {
*g_active_popup_ax_unique_id = base::nullopt;
}
} // namespace ui
// 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.
#ifndef UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_
#define UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/optional.h"
#include "ui/accessibility/ax_export.h"
namespace ui {
AX_EXPORT base::Optional<int32_t> GetActivePopupAxUniqueId();
AX_EXPORT void SetActivePopupAxUniqueId(base::Optional<int32_t> ax_unique_id);
AX_EXPORT void ClearActivePopupAxUniqueId();
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_
// Copyright 2018 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 "ui/accessibility/ax_assistant_structure.h"
#include <string>
#include "base/logging.h"
#include "base/optional.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/accessibility/ax_serializable_tree.h"
#include "ui/accessibility/platform/ax_android_constants.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/transform.h"
namespace ui {
namespace {
bool HasFocusableChild(const AXNode* node) {
for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
if (child->data().HasState(ax::mojom::State::kFocusable) ||
HasFocusableChild(child)) {
return true;
}
}
return false;
}
bool HasOnlyTextChildren(const AXNode* node) {
for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
if (!child->IsText())
return false;
}
return true;
}
// TODO(muyuanli): share with BrowserAccessibility.
bool IsSimpleTextControl(const AXNode* node, uint32_t state) {
return (node->data().role == ax::mojom::Role::kTextField ||
node->data().role == ax::mojom::Role::kTextFieldWithComboBox ||
node->data().role == ax::mojom::Role::kSearchBox ||
node->data().HasBoolAttribute(
ax::mojom::BoolAttribute::kEditableRoot)) &&
!node->data().HasState(ax::mojom::State::kRichlyEditable);
}
bool IsRichTextEditable(const AXNode* node) {
const AXNode* parent = node->GetUnignoredParent();
return node->data().HasState(ax::mojom::State::kRichlyEditable) &&
(!parent ||
!parent->data().HasState(ax::mojom::State::kRichlyEditable));
}
bool IsNativeTextControl(const AXNode* node) {
const std::string& html_tag =
node->data().GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag);
if (html_tag == "input") {
std::string input_type;
if (!node->data().GetHtmlAttribute("type", &input_type))
return true;
return input_type.empty() || input_type == "email" ||
input_type == "password" || input_type == "search" ||
input_type == "tel" || input_type == "text" || input_type == "url" ||
input_type == "number";
}
return html_tag == "textarea";
}
bool IsLeaf(const AXNode* node) {
if (node->children().empty())
return true;
if (IsNativeTextControl(node) || node->IsText()) {
return true;
}
switch (node->data().role) {
case ax::mojom::Role::kImage:
case ax::mojom::Role::kMeter:
case ax::mojom::Role::kScrollBar:
case ax::mojom::Role::kSlider:
case ax::mojom::Role::kSplitter:
case ax::mojom::Role::kProgressIndicator:
case ax::mojom::Role::kDate:
case ax::mojom::Role::kDateTime:
case ax::mojom::Role::kInputTime:
return true;
default:
return false;
}
}
base::string16 GetInnerText(const AXNode* node) {
if (node->IsText()) {
return node->data().GetString16Attribute(ax::mojom::StringAttribute::kName);
}
base::string16 text;
for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
text += GetInnerText(child);
}
return text;
}
base::string16 GetValue(const AXNode* node, bool show_password) {
base::string16 value =
node->data().GetString16Attribute(ax::mojom::StringAttribute::kValue);
if (value.empty() &&
(IsSimpleTextControl(node, node->data().state) ||
IsRichTextEditable(node)) &&
!IsNativeTextControl(node)) {
value = GetInnerText(node);
}
if (node->data().HasState(ax::mojom::State::kProtected)) {
if (!show_password) {
value = base::string16(value.size(), kSecurePasswordBullet);
}
}
return value;
}
bool HasOnlyTextAndImageChildren(const AXNode* node) {
for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
if (!child->IsText() && !ui::IsImage(child->data().role)) {
return false;
}
}
return true;
}
bool IsFocusable(const AXNode* node) {
if (node->data().role == ax::mojom::Role::kIframe ||
node->data().role == ax::mojom::Role::kIframePresentational ||
(node->data().role == ax::mojom::Role::kRootWebArea &&
node->GetUnignoredParent())) {
return node->data().HasStringAttribute(ax::mojom::StringAttribute::kName);
}
return node->data().HasState(ax::mojom::State::kFocusable);
}
base::string16 GetText(const AXNode* node, bool show_password) {
if (node->data().role == ax::mojom::Role::kWebArea ||
node->data().role == ax::mojom::Role::kIframe ||
node->data().role == ax::mojom::Role::kIframePresentational) {
return base::string16();
}
ax::mojom::NameFrom name_from = static_cast<ax::mojom::NameFrom>(
node->data().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom));
if (ui::IsListItem(node->data().role) &&
name_from == ax::mojom::NameFrom::kContents) {
if (!node->children().empty() && !HasOnlyTextChildren(node))
return base::string16();
}
base::string16 value = GetValue(node, show_password);
if (!value.empty()) {
if (node->data().HasState(ax::mojom::State::kEditable))
return value;
switch (node->data().role) {
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kTextFieldWithComboBox:
case ax::mojom::Role::kPopUpButton:
case ax::mojom::Role::kTextField:
return value;
default:
break;
}
}
if (node->data().role == ax::mojom::Role::kColorWell) {
unsigned int color = static_cast<unsigned int>(
node->data().GetIntAttribute(ax::mojom::IntAttribute::kColorValue));
unsigned int red = color >> 16 & 0xFF;
unsigned int green = color >> 8 & 0xFF;
unsigned int blue = color >> 0 & 0xFF;
return base::UTF8ToUTF16(
base::StringPrintf("#%02X%02X%02X", red, green, blue));
}
base::string16 text =
node->data().GetString16Attribute(ax::mojom::StringAttribute::kName);
base::string16 description = node->data().GetString16Attribute(
ax::mojom::StringAttribute::kDescription);
if (!description.empty()) {
if (!text.empty())
text += base::ASCIIToUTF16(" ");
text += description;
}
if (text.empty())
text = value;
if (node->data().role == ax::mojom::Role::kRootWebArea)
return text;
if (text.empty() &&
(HasOnlyTextChildren(node) ||
(IsFocusable(node) && HasOnlyTextAndImageChildren(node)))) {
for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
text += GetText(child, show_password);
}
}
if (text.empty() && (ui::IsLink(node->data().role) ||
node->data().role == ax::mojom::Role::kImage)) {
base::string16 url =
node->data().GetString16Attribute(ax::mojom::StringAttribute::kUrl);
text = AXUrlBaseText(url);
}
return text;
}
// Get string representation of ax::mojom::Role. We are not using ToString() in
// ax_enums.h since the names are subject to change in the future and
// we are only interested in a subset of the roles.
base::Optional<std::string> AXRoleToString(ax::mojom::Role role) {
switch (role) {
case ax::mojom::Role::kArticle:
return base::Optional<std::string>("article");
case ax::mojom::Role::kBanner:
return base::Optional<std::string>("banner");
case ax::mojom::Role::kCaption:
return base::Optional<std::string>("caption");
case ax::mojom::Role::kComplementary:
return base::Optional<std::string>("complementary");
case ax::mojom::Role::kDate:
return base::Optional<std::string>("date");
case ax::mojom::Role::kDateTime:
return base::Optional<std::string>("date_time");
case ax::mojom::Role::kDefinition:
return base::Optional<std::string>("definition");
case ax::mojom::Role::kDetails:
return base::Optional<std::string>("details");
case ax::mojom::Role::kDocument:
return base::Optional<std::string>("document");
case ax::mojom::Role::kFeed:
return base::Optional<std::string>("feed");
case ax::mojom::Role::kHeading:
return base::Optional<std::string>("heading");
case ax::mojom::Role::kIframe:
return base::Optional<std::string>("iframe");
case ax::mojom::Role::kIframePresentational:
return base::Optional<std::string>("iframe_presentational");
case ax::mojom::Role::kList:
return base::Optional<std::string>("list");
case ax::mojom::Role::kListItem:
return base::Optional<std::string>("list_item");
case ax::mojom::Role::kMain:
return base::Optional<std::string>("main");
case ax::mojom::Role::kParagraph:
return base::Optional<std::string>("paragraph");
default:
return base::Optional<std::string>();
}
}
AssistantNode* AddChild(AssistantTree* tree) {
auto node = std::make_unique<AssistantNode>();
tree->nodes.push_back(std::move(node));
return tree->nodes.back().get();
}
struct WalkAXTreeConfig {
bool should_select_leaf;
const bool show_password;
};
void WalkAXTreeDepthFirst(const AXNode* node,
const gfx::Rect& rect,
const AXTreeUpdate& update,
const AXTree* tree,
WalkAXTreeConfig* config,
AssistantTree* assistant_tree,
AssistantNode* result) {
result->text = GetText(node, config->show_password);
result->class_name =
AXRoleToAndroidClassName(node->data().role, node->GetUnignoredParent());
result->role = AXRoleToString(node->data().role);
result->text_size = -1.0;
result->bgcolor = 0;
result->color = 0;
result->bold = 0;
result->italic = 0;
result->line_through = 0;
result->underline = 0;
if (node->data().HasFloatAttribute(ax::mojom::FloatAttribute::kFontSize)) {
gfx::RectF text_size_rect(
0, 0, 1,
node->data().GetFloatAttribute(ax::mojom::FloatAttribute::kFontSize));
gfx::Rect scaled_text_size_rect =
gfx::ToEnclosingRect(tree->RelativeToTreeBounds(node, text_size_rect));
result->text_size = scaled_text_size_rect.height();
result->color =
node->data().GetIntAttribute(ax::mojom::IntAttribute::kColor);
result->bgcolor =
node->data().GetIntAttribute(ax::mojom::IntAttribute::kBackgroundColor);
result->bold = node->data().HasTextStyle(ax::mojom::TextStyle::kBold);
result->italic = node->data().HasTextStyle(ax::mojom::TextStyle::kItalic);
result->line_through =
node->data().HasTextStyle(ax::mojom::TextStyle::kLineThrough);
result->underline =
node->data().HasTextStyle(ax::mojom::TextStyle::kUnderline);
}
const gfx::Rect& absolute_rect =
gfx::ToEnclosingRect(tree->GetTreeBounds(node));
gfx::Rect parent_relative_rect = absolute_rect;
bool is_root = !node->GetUnignoredParent();
if (!is_root) {
parent_relative_rect.Offset(-rect.OffsetFromOrigin());
}
result->rect = gfx::Rect(parent_relative_rect.x(), parent_relative_rect.y(),
absolute_rect.width(), absolute_rect.height());
if (IsLeaf(node) && update.has_tree_data) {
int start_selection = 0;
int end_selection = 0;
AXTree::Selection unignored_selection = tree->GetUnignoredSelection();
if (unignored_selection.anchor_object_id == node->id()) {
start_selection = unignored_selection.anchor_offset;
config->should_select_leaf = true;
}
if (config->should_select_leaf) {
end_selection =
static_cast<int32_t>(GetText(node, config->show_password).length());
}
if (unignored_selection.focus_object_id == node->id()) {
end_selection = unignored_selection.focus_offset;
config->should_select_leaf = false;
}
if (end_selection > 0)
result->selection =
base::make_optional<gfx::Range>(start_selection, end_selection);
}
for (size_t i = 0; i < node->GetUnignoredChildCount(); ++i) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
auto* n = AddChild(assistant_tree);
result->children_indices.push_back(assistant_tree->nodes.size() - 1);
WalkAXTreeDepthFirst(child, absolute_rect, update, tree, config,
assistant_tree, n);
}
}
} // namespace
AssistantNode::AssistantNode() = default;
AssistantNode::AssistantNode(const AssistantNode& other) = default;
AssistantNode::~AssistantNode() = default;
AssistantTree::AssistantTree() = default;
AssistantTree::~AssistantTree() = default;
AssistantTree::AssistantTree(const AssistantTree& other) {
for (const auto& node : other.nodes)
nodes.emplace_back(std::make_unique<AssistantNode>(*node));
}
std::unique_ptr<AssistantTree> CreateAssistantTree(const AXTreeUpdate& update,
bool show_password) {
auto tree = std::make_unique<AXSerializableTree>();
auto assistant_tree = std::make_unique<AssistantTree>();
auto* root = AddChild(assistant_tree.get());
if (!tree->Unserialize(update))
LOG(FATAL) << tree->error();
WalkAXTreeConfig config{
false, // should_select_leaf
show_password // show_password
};
WalkAXTreeDepthFirst(tree->root(), gfx::Rect(), update, tree.get(), &config,
assistant_tree.get(), root);
return assistant_tree;
}
base::string16 AXUrlBaseText(base::string16 url) {
// Given a url like http://foo.com/bar/baz.png, just return the
// base text, e.g., "baz".
int trailing_slashes = 0;
while (url.size() - trailing_slashes > 0 &&
url[url.size() - trailing_slashes - 1] == '/') {
trailing_slashes++;
}
if (trailing_slashes)
url = url.substr(0, url.size() - trailing_slashes);
size_t slash_index = url.rfind('/');
if (slash_index != std::string::npos)
url = url.substr(slash_index + 1);
size_t dot_index = url.rfind('.');
if (dot_index != std::string::npos)
url = url.substr(0, dot_index);
return url;
}
const char* AXRoleToAndroidClassName(ax::mojom::Role role, bool has_parent) {
switch (role) {
case ax::mojom::Role::kSearchBox:
case ax::mojom::Role::kSpinButton:
case ax::mojom::Role::kTextField:
case ax::mojom::Role::kTextFieldWithComboBox:
return kAXEditTextClassname;
case ax::mojom::Role::kSlider:
return kAXSeekBarClassname;
case ax::mojom::Role::kColorWell:
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kDate:
case ax::mojom::Role::kPopUpButton:
case ax::mojom::Role::kInputTime:
return kAXSpinnerClassname;
case ax::mojom::Role::kButton:
case ax::mojom::Role::kPdfActionableHighlight:
return kAXButtonClassname;
case ax::mojom::Role::kCheckBox:
case ax::mojom::Role::kSwitch:
return kAXCheckBoxClassname;
case ax::mojom::Role::kRadioButton:
return kAXRadioButtonClassname;
case ax::mojom::Role::kToggleButton:
return kAXToggleButtonClassname;
case ax::mojom::Role::kCanvas:
case ax::mojom::Role::kImage:
case ax::mojom::Role::kSvgRoot:
return kAXImageClassname;
case ax::mojom::Role::kMeter:
case ax::mojom::Role::kProgressIndicator:
return kAXProgressBarClassname;
case ax::mojom::Role::kTabList:
return kAXTabWidgetClassname;
case ax::mojom::Role::kGrid:
case ax::mojom::Role::kTreeGrid:
case ax::mojom::Role::kTable:
return kAXGridViewClassname;
case ax::mojom::Role::kList:
case ax::mojom::Role::kListBox:
case ax::mojom::Role::kDescriptionList:
return kAXListViewClassname;
case ax::mojom::Role::kDialog:
return kAXDialogClassname;
case ax::mojom::Role::kRootWebArea:
return has_parent ? kAXViewClassname : kAXWebViewClassname;
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
return kAXMenuItemClassname;
case ax::mojom::Role::kStaticText:
return kAXTextViewClassname;
default:
return kAXViewClassname;
}
}
} // namespace ui
// Copyright 2018 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.
#ifndef UI_ACCESSIBILITY_AX_ASSISTANT_STRUCTURE_H_
#define UI_ACCESSIBILITY_AX_ASSISTANT_STRUCTURE_H_
#include <cstdint>
#include <vector>
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/string16.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree_update.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/range/range.h"
namespace ui {
struct AssistantNode {
AssistantNode();
AssistantNode(const AssistantNode& other);
AssistantNode& operator=(const AssistantNode&) = delete;
~AssistantNode();
std::vector<int32_t> children_indices;
// Geometry of the view in pixels
gfx::Rect rect;
// Text of the view.
base::string16 text;
// Text properties
float text_size;
uint32_t color;
uint32_t bgcolor;
bool bold;
bool italic;
bool underline;
bool line_through;
// Selected portion of the text.
base::Optional<gfx::Range> selection;
// Fake Android view class name of the element. Each node is assigned
// a closest approximation of Android's views to keep the server happy.
std::string class_name;
// Accessibility functionality of the node inferred from DOM or based on HTML
// role attribute.
base::Optional<std::string> role;
};
struct AssistantTree {
AssistantTree();
AssistantTree(const AssistantTree& other);
AssistantTree& operator=(const AssistantTree&) = delete;
~AssistantTree();
std::vector<std::unique_ptr<AssistantNode>> nodes;
};
std::unique_ptr<AssistantTree> CreateAssistantTree(const AXTreeUpdate& update,
bool show_password);
base::string16 AXUrlBaseText(base::string16 url);
const char* AXRoleToAndroidClassName(ax::mojom::Role role, bool has_parent);
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_ASSISTANT_STRUCTURE_H_
......@@ -2,7 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module ax.mojom;
#include <stdint.h>
namespace ax {
namespace mojom {
// https://www.w3.org/TR/wai-aria-1.1/#aria-rowcount
// https://www.w3.org/TR/wai-aria-1.1/#aria-colcount
......@@ -10,4 +14,8 @@ module ax.mojom;
// value of aria-(rowcount|colcount) to -1 to indicate that the value should not
// be calculated by the user agent.
// See: AXTableInfo
const int32 kUnknownAriaColumnOrRowCount = -1;
const int32_t kUnknownAriaColumnOrRowCount = -1;
} // namespace mojom
} // namespace ax
......@@ -2,12 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
#include "ax_enum_util.h"
namespace ui {
......@@ -1352,33 +1347,6 @@ const char* ToString(ax::mojom::DefaultActionVerb default_action_verb) {
return "";
}
std::string ToLocalizedString(ax::mojom::DefaultActionVerb action_verb) {
switch (action_verb) {
case ax::mojom::DefaultActionVerb::kNone:
return "";
case ax::mojom::DefaultActionVerb::kActivate:
return l10n_util::GetStringUTF8(IDS_AX_ACTIVATE_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kCheck:
return l10n_util::GetStringUTF8(IDS_AX_CHECK_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kClick:
return l10n_util::GetStringUTF8(IDS_AX_CLICK_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kClickAncestor:
return l10n_util::GetStringUTF8(IDS_AX_CLICK_ANCESTOR_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kJump:
return l10n_util::GetStringUTF8(IDS_AX_JUMP_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kOpen:
return l10n_util::GetStringUTF8(IDS_AX_OPEN_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kPress:
return l10n_util::GetStringUTF8(IDS_AX_PRESS_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kSelect:
return l10n_util::GetStringUTF8(IDS_AX_SELECT_ACTION_VERB);
case ax::mojom::DefaultActionVerb::kUncheck:
return l10n_util::GetStringUTF8(IDS_AX_UNCHECK_ACTION_VERB);
}
return "";
}
ax::mojom::DefaultActionVerb ParseDefaultActionVerb(
const char* default_action_verb) {
if (0 == strcmp(default_action_verb, "none"))
......
......@@ -7,8 +7,8 @@
#include <string>
#include "ui/accessibility/ax_base_export.h"
#include "ui/accessibility/ax_enums.mojom-forward.h"
#include "ax_base_export.h"
#include "ax_enums.h"
namespace ui {
......
......@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_enum_util.h"
#include "ax_enum_util.h"
#include <string>
#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "gtest/gtest.h"
#include "ax_enums.h"
#include "ax_node_data.h"
namespace ui {
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Must also be kept in sync with extensions/common/api/automation.idl.
module ax.mojom;
#ifndef UI_ACCESSIBILITY_AX_ENUMS_H_
#define UI_ACCESSIBILITY_AX_ENUMS_H_
// For new entries to the following four enums, also add to
// extensions/common/api/automation.idl. This is enforced
......@@ -24,8 +24,11 @@ module ax.mojom;
//
// If unspecified, the attribute is used across web and native on multiple
// platforms.
namespace ax {
enum Event {
namespace mojom {
enum class Event {
kNone,
kActiveDescendantChanged,
kAlert,
......@@ -79,9 +82,9 @@ enum Event {
kShow, // Native / Automation
kStateChanged, // Native / Automation
kTextChanged,
kWindowActivated, // Native
kWindowDeactivated, // Native
kWindowVisibilityChanged, // Native
kWindowActivated, // Native
kWindowDeactivated, // Native
kWindowVisibilityChanged, // Native
kTextSelectionChanged,
kTooltipClosed,
kTooltipOpened,
......@@ -89,6 +92,8 @@ enum Event {
// explicitly fire an accessibility event,
// only implicitly due to the change.
kValueChanged,
kMinValue = kNone,
kMaxValue = kValueChanged,
};
// Accessibility object roles.
......@@ -103,7 +108,7 @@ enum Event {
// Web: this attribute is only used in web content.
//
// Native: this attribute is only used in native UI.
enum Role {
enum class Role {
kNone,
kAbbr,
kAlert,
......@@ -252,7 +257,7 @@ enum Role {
kNote,
kPane,
kParagraph,
kPdfActionableHighlight, // PDF specific highlight role.
kPdfActionableHighlight, // PDF specific highlight role.
kPluginObject,
kPopUpButton,
kPortal,
......@@ -305,9 +310,11 @@ enum Role {
kWebArea,
kWebView,
kWindow,
kMinValue = kNone,
kMaxValue = kWindow,
};
enum State {
enum class State {
kNone,
kAutofillAvailable,
kCollapsed,
......@@ -330,12 +337,14 @@ enum State {
// Grows vertically, e.g. menu or combo box.
kVertical,
kVisited,
kMinValue = kNone,
kMaxValue = kVisited,
};
// An action to be taken on an accessibility node.
// In contrast to |AXDefaultActionVerb|, these describe what happens to the
// object, e.g. "FOCUS".
enum Action {
enum class Action {
kNone,
// Request image annotations for all the eligible images on a page.
......@@ -431,34 +440,44 @@ enum Action {
// Send an event signaling the end of a test.
kSignalEndOfTest,
kShowTooltip,
// Used for looping through the enum, This must be the last value of this
// enum.
kMinValue = kNone,
kMaxValue = kShowTooltip,
};
enum ActionFlags {
enum class ActionFlags {
kNone,
kRequestImages,
kRequestInlineTextBoxes,
kMinValue = kNone,
kMaxValue = kRequestInlineTextBoxes,
};
// A list of valid values for the horizontal and vertical scroll alignment
// arguments in |AXActionData|. These values control where a node is scrolled
// in the viewport.
enum ScrollAlignment {
enum class ScrollAlignment {
kNone,
kScrollAlignmentCenter,
kScrollAlignmentTop,
kScrollAlignmentBottom,
kScrollAlignmentLeft,
kScrollAlignmentRight,
kScrollAlignmentClosestEdge
kScrollAlignmentClosestEdge,
kMinValue = kNone,
kMaxValue = kScrollAlignmentClosestEdge,
};
// A list of valid values for the scroll behavior argument to argument in
// |AXActionData|. These values control whether a node is scrolled in the
// viewport if it is already visible.
enum ScrollBehavior {
enum class ScrollBehavior {
kNone,
kDoNotScrollIfVisible,
kScrollIfVisible,
kMinValue = kNone,
kMaxValue = kScrollIfVisible,
};
// A list of valid values for the |AXIntAttribute| |default_action_verb|.
......@@ -467,7 +486,7 @@ enum ScrollBehavior {
// In contrast to |AXAction|, these describe what the user can do on the
// object, e.g. "PRESS", not what happens to the object as a result.
// Only one verb can be used at a time to describe the default action.
enum DefaultActionVerb {
enum class DefaultActionVerb {
kNone,
kActivate,
kCheck,
......@@ -484,18 +503,22 @@ enum DefaultActionVerb {
kPress,
kSelect,
kUncheck,
kMinValue = kNone,
kMaxValue = kUncheck,
};
// A change to the accessibility tree.
enum Mutation {
enum class Mutation {
kNone,
kNodeCreated,
kSubtreeCreated,
kNodeChanged,
kNodeRemoved,
kMinValue = kNone,
kMaxValue = kNodeRemoved,
};
enum StringAttribute {
enum class StringAttribute {
kNone,
kAccessKey,
// Only used when invalid_state == invalid_state_other.
......@@ -530,9 +553,11 @@ enum StringAttribute {
kTooltip,
kUrl,
kValue,
kMinValue = kNone,
kMaxValue = kValue,
};
enum IntAttribute {
enum class IntAttribute {
kNone,
kDefaultActionVerb,
// Scrollable container attributes.
......@@ -667,9 +692,11 @@ enum IntAttribute {
// unrelated to the accessibility node ID, or the ID attribute for an
// HTML element - it's an ID used to uniquely identify nodes in Blink.
kDOMNodeId,
kMinValue = kNone,
kMaxValue = kDOMNodeId,
};
enum FloatAttribute {
enum class FloatAttribute {
kNone,
// Range attributes.
kValueForRange,
......@@ -688,6 +715,8 @@ enum FloatAttribute {
// The text indent of the text, in mm.
kTextIndent,
kMinValue = kNone,
kMaxValue = kTextIndent,
};
// These attributes can take three states:
......@@ -698,7 +727,7 @@ enum FloatAttribute {
//
// Finally, note that different tree sources can use all three states for a
// given attribute, while another tree source only uses two.
enum BoolAttribute {
enum class BoolAttribute {
kNone,
// Generic busy state, does not have to be on a live region.
......@@ -763,9 +792,11 @@ enum BoolAttribute {
// True if the node has any ARIA attributes set.
kHasAriaAttribute,
kMinValue = kNone,
kMaxValue = kHasAriaAttribute,
};
enum IntListAttribute {
enum class IntListAttribute {
kNone,
// Ids of nodes that are children of this node logically, but are
// not children of this node in the tree structure. As an example,
......@@ -812,26 +843,32 @@ enum IntListAttribute {
// items. Developer can expose those actions as custom actions. Currently
// custom actions are used only in Android window.
kCustomActionIds,
kMinValue = kNone,
kMaxValue = kCustomActionIds,
};
enum StringListAttribute {
enum class StringListAttribute {
kNone,
// Descriptions for custom actions. This must be aligned with
// custom_action_ids.
kCustomActionDescriptions,
kMinValue = kNone,
kMaxValue = kCustomActionDescriptions,
};
enum ListStyle {
enum class ListStyle {
kNone,
kCircle,
kDisc,
kImage,
kNumeric,
kSquare,
kOther, // Language specific ordering (alpha, roman, cjk-ideographic, etc...)
kOther, // Language specific ordering (alpha, roman, cjk-ideographic, etc...)
kMinValue = kNone,
kMaxValue = kOther,
};
enum MarkerType {
enum class MarkerType {
kNone = 0,
kSpelling = 1,
kGrammar = 2,
......@@ -840,35 +877,41 @@ enum MarkerType {
// purposes
kActiveSuggestion = 16,
kSuggestion = 32,
kMinValue = kNone,
kMaxValue = kSuggestion,
};
// Describes a move direction in the accessibility tree that is independent of
// the left-to-right or right-to-left direction of the text. For example, a
// forward movement will always move to the next node in depth-first pre-order
// traversal.
enum MoveDirection {
enum class MoveDirection {
kForward,
kBackward,
kNone = kForward
kNone = kForward,
kMinValue = kForward,
kMaxValue = kBackward,
};
// Describes the edit or selection command that resulted in a selection or a
// text changed event.
enum Command {
enum class Command {
kClearSelection,
kCut,
kDelete,
kDictate,
kExtendSelection, // The existing selection has been extended or shrunk.
kFormat, // The text attributes, such as font size, have changed.
kFormat, // The text attributes, such as font size, have changed.
kInsert,
kMarker, // A document marker has been added or removed.
kMarker, // A document marker has been added or removed.
kMoveSelection, // The selection has been moved by a specific granularity.
kPaste,
kReplace,
kSetSelection, // A completely new selection has been set.
kType,
kNone = kType
kNone = kType,
kMinValue = kClearSelection,
kMaxValue = kType,
};
// Defines a set of text boundaries in the accessibility tree.
......@@ -882,7 +925,7 @@ enum Command {
// e.g. at the start or end of a text field.
//
// TODO(nektar): Split TextBoundary into TextUnit and TextBoundary.
enum TextBoundary {
enum class TextBoundary {
kCharacter,
kFormat,
kLineEnd,
......@@ -902,53 +945,65 @@ enum TextBoundary {
kWordEnd,
kWordStart,
kWordStartOrEnd,
kNone = kObject
kNone = kObject,
kMinValue = kCharacter,
kMaxValue = kWordStartOrEnd,
};
// Types of text alignment according to the IAccessible2 Object Attributes spec.
enum TextAlign {
enum class TextAlign {
kNone,
kLeft,
kRight,
kCenter,
kJustify,
kMinValue = kNone,
kMaxValue = kJustify,
};
enum WritingDirection {
enum class WritingDirection {
kNone,
kLtr,
kRtl,
kTtb,
kBtt,
kMinValue = kNone,
kMaxValue = kBtt,
};
enum TextPosition {
enum class TextPosition {
kNone,
kSubscript,
kSuperscript,
kMinValue = kNone,
kMaxValue = kSuperscript,
};
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.ui.accessibility
enum TextStyle {
enum class TextStyle {
kBold,
kItalic,
kUnderline,
kLineThrough,
kOverline,
kNone
kNone,
kMinValue = kBold,
kMaxValue = kNone,
};
enum TextDecorationStyle {
enum class TextDecorationStyle {
kNone,
kDotted,
kDashed,
kSolid,
kDouble,
kWavy,
kMinValue = kNone,
kMaxValue = kWavy,
};
enum AriaCurrentState {
enum class AriaCurrentState {
kNone,
kFalse,
kTrue,
......@@ -958,9 +1013,11 @@ enum AriaCurrentState {
kUnclippedLocation,
kDate,
kTime,
kMinValue = kNone,
kMaxValue = kTime,
};
enum HasPopup {
enum class HasPopup {
kFalse = 0,
kTrue,
kMenu,
......@@ -968,72 +1025,88 @@ enum HasPopup {
kTree,
kGrid,
kDialog,
kNone = kFalse
kNone = kFalse,
kMinValue = kNone,
kMaxValue = kDialog,
};
enum InvalidState {
enum class InvalidState {
kNone,
kFalse,
kTrue,
kOther,
kMinValue = kNone,
kMaxValue = kOther,
};
// Input restriction associated with an object.
// No value for a control means it is enabled.
// Use read_only for a textbox that allows focus/selection but not input.
// Use disabled for a control or group of controls that disallows input.
enum Restriction {
enum class Restriction {
kNone,
kReadOnly,
kDisabled,
kMinValue = kNone,
kMaxValue = kDisabled,
};
enum CheckedState {
enum class CheckedState {
kNone,
kFalse,
kTrue,
kMixed,
kMinValue = kNone,
kMaxValue = kMixed,
};
enum SortDirection {
enum class SortDirection {
kNone,
kUnsorted,
kAscending,
kDescending,
kOther,
kMinValue = kNone,
kMaxValue = kOther,
};
enum NameFrom {
enum class NameFrom {
kNone,
kUninitialized,
kAttribute, // E.g. aria-label.
kAttributeExplicitlyEmpty,
kCaption, // E.g. in the case of a table, from a caption element.
kContents,
kPlaceholder, // E.g. from an HTML placeholder attribute on a text field.
kRelatedElement, // E.g. from a figcaption Element in a figure.
kTitle, // E.g. <input type="text" title="title">.
kValue, // E.g. <input type="button" value="Button's name">.
kPlaceholder, // E.g. from an HTML placeholder attribute on a text field.
kRelatedElement, // E.g. from a figcaption Element in a figure.
kTitle, // E.g. <input type="text" title="title">.
kValue, // E.g. <input type="button" value="Button's name">.
kMinValue = kNone,
kMaxValue = kValue,
};
enum DescriptionFrom {
enum class DescriptionFrom {
kNone,
kUninitialized,
kAttribute,
kContents,
kRelatedElement,
kTitle,
kMinValue = kNone,
kMaxValue = kTitle,
};
enum EventFrom {
enum class EventFrom {
kNone,
kUser,
kPage,
kAction,
kMinValue = kNone,
kMaxValue = kAction,
};
// Touch gestures on Chrome OS.
enum Gesture {
enum class Gesture {
kNone,
kClick,
kSwipeLeft1,
......@@ -1056,16 +1129,20 @@ enum Gesture {
kTap3,
kTap4,
kTouchExplore,
kMinValue = kNone,
kMaxValue = kTouchExplore,
};
enum TextAffinity {
enum class TextAffinity {
kNone,
kDownstream,
kUpstream,
kMinValue = kNone,
kMaxValue = kUpstream,
};
// Compares two nodes in an accessibility tree in pre-order traversal.
enum TreeOrder {
enum class TreeOrder {
kNone,
// Not in the same tree, or other error.
kUndefined,
......@@ -1078,15 +1155,19 @@ enum TreeOrder {
// First node is after the second one.
kAfter,
kMinValue = kNone,
kMaxValue = kAfter,
};
// For internal use by ui::AXTreeID / ax::mojom::AXTreeID.
enum AXTreeIDType {
// For internal use by ui::AXTreeID / ui::AXTreeID.
enum class AXTreeIDType {
kUnknown, // The Tree ID is unknown.
kToken, // Every other tree ID must have a valid unguessable token.
kMinValue = kUnknown,
kMaxValue = kToken,
};
enum ImageAnnotationStatus {
enum class ImageAnnotationStatus {
// Not an image, or image annotation feature not enabled.
kNone,
......@@ -1124,13 +1205,23 @@ enum ImageAnnotationStatus {
// The annotation process failed, e.g. unable to contact the server,
// request timed out, etc.
kAnnotationProcessFailed,
kMinValue = kNone,
kMaxValue = kAnnotationProcessFailed,
};
enum Dropeffect {
enum class Dropeffect {
kNone,
kCopy,
kExecute,
kLink,
kMove,
kPopup,
kMinValue = kNone,
kMaxValue = kPopup,
};
} // namespace mojom
} // namespace ax
#endif // UI_ACCESSIBILITY_AX_ENUMS_H_
// Copyright 2018 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 "ui/accessibility/ax_event.h"
#include "base/strings/string_number_conversions.h"
#include "ui/accessibility/ax_enum_util.h"
namespace ui {
AXEvent::AXEvent() = default;
AXEvent::AXEvent(AXNodeData::AXID id,
ax::mojom::Event event_type,
ax::mojom::EventFrom event_from,
const std::vector<AXEventIntent>& event_intents,
int action_request_id)
: id(id),
event_type(event_type),
event_from(event_from),
event_intents(event_intents),
action_request_id(action_request_id) {}
AXEvent::~AXEvent() = default;
AXEvent::AXEvent(const AXEvent& event) = default;
AXEvent& AXEvent::operator=(const AXEvent& event) = default;
std::string AXEvent::ToString() const {
std::string result = "AXEvent ";
result += ui::ToString(event_type);
result += " on node id=" + base::NumberToString(id);
if (event_from != ax::mojom::EventFrom::kNone)
result += std::string(" from ") + ui::ToString(event_from);
if (!event_intents.empty()) {
result += " caused by [ ";
for (const AXEventIntent& intent : event_intents) {
result += intent.ToString() + ' ';
}
result += ']';
}
if (action_request_id)
result += " action_request_id=" + base::NumberToString(action_request_id);
return result;
}
} // namespace ui
// Copyright 2018 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.
#ifndef UI_ACCESSIBILITY_AX_EVENT_H_
#define UI_ACCESSIBILITY_AX_EVENT_H_
#include <string>
#include <vector>
#include "ui/accessibility/ax_base_export.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_event_intent.h"
#include "ui/accessibility/ax_node_data.h"
namespace ui {
struct AX_BASE_EXPORT AXEvent final {
AXEvent();
AXEvent(AXNodeData::AXID id,
ax::mojom::Event event_type,
ax::mojom::EventFrom event_from = ax::mojom::EventFrom::kNone,
const std::vector<AXEventIntent>& event_intents = {},
int action_request_id = -1);
virtual ~AXEvent();
AXEvent(const AXEvent& event);
AXEvent& operator=(const AXEvent& event);
// The id of the node in the AXTree that the event should be fired on.
AXNodeData::AXID id = AXNodeData::kInvalidAXID;
// The type of event.
ax::mojom::Event event_type = ax::mojom::Event::kNone;
// The source of the event.
ax::mojom::EventFrom event_from = ax::mojom::EventFrom::kNone;
// Describes what caused an accessibility event to be raised. For example, in
// the case of a selection changed event, the selection could have been
// extended to the beginning of the previous word, or it could have been moved
// to the end of the next line. Note that there could be multiple causes that
// resulted in an event.
std::vector<AXEventIntent> event_intents;
// The action request ID that was passed in if this event was fired in
// direct response to a ax::mojom::Action.
int action_request_id = -1;
// Returns a string representation of this data, for debugging.
std::string ToString() const;
};
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_EVENT_H_
// Copyright 2018 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.
#ifndef UI_ACCESSIBILITY_AX_EVENT_BUNDLE_SINK_H_
#define UI_ACCESSIBILITY_AX_EVENT_BUNDLE_SINK_H_
#include <vector>
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree_update.h"
namespace gfx {
class Point;
} // namespace gfx
namespace ui {
struct AXEvent;
class AXTreeID;
// Interface for a consumer of groups of AXEvents.
class AX_EXPORT AXEventBundleSink {
public:
// |tree_id|: ID of the accessibility tree that the events apply to.
// |updates|: Zero or more updates to the accessibility tree to apply first.
// |mouse location|: Current mouse location in screen coordinates.
// |events|: Zero or more events to fire after the updates have been applied.
// Callers may wish to std::move() into the vector params to avoid copies.
virtual void DispatchAccessibilityEvents(const AXTreeID& tree_id,
std::vector<AXTreeUpdate> updates,
const gfx::Point& mouse_location,
std::vector<AXEvent> events) = 0;
protected:
virtual ~AXEventBundleSink() {}
};
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_EVENT_BUNDLE_SINK_H_
......@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_event_generator.h"
#include "ax_event_generator.h"
#include <algorithm>
#include "base/stl_util.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ax_enums.h"
#include "ax_node.h"
#include "ax_role_properties.h"
#include "base/container_utils.h"
namespace ui {
namespace {
......@@ -96,7 +96,7 @@ AXEventGenerator::EventParams::~EventParams() = default;
AXEventGenerator::TargetedEvent::TargetedEvent(AXNode* node,
const EventParams& event_params)
: node(node), event_params(event_params) {
DCHECK(node);
BASE_DCHECK(node);
}
bool AXEventGenerator::EventParams::operator==(const EventParams& rhs) {
......@@ -141,7 +141,7 @@ AXEventGenerator::Iterator& AXEventGenerator::Iterator::operator++() {
}
AXEventGenerator::TargetedEvent AXEventGenerator::Iterator::operator*() const {
DCHECK(map_iter_ != map_.end() && set_iter_ != map_iter_->second.end());
BASE_DCHECK(map_iter_ != map_.end() && set_iter_ != map_iter_->second.end());
return AXEventGenerator::TargetedEvent(map_iter_->first, *set_iter_);
}
......@@ -149,21 +149,21 @@ AXEventGenerator::AXEventGenerator() = default;
AXEventGenerator::AXEventGenerator(AXTree* tree) : tree_(tree) {
if (tree_)
tree_event_observer_.Add(tree_);
tree_->AddObserver(this);
}
AXEventGenerator::~AXEventGenerator() = default;
void AXEventGenerator::SetTree(AXTree* new_tree) {
if (tree_)
tree_event_observer_.Remove(tree_);
tree_->RemoveObserver(this);
tree_ = new_tree;
if (tree_)
tree_event_observer_.Add(tree_);
tree_->AddObserver(this);
}
void AXEventGenerator::ReleaseTree() {
tree_event_observer_.RemoveAll();
tree_->RemoveObserver(this);
tree_ = nullptr;
}
......@@ -172,7 +172,7 @@ void AXEventGenerator::ClearEvents() {
}
void AXEventGenerator::AddEvent(AXNode* node, AXEventGenerator::Event event) {
DCHECK(node);
BASE_DCHECK(node);
if (node->data().role == ax::mojom::Role::kInlineTextBox)
return;
......@@ -185,7 +185,7 @@ void AXEventGenerator::AddEvent(AXNode* node, AXEventGenerator::Event event) {
void AXEventGenerator::OnNodeDataChanged(AXTree* tree,
const AXNodeData& old_node_data,
const AXNodeData& new_node_data) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
// Fire CHILDREN_CHANGED events when the list of children updates.
// Internally we store inline text box nodes as children of a static text
// node or a line break node, which enables us to determine character bounds
......@@ -204,7 +204,7 @@ void AXEventGenerator::OnRoleChanged(AXTree* tree,
AXNode* node,
ax::mojom::Role old_role,
ax::mojom::Role new_role) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
AddEvent(node, Event::ROLE_CHANGED);
}
......@@ -212,7 +212,7 @@ void AXEventGenerator::OnStateChanged(AXTree* tree,
AXNode* node,
ax::mojom::State state,
bool new_value) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
if (state != ax::mojom::State::kIgnored) {
AddEvent(node, Event::STATE_CHANGED);
......@@ -260,7 +260,7 @@ void AXEventGenerator::OnStringAttributeChanged(AXTree* tree,
ax::mojom::StringAttribute attr,
const std::string& old_value,
const std::string& new_value) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
switch (attr) {
case ax::mojom::StringAttribute::kAccessKey:
......@@ -334,7 +334,7 @@ void AXEventGenerator::OnIntAttributeChanged(AXTree* tree,
ax::mojom::IntAttribute attr,
int32_t old_value,
int32_t new_value) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
switch (attr) {
case ax::mojom::IntAttribute::kActivedescendantId:
......@@ -433,7 +433,7 @@ void AXEventGenerator::OnFloatAttributeChanged(AXTree* tree,
ax::mojom::FloatAttribute attr,
float old_value,
float new_value) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
switch (attr) {
case ax::mojom::FloatAttribute::kMaxValueForRange:
......@@ -470,7 +470,7 @@ void AXEventGenerator::OnBoolAttributeChanged(AXTree* tree,
AXNode* node,
ax::mojom::BoolAttribute attr,
bool new_value) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
switch (attr) {
case ax::mojom::BoolAttribute::kBusy:
......@@ -509,7 +509,7 @@ void AXEventGenerator::OnIntListAttributeChanged(
ax::mojom::IntListAttribute attr,
const std::vector<int32_t>& old_value,
const std::vector<int32_t>& new_value) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
switch (attr) {
case ax::mojom::IntListAttribute::kControlsIds:
......@@ -551,7 +551,7 @@ void AXEventGenerator::OnIntListAttributeChanged(
void AXEventGenerator::OnTreeDataChanged(AXTree* tree,
const AXTreeData& old_tree_data,
const AXTreeData& new_tree_data) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
if (new_tree_data.loaded && !old_tree_data.loaded &&
ShouldFireLoadEvents(tree->root())) {
......@@ -573,28 +573,28 @@ void AXEventGenerator::OnTreeDataChanged(AXTree* tree,
}
void AXEventGenerator::OnNodeWillBeDeleted(AXTree* tree, AXNode* node) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
tree_events_.erase(node);
}
void AXEventGenerator::OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
}
void AXEventGenerator::OnNodeWillBeReparented(AXTree* tree, AXNode* node) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
tree_events_.erase(node);
}
void AXEventGenerator::OnSubtreeWillBeReparented(AXTree* tree, AXNode* node) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
}
void AXEventGenerator::OnAtomicUpdateFinished(
AXTree* tree,
bool root_changed,
const std::vector<Change>& changes) {
DCHECK_EQ(tree_, tree);
BASE_DCHECK(tree_ == tree);
if (root_changed && ShouldFireLoadEvents(tree->root())) {
if (tree->data().loaded)
......@@ -718,7 +718,7 @@ void AXEventGenerator::TrimEventsDueToAncestorIgnoredChanged(
AXNode* node,
std::map<AXNode*, IgnoredChangedStatesBitset>&
ancestor_ignored_changed_map) {
DCHECK(node);
BASE_DCHECK(node);
// Recursively compute and cache ancestor ignored changed results in
// |ancestor_ignored_changed_map|, if |node|'s ancestors have become ignored
......@@ -1049,7 +1049,7 @@ const char* ToString(AXEventGenerator::Event event) {
case AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED:
return "WIN_IACCESSIBLE_STATE_CHANGED";
}
NOTREACHED();
BASE_UNREACHABLE();
}
} // namespace ui
......@@ -11,11 +11,10 @@
#include <set>
#include <vector>
#include "base/scoped_observer.h"
#include "ui/accessibility/ax_event_intent.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/ax_tree_observer.h"
#include "ax_event_intent.h"
#include "ax_export.h"
#include "ax_tree.h"
#include "ax_tree_observer.h"
namespace ui {
......@@ -290,10 +289,6 @@ class AX_EXPORT AXEventGenerator : public AXTreeObserver {
std::vector<AXNode*> active_descendant_changed_;
bool always_fire_load_complete_ = false;
// Please make sure that this ScopedObserver is always declared last in order
// to prevent any use-after-free.
ScopedObserver<AXTree, AXTreeObserver> tree_event_observer_{this};
};
AX_EXPORT std::ostream& operator<<(std::ostream& os,
......
......@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_event_intent.h"
#include "ax_event_intent.h"
#include "ui/accessibility/ax_enum_util.h"
#include "ax_enum_util.h"
namespace ui {
......
......@@ -7,8 +7,8 @@
#include <string>
#include "ui/accessibility/ax_base_export.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ax_base_export.h"
#include "ax_enums.h"
namespace ui {
......
// Copyright 2014 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 <memory>
#include <numeric>
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_event_generator.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_serializable_tree.h"
#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/ax_tree_serializer.h"
#include "ui/accessibility/tree_generator.h"
namespace ui {
namespace {
// A function to turn a tree into a string, capturing only the node ids
// and their relationship to one another.
//
// The string format is kind of like an S-expression, with each expression
// being either a node id, or a node id followed by a subexpression
// representing its children.
//
// Examples:
//
// (1) is a tree with a single node with id 1.
// (1 (2 3)) is a tree with 1 as the root, and 2 and 3 as its children.
// (1 (2 (3))) has 1 as the root, 2 as its child, and then 3 as the child of 2.
// (1 (2 (3x))) is the same with node 3 ignored.
std::string TreeToStringHelper(const AXNode* node) {
std::string result = base::NumberToString(node->id());
if (node->IsIgnored())
result += "x";
if (node->children().empty())
return result;
const auto add_children = [](const std::string& str, const auto* node) {
return str + " " + TreeToStringHelper(node);
};
return result + " (" +
std::accumulate(node->children().cbegin() + 1, node->children().cend(),
TreeToStringHelper(node->children().front()),
add_children) +
")";
}
std::string TreeToString(const AXTree& tree) {
return "(" + TreeToStringHelper(tree.root()) + ")";
}
AXTreeUpdate SerializeEntireTree(AXSerializableTree& tree) {
std::unique_ptr<AXTreeSource<const AXNode*, AXNodeData, AXTreeData>>
tree_source(tree.CreateTreeSource());
AXTreeSerializer<const AXNode*, AXNodeData, AXTreeData> serializer(
tree_source.get());
AXTreeUpdate update;
CHECK(serializer.SerializeChanges(tree.root(), &update));
return update;
}
// Create an AXTreeUpdate consisting of only those nodes from
// |tree0| that changed their ignored status in |tree1|.
AXTreeUpdate MakeTreeUpdateFromIgnoredChanges(AXSerializableTree& tree0,
AXSerializableTree& tree1) {
AXTreeUpdate update = SerializeEntireTree(tree1);
AXTreeUpdate result;
for (size_t i = 0; i < update.nodes.size(); i++) {
AXNode* tree0_node = tree0.GetFromId(update.nodes[i].id);
AXNode* tree1_node = tree1.GetFromId(update.nodes[i].id);
if (tree0_node->IsIgnored() != tree1_node->IsIgnored())
result.nodes.push_back(update.nodes[i]);
}
return result;
}
void SerializeUnignoredNodes(AXNode* node, AXTreeUpdate* update) {
AXNodeData data = node->data();
data.child_ids.clear();
for (size_t i = 0; i < node->GetUnignoredChildCount(); i++) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
data.child_ids.push_back(child->id());
}
update->nodes.push_back(data);
for (size_t i = 0; i < node->GetUnignoredChildCount(); i++) {
AXNode* child = node->GetUnignoredChildAtIndex(i);
SerializeUnignoredNodes(child, update);
}
}
void MakeTreeOfUnignoredNodesOnly(AXSerializableTree& src,
AXSerializableTree* dst) {
AXTreeUpdate update;
update.root_id = src.root()->id();
SerializeUnignoredNodes(src.root(), &update);
CHECK(dst->Unserialize(update));
}
} // anonymous namespace
// Test the TreeGenerator class by building all possible trees with
// 3 nodes and the ids [1...3], with no permutations of ids.
TEST(AXGeneratedTreeTest, TestTreeGeneratorNoPermutations) {
int tree_size = 3;
TreeGenerator generator(tree_size, false);
// clang-format off
const char* EXPECTED_TREES[] = {
"(1)",
"(1 (2))",
"(1 (2 3))",
"(1 (2 (3)))",
};
// clang-format on
int n = generator.UniqueTreeCount();
ASSERT_EQ(static_cast<int>(base::size(EXPECTED_TREES)), n);
for (int i = 0; i < n; ++i) {
AXTree tree;
generator.BuildUniqueTree(i, &tree);
std::string str = TreeToString(tree);
EXPECT_EQ(EXPECTED_TREES[i], str);
}
}
// Test generating trees with permutations of ignored nodes.
TEST(AXGeneratedTreeTest, TestGeneratingTreesWithIgnoredNodes) {
int tree_size = 3;
TreeGenerator generator(tree_size, false);
// clang-format off
const char* EXPECTED_TREES[] = {
"(1)",
"(1 (2))",
"(1 (2x))",
"(1 (2 3))",
"(1 (2x 3))",
"(1 (2 3x))",
"(1 (2x 3x))",
"(1 (2 (3)))",
"(1 (2x (3)))",
"(1 (2 (3x)))",
"(1 (2x (3x)))",
};
// clang-format on
int n = generator.UniqueTreeCount();
int expected_index = 0;
for (int i = 0; i < n; ++i) {
int ignored_permutation_count =
generator.IgnoredPermutationCountPerUniqueTree(i);
for (int j = 0; j < ignored_permutation_count; j++) {
AXTree tree;
generator.BuildUniqueTreeWithIgnoredNodes(i, j, &tree);
std::string str = TreeToString(tree);
EXPECT_EQ(EXPECTED_TREES[expected_index++], str);
}
}
EXPECT_EQ(11, expected_index);
}
// Test the TreeGenerator class by building all possible trees with
// 3 nodes and the ids [1...3] permuted in any order.
TEST(AXGeneratedTreeTest, TestTreeGeneratorWithPermutations) {
int tree_size = 3;
TreeGenerator generator(tree_size, true);
// clang-format off
const char* EXPECTED_TREES[] = {
"(1)",
"(1 (2))",
"(2 (1))",
"(1 (2 3))",
"(2 (1 3))",
"(3 (1 2))",
"(1 (3 2))",
"(2 (3 1))",
"(3 (2 1))",
"(1 (2 (3)))",
"(2 (1 (3)))",
"(3 (1 (2)))",
"(1 (3 (2)))",
"(2 (3 (1)))",
"(3 (2 (1)))",
};
// clang-format on
int n = generator.UniqueTreeCount();
ASSERT_EQ(static_cast<int>(base::size(EXPECTED_TREES)), n);
for (int i = 0; i < n; i++) {
AXTree tree;
generator.BuildUniqueTree(i, &tree);
std::string str = TreeToString(tree);
EXPECT_EQ(EXPECTED_TREES[i], str);
}
}
// Test mutating every possible tree with <n> nodes to every other possible
// tree with <n> nodes, where <n> is 4 in release mode and 3 in debug mode
// (for speed). For each possible combination of trees, we also vary which
// node we serialize first.
//
// For every possible scenario, we check that the AXTreeUpdate is valid,
// that the destination tree can unserialize it and create a valid tree,
// and that after updating all nodes the resulting tree now matches the
// intended tree.
TEST(AXGeneratedTreeTest, SerializeGeneratedTrees) {
// Do a more exhaustive test in release mode. If you're modifying
// the algorithm you may want to try even larger tree sizes if you
// can afford the time.
#ifdef NDEBUG
int max_tree_size = 4;
#else
LOG(WARNING) << "Debug build, only testing trees with 3 nodes and not 4.";
int max_tree_size = 3;
#endif
TreeGenerator generator0(max_tree_size, false);
int n0 = generator0.UniqueTreeCount();
TreeGenerator generator1(max_tree_size, true);
int n1 = generator1.UniqueTreeCount();
for (int i = 0; i < n0; i++) {
// Build the first tree, tree0.
AXSerializableTree tree0;
generator0.BuildUniqueTree(i, &tree0);
SCOPED_TRACE("tree0 is " + TreeToString(tree0));
for (int j = 0; j < n1; j++) {
// Build the second tree, tree1.
AXSerializableTree tree1;
generator1.BuildUniqueTree(j, &tree1);
SCOPED_TRACE("tree1 is " + TreeToString(tree1));
int tree_size = tree1.size();
// Now iterate over which node to update first, |k|.
for (int k = 0; k < tree_size; k++) {
// Iterate over a node to invalidate, |l| (zero means no invalidation).
for (int l = 0; l <= tree_size; l++) {
SCOPED_TRACE("i=" + base::NumberToString(i) +
" j=" + base::NumberToString(j) +
" k=" + base::NumberToString(k) +
" l=" + base::NumberToString(l));
// Start by serializing tree0 and unserializing it into a new
// empty tree |dst_tree|.
std::unique_ptr<AXTreeSource<const AXNode*, AXNodeData, AXTreeData>>
tree0_source(tree0.CreateTreeSource());
AXTreeSerializer<const AXNode*, AXNodeData, AXTreeData> serializer(
tree0_source.get());
AXTreeUpdate update0;
ASSERT_TRUE(serializer.SerializeChanges(tree0.root(), &update0));
AXTree dst_tree;
ASSERT_TRUE(dst_tree.Unserialize(update0));
// At this point, |dst_tree| should now be identical to |tree0|.
EXPECT_EQ(TreeToString(tree0), TreeToString(dst_tree));
// Next, pretend that tree0 turned into tree1.
std::unique_ptr<AXTreeSource<const AXNode*, AXNodeData, AXTreeData>>
tree1_source(tree1.CreateTreeSource());
serializer.ChangeTreeSourceForTesting(tree1_source.get());
// Invalidate a subtree rooted at one of the nodes.
if (l > 0)
serializer.InvalidateSubtree(tree1.GetFromId(l));
// Serialize a sequence of updates to |dst_tree| to match.
for (int k_index = 0; k_index < tree_size; ++k_index) {
int id = 1 + (k + k_index) % tree_size;
AXTreeUpdate update;
ASSERT_TRUE(
serializer.SerializeChanges(tree1.GetFromId(id), &update));
ASSERT_TRUE(dst_tree.Unserialize(update));
}
// After the sequence of updates, |dst_tree| should now be
// identical to |tree1|.
EXPECT_EQ(TreeToString(tree1), TreeToString(dst_tree));
}
}
}
}
}
TEST(AXGeneratedTreeTest, GeneratedTreesWithIgnoredNodes) {
int max_tree_size = 5;
TreeGenerator generator(max_tree_size, false);
int unique_tree_count = generator.UniqueTreeCount();
// Loop over every possible tree up to a certain size.
for (int tree_index = 0; tree_index < unique_tree_count; tree_index++) {
// Try each permutation of nodes other than the root being ignored.
// We'll call this tree the "fat" tree because it has redundant
// ignored nodes.
int ignored_permutation_count =
generator.IgnoredPermutationCountPerUniqueTree(tree_index);
for (int perm_index0 = 0; perm_index0 < ignored_permutation_count;
perm_index0++) {
AXSerializableTree fat_tree;
generator.BuildUniqueTreeWithIgnoredNodes(tree_index, perm_index0,
&fat_tree);
SCOPED_TRACE("fat_tree is " + TreeToString(fat_tree));
// Create a second tree, also with each permutations of nodes
// other than the root being ignored.
for (int perm_index1 = 1; perm_index1 < ignored_permutation_count;
perm_index1++) {
AXSerializableTree fat_tree1;
generator.BuildUniqueTreeWithIgnoredNodes(tree_index, perm_index1,
&fat_tree1);
SCOPED_TRACE("fat_tree1 is " + TreeToString(fat_tree1));
// Make a source and destination tree using only the unignored nodes.
// We call this one the "skinny" tree.
AXSerializableTree skinny_tree;
MakeTreeOfUnignoredNodesOnly(fat_tree, &skinny_tree);
AXSerializableTree skinny_tree1;
MakeTreeOfUnignoredNodesOnly(fat_tree1, &skinny_tree1);
// Now, turn fat_tree into fat_tree1, and record the generated events.
AXEventGenerator event_generator(&fat_tree);
AXTreeUpdate update =
MakeTreeUpdateFromIgnoredChanges(fat_tree, fat_tree1);
ASSERT_TRUE(fat_tree.Unserialize(update));
EXPECT_EQ(TreeToString(fat_tree), TreeToString(fat_tree1));
// Capture the events generated.
std::map<AXNode::AXID, std::set<AXEventGenerator::Event>> actual_events;
for (const AXEventGenerator::TargetedEvent& event : event_generator) {
if (event.node->IsIgnored() ||
event.event_params.event ==
AXEventGenerator::Event::IGNORED_CHANGED) {
continue;
}
actual_events[event.node->id()].insert(event.event_params.event);
}
// Now, turn skinny_tree into skinny_tree1 and compare
// the generated events.
AXEventGenerator skinny_event_generator(&skinny_tree);
AXTreeUpdate skinny_update = SerializeEntireTree(skinny_tree1);
ASSERT_TRUE(skinny_tree.Unserialize(skinny_update));
EXPECT_EQ(TreeToString(skinny_tree), TreeToString(skinny_tree1));
std::map<AXNode::AXID, std::set<AXEventGenerator::Event>>
expected_events;
for (const AXEventGenerator::TargetedEvent& event :
skinny_event_generator)
expected_events[event.node->id()].insert(event.event_params.event);
for (auto& entry : expected_events) {
AXNode::AXID node_id = entry.first;
for (auto& event_type : entry.second) {
EXPECT_TRUE(actual_events[node_id].find(event_type) !=
actual_events[node_id].end())
<< "Expected " << event_type << " on node " << node_id;
}
}
for (auto& entry : actual_events) {
AXNode::AXID node_id = entry.first;
for (auto& event_type : entry.second) {
EXPECT_TRUE(expected_events[node_id].find(event_type) !=
expected_events[node_id].end())
<< "Unexpected " << event_type << " on node " << node_id;
}
}
// For each node in skinny_tree (the tree with only the unignored
// nodes), check the node in fat_tree (the tree with ignored nodes).
// Make sure that the parents, children, and siblings are all computed
// correctly.
AXTreeUpdate skinny_tree_serialized = SerializeEntireTree(skinny_tree);
for (size_t i = 0; i < skinny_tree_serialized.nodes.size(); i++) {
AXNode::AXID id = skinny_tree_serialized.nodes[i].id;
AXNode* skinny_tree_node = skinny_tree.GetFromId(id);
AXNode* fat_tree_node = fat_tree.GetFromId(id);
SCOPED_TRACE("Testing node ID " + base::NumberToString(id));
// Check children.
EXPECT_EQ(skinny_tree_node->children().size(),
fat_tree_node->GetUnignoredChildCount());
// Check child IDs.
for (size_t j = 0; j < skinny_tree_node->children().size(); j++) {
AXNode* skinny_tree_child = skinny_tree_node->children()[j];
AXNode* fat_tree_child = fat_tree_node->GetUnignoredChildAtIndex(j);
EXPECT_TRUE(skinny_tree_child);
EXPECT_TRUE(fat_tree_child);
if (fat_tree_child)
EXPECT_EQ(skinny_tree_child->id(), fat_tree_child->id());
}
// Check parent.
if (skinny_tree_node->parent()) {
EXPECT_EQ(skinny_tree_node->parent()->id(),
fat_tree_node->GetUnignoredParent()->id());
} else {
EXPECT_FALSE(fat_tree_node->GetUnignoredParent());
}
// Check index in parent.
EXPECT_EQ(skinny_tree_node->index_in_parent(),
fat_tree_node->GetUnignoredIndexInParent());
// Unignored previous sibling.
size_t index_in_parent = skinny_tree_node->index_in_parent();
size_t num_siblings =
skinny_tree_node->parent()
? skinny_tree_node->parent()->children().size()
: 1;
if (index_in_parent > 0) {
AXNode* skinny_tree_previous_sibling =
skinny_tree_node->parent()->children()[index_in_parent - 1];
AXNode* fat_tree_previous_sibling =
fat_tree_node->GetPreviousUnignoredSibling();
EXPECT_TRUE(fat_tree_previous_sibling);
if (fat_tree_previous_sibling) {
EXPECT_EQ(skinny_tree_previous_sibling->id(),
fat_tree_previous_sibling->id());
}
}
// Unignored next sibling.
if (index_in_parent < num_siblings - 1) {
AXNode* skinny_tree_next_sibling =
skinny_tree_node->parent()->children()[index_in_parent + 1];
AXNode* fat_tree_next_sibling =
fat_tree_node->GetNextUnignoredSibling();
EXPECT_TRUE(fat_tree_next_sibling);
if (fat_tree_next_sibling) {
EXPECT_EQ(skinny_tree_next_sibling->id(),
fat_tree_next_sibling->id());
}
}
}
}
}
}
}
} // namespace ui
......@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_mode.h"
#include "ax_mode.h"
#include <vector>
#include "base/strings/string_util.h"
#include "base/logging.h"
#include "base/string_utils.h"
namespace ui {
......@@ -46,11 +47,12 @@ std::string AXMode::ToString() const {
break;
}
DCHECK(flag_name);
BASE_DCHECK(flag_name);
if (has_mode(mode_flag))
tokens.push_back(flag_name);
}
return base::JoinString(tokens, " | ");
}
......
......@@ -10,7 +10,7 @@
#include <ostream>
#include <string>
#include "ui/accessibility/ax_base_export.h"
#include "ax_base_export.h"
namespace ui {
......
......@@ -5,8 +5,8 @@
#ifndef UI_ACCESSIBILITY_AX_MODE_OBSERVER_H_
#define UI_ACCESSIBILITY_AX_MODE_OBSERVER_H_
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_mode.h"
#include "ax_export.h"
#include "ax_mode.h"
namespace ui {
......
......@@ -6,22 +6,24 @@
#define UI_ACCESSIBILITY_AX_NODE_H_
#include <stdint.h>
#include <optional>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
#include "base/optional.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ax_build/build_config.h"
#include "ax_export.h"
#include "ax_node_data.h"
#include "ax_tree_id.h"
#include "base/logging.h"
#include "gfx/geometry/rect.h"
#include "gfx/transform.h"
namespace ui {
class AXTableInfo;
struct AXLanguageInfo;
// One node in an AXTree.
class AX_EXPORT AXNode final {
......@@ -29,9 +31,10 @@ class AX_EXPORT AXNode final {
// Defines the type used for AXNode IDs.
using AXID = int32_t;
// TODO(chunhtai): I modified this to be -1 so it can work with flutter.
// If a node is not yet or no longer valid, its ID should have a value of
// kInvalidAXID.
static constexpr AXID kInvalidAXID = 0;
static constexpr AXID kInvalidAXID = -1;
// Interface to the tree class that owns an AXNode. We use this instead
// of letting AXNode have a pointer to its AXTree directly so that we're
......@@ -56,8 +59,8 @@ class AX_EXPORT AXNode final {
// See AXTree::GetFromId.
virtual AXNode* GetFromId(int32_t id) const = 0;
virtual base::Optional<int> GetPosInSet(const AXNode& node) = 0;
virtual base::Optional<int> GetSetSize(const AXNode& node) = 0;
virtual std::optional<int> GetPosInSet(const AXNode& node) = 0;
virtual std::optional<int> GetSetSize(const AXNode& node) = 0;
virtual Selection GetUnignoredSelection() const = 0;
virtual bool GetTreeUpdateInProgressState() const = 0;
......@@ -235,10 +238,10 @@ class AX_EXPORT AXNode final {
}
bool GetString16Attribute(ax::mojom::StringAttribute attribute,
base::string16* value) const {
std::u16string* value) const {
return data().GetString16Attribute(attribute, value);
}
base::string16 GetString16Attribute(
std::u16string GetString16Attribute(
ax::mojom::StringAttribute attribute) const {
return data().GetString16Attribute(attribute);
}
......@@ -267,7 +270,7 @@ class AX_EXPORT AXNode final {
return data().GetStringListAttribute(attribute, value);
}
bool GetHtmlAttribute(const char* attribute, base::string16* value) const {
bool GetHtmlAttribute(const char* attribute, std::u16string* value) const {
return data().GetHtmlAttribute(attribute, value);
}
bool GetHtmlAttribute(const char* attribute, std::string* value) const {
......@@ -275,13 +278,13 @@ class AX_EXPORT AXNode final {
}
// Return the hierarchical level if supported.
base::Optional<int> GetHierarchicalLevel() const;
std::optional<int> GetHierarchicalLevel() const;
// PosInSet and SetSize public methods.
bool IsOrderedSetItem() const;
bool IsOrderedSet() const;
base::Optional<int> GetPosInSet();
base::Optional<int> GetSetSize();
std::optional<int> GetPosInSet();
std::optional<int> GetSetSize();
// Helpers for GetPosInSet and GetSetSize.
// Returns true if the role of ordered set matches the role of item.
......@@ -294,9 +297,8 @@ class AX_EXPORT AXNode final {
const std::string& GetInheritedStringAttribute(
ax::mojom::StringAttribute attribute) const;
base::string16 GetInheritedString16Attribute(
std::u16string GetInheritedString16Attribute(
ax::mojom::StringAttribute attribute) const;
// Returns the text of this node and all descendant nodes; including text
// found in embedded objects.
//
......@@ -315,7 +317,6 @@ class AX_EXPORT AXNode final {
// Returns empty string if no appropriate language was found.
std::string GetLanguage() const;
//
// Helper functions for tables, table rows, and table cells.
// Most of these functions construct and cache an AXTableInfo behind
// the scenes to infer many properties of tables.
......@@ -332,15 +333,15 @@ class AX_EXPORT AXNode final {
// of the table is row 0, column 0, cell index 0 - but that same cell
// has a minimum ARIA row index of 1 and column index of 1.
//
// The below methods return base::nullopt if the AXNode they are called on is
// The below methods return std::nullopt if the AXNode they are called on is
// not inside a table.
bool IsTable() const;
base::Optional<int> GetTableColCount() const;
base::Optional<int> GetTableRowCount() const;
base::Optional<int> GetTableAriaColCount() const;
base::Optional<int> GetTableAriaRowCount() const;
base::Optional<int> GetTableCellCount() const;
base::Optional<bool> GetTableHasColumnOrRowHeaderNode() const;
std::optional<int> GetTableColCount() const;
std::optional<int> GetTableRowCount() const;
std::optional<int> GetTableAriaColCount() const;
std::optional<int> GetTableAriaRowCount() const;
std::optional<int> GetTableCellCount() const;
std::optional<bool> GetTableHasColumnOrRowHeaderNode() const;
AXNode* GetTableCaption() const;
AXNode* GetTableCellFromIndex(int index) const;
AXNode* GetTableCellFromCoords(int row_index, int col_index) const;
......@@ -358,25 +359,25 @@ class AX_EXPORT AXNode final {
// Table row-like nodes.
bool IsTableRow() const;
base::Optional<int> GetTableRowRowIndex() const;
std::optional<int> GetTableRowRowIndex() const;
// Get the node ids that represent rows in a table.
std::vector<AXNode::AXID> GetTableRowNodeIds() const;
#if defined(OS_APPLE)
// Table column-like nodes. These nodes are only present on macOS.
bool IsTableColumn() const;
base::Optional<int> GetTableColColIndex() const;
std::optional<int> GetTableColColIndex() const;
#endif // defined(OS_APPLE)
// Table cell-like nodes.
bool IsTableCellOrHeader() const;
base::Optional<int> GetTableCellIndex() const;
base::Optional<int> GetTableCellColIndex() const;
base::Optional<int> GetTableCellRowIndex() const;
base::Optional<int> GetTableCellColSpan() const;
base::Optional<int> GetTableCellRowSpan() const;
base::Optional<int> GetTableCellAriaColIndex() const;
base::Optional<int> GetTableCellAriaRowIndex() const;
std::optional<int> GetTableCellIndex() const;
std::optional<int> GetTableCellColIndex() const;
std::optional<int> GetTableCellRowIndex() const;
std::optional<int> GetTableCellColSpan() const;
std::optional<int> GetTableCellRowSpan() const;
std::optional<int> GetTableCellAriaColIndex() const;
std::optional<int> GetTableCellAriaRowIndex() const;
std::vector<AXNode::AXID> GetTableCellColHeaderNodeIds() const;
std::vector<AXNode::AXID> GetTableCellRowHeaderNodeIds() const;
void GetTableCellColHeaders(std::vector<AXNode*>* col_headers) const;
......@@ -386,21 +387,6 @@ class AX_EXPORT AXNode final {
bool IsCellOrHeaderOfARIATable() const;
bool IsCellOrHeaderOfARIAGrid() const;
// Return an object containing information about the languages detected on
// this node.
// Callers should not retain this pointer, instead they should request it
// every time it is needed.
//
// Returns nullptr if the node has no language info.
AXLanguageInfo* GetLanguageInfo() const;
// This should only be called by LabelLanguageForSubtree and is used as part
// of the language detection feature.
void SetLanguageInfo(std::unique_ptr<AXLanguageInfo> lang_info);
// Destroy the language info for this node.
void ClearLanguageInfo();
// Returns true if node is a group and is a direct descendant of a set-like
// element.
bool IsEmbeddedGroup() const;
......@@ -466,9 +452,6 @@ class AX_EXPORT AXNode final {
AXNode* const parent_;
std::vector<AXNode*> children_;
AXNodeData data_;
// Stores the detected language computed from the node's text.
std::unique_ptr<AXLanguageInfo> language_info_;
};
AX_EXPORT std::ostream& operator<<(std::ostream& stream, const AXNode& node);
......@@ -595,7 +578,7 @@ NodeType* AXNode::ChildIteratorBase<NodeType,
PreviousSibling,
FirstChild,
LastChild>::get() const {
DCHECK(child_);
BASE_DCHECK(child_);
return child_;
}
......@@ -609,7 +592,7 @@ NodeType& AXNode::ChildIteratorBase<NodeType,
PreviousSibling,
FirstChild,
LastChild>::operator*() const {
DCHECK(child_);
BASE_DCHECK(child_);
return *child_;
}
......@@ -623,7 +606,7 @@ NodeType* AXNode::ChildIteratorBase<NodeType,
PreviousSibling,
FirstChild,
LastChild>::operator->() const {
DCHECK(child_);
BASE_DCHECK(child_);
return child_;
}
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/accessibility/ax_node_text_styles.h"
#include "ax_node_text_styles.h"
constexpr int kUnsetValue = -1;
......
......@@ -7,7 +7,7 @@
#include <string>
#include "ui/accessibility/ax_base_export.h"
#include "ax_base_export.h"
namespace ui {
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册