blob: d8a9c3354f7c98bd910f5b381a4d3a110f43033a [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tools/accessibility/inspect/ax_utils.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
char kActiveTabSwitch[] = "active-tab";
char kChromeSwitch[] = "chrome";
char kChromiumSwitch[] = "chromium";
char kFirefoxSwitch[] = "firefox";
char kEdgeSwitch[] = "edge";
char kPatternSwitch[] = "pattern";
char kSafariSwitch[] = "safari";
char kFiltersSwitch[] = "filters";
#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC)
char kIdSwitch[] = "pid";
#else
char kIdSwitch[] = "window";
#endif // defined(WINDOWS)
using ui::AXTreeSelector;
gfx::AcceleratedWidget CastToAcceleratedWidget(unsigned int window_id) {
#if BUILDFLAG(IS_OZONE) || BUILDFLAG(IS_MAC)
return static_cast<gfx::AcceleratedWidget>(window_id);
#else
return reinterpret_cast<gfx::AcceleratedWidget>(window_id);
#endif
}
// Convert from string to int, whether in 0x hex format or decimal format.
bool StringToInt(std::string str, unsigned* result) {
if (str.empty())
return false;
bool is_hex =
str.size() > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X');
return is_hex ? base::HexStringToUInt(str, result)
: base::StringToUint(str, result);
}
namespace tools {
void PrintHelpShared() {
printf("options:\n");
PrintHelpTreeSelectors();
PrintHelpFilters();
PrintHelpFooter();
}
void PrintHelpTreeSelectors() {
printf(" --pattern\ttitle of an application\n");
#if defined(WINDOWS)
printf(" --window\tHWND of a window\n");
#else
printf(" --pid\t\tprocess id of an application\n");
#endif
printf(" pre-defined application selectors:\n");
printf(" --chrome\tChrome browser\n");
printf(" --chromium\tChromium browser\n");
#if defined(WINDOWS)
printf(" --edge\tEdge browser\n");
#endif
printf(" --firefox\tFirefox browser\n");
#if BUILDFLAG(IS_MAC)
printf(" --safari\tSafari browser\n");
#endif
printf(
" --active-tab\tactive tab of browser, if application is a browser\n");
}
void PrintHelpFilters() {
printf(
" --filters\tfile containing property filters used to filter out\n"
" \t\taccessible tree, for example:\n"
" \t\t--filters=/absolute/path/to/filters/file\n");
}
void PrintHelpFooter() {
printf(
"\nmore info at "
"https://www.chromium.org/developers/accessibility/testing/"
"automated-testing/ax-inspect\n");
}
std::optional<AXTreeSelector> TreeSelectorFromCommandLine(
const base::CommandLine& command_line) {
int selectors = AXTreeSelector::None;
if (command_line.HasSwitch(kChromeSwitch)) {
selectors = AXTreeSelector::Chrome;
} else if (command_line.HasSwitch(kChromiumSwitch)) {
selectors = AXTreeSelector::Chromium;
} else if (command_line.HasSwitch(kEdgeSwitch)) {
selectors = AXTreeSelector::Edge;
} else if (command_line.HasSwitch(kFirefoxSwitch)) {
selectors = AXTreeSelector::Firefox;
} else if (command_line.HasSwitch(kSafariSwitch)) {
selectors = AXTreeSelector::Safari;
}
if (command_line.HasSwitch(kActiveTabSwitch)) {
selectors |= AXTreeSelector::ActiveTab;
}
std::string pattern_str = command_line.GetSwitchValueASCII(kPatternSwitch);
std::string id_str = command_line.GetSwitchValueASCII(kIdSwitch);
if (!id_str.empty()) {
unsigned hwnd_or_pid = 0;
if (!StringToInt(id_str, &hwnd_or_pid)) {
LOG(ERROR) << "Error: can't convert window id string to integer.";
return std::nullopt;
}
return AXTreeSelector(selectors, pattern_str,
CastToAcceleratedWidget(hwnd_or_pid));
}
return AXTreeSelector(selectors, pattern_str);
}
std::string DirectivePrefixFromAPIType(ui::AXApiType::Type api) {
switch (api) {
case ui::AXApiType::kMac:
return "@AXAPI-";
case ui::AXApiType::kLinux:
return "@ATSPI-";
case ui::AXApiType::kWinIA2:
return "@IA2-";
case ui::AXApiType::kWinUIA:
return "@UIA-";
// If no or unsupported API, use the generic prefix
default:
return "@";
}
}
std::optional<ui::AXInspectScenario> ScenarioFromCommandLine(
const base::CommandLine& command_line,
ui::AXApiType::Type api) {
base::FilePath filters_path = command_line.GetSwitchValuePath(kFiltersSwitch);
if (filters_path.empty() && command_line.HasSwitch(kFiltersSwitch)) {
LOG(ERROR) << "Error: empty filter path given. Run with --help for help.";
return std::nullopt;
}
std::string directive_prefix = DirectivePrefixFromAPIType(api);
// Return with the default filter scenario if no file is provided.
if (filters_path.empty()) {
return ui::AXInspectScenario::From(directive_prefix,
std::vector<std::string>());
}
std::optional<ui::AXInspectScenario> scenario =
ui::AXInspectScenario::From(directive_prefix, filters_path);
if (!scenario) {
LOG(ERROR) << "Error: failed to open filters file " << filters_path
<< ". Note: path traversal components ('..') are not allowed "
"for security reasons";
return std::nullopt;
}
return scenario;
}
} // namespace tools