#include "chrome/renderer/accessibility/read_anything/read_anything_node_utils.h"
#include <cinttypes>
#include "base/strings/stringprintf.h"
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/base/l10n/l10n_util.h"
namespace a11y {
bool IsSuperscript(const ui::AXNode* ax_node) {
return ax_node->data().GetTextPosition() ==
ax::mojom::TextPosition::kSuperscript;
}
bool IsTextForReadAnything(const ui::AXNode* node, bool is_pdf, bool is_docs) {
if (!node) {
return false;
}
bool is_list_marker = node->GetRole() == ax::mojom::Role::kListMarker;
return (GetHtmlTag(node, is_pdf, is_docs).length() == 0) || is_list_marker;
}
bool IsIgnored(const ui::AXNode* const ax_node, bool is_pdf) {
if (ax_node->IsIgnored()) {
return true;
}
const ax::mojom::Role role = ax_node->GetRole();
if (is_pdf) {
const ui::AXNode* const parent = ax_node->GetParent();
if (const std::string_view text = ax_node->GetTextContentUTF8();
text == l10n_util::GetStringUTF8(IDS_PDF_OCR_RESULT_BEGIN)) {
if (role == ax::mojom::Role::kBanner ||
(parent && parent->GetRole() == ax::mojom::Role::kBanner)) {
return true;
}
} else if (text == l10n_util::GetStringUTF8(IDS_PDF_OCR_RESULT_END) &&
parent && parent->GetRole() == ax::mojom::Role::kContentInfo) {
return true;
}
}
return (ui::IsControl(role) && !ui::IsTextField(role)) || ui::IsSelect(role);
}
std::string GetHtmlTag(const ui::AXNode* ax_node, bool is_pdf, bool is_docs) {
std::string html_tag =
ax_node->GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag);
if (is_pdf) {
return GetHtmlTagForPDF(ax_node, html_tag);
}
if (ui::IsTextField(ax_node->GetRole())) {
return "div";
}
if (ui::IsHeading(ax_node->GetRole())) {
int32_t hierarchical_level =
ax_node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
if (hierarchical_level) {
return base::StringPrintf("h%" PRId32, hierarchical_level);
}
}
if (html_tag == ui::ToString(ax::mojom::Role::kMark)) {
html_tag = "b";
} else if (is_docs) {
if (html_tag == "svg") {
html_tag = "div";
}
if (html_tag == "g" && ax_node->GetRole() == ax::mojom::Role::kParagraph) {
html_tag = "p";
}
}
return html_tag;
}
std::string GetHtmlTagForPDF(const ui::AXNode* ax_node,
const std::string& html_tag) {
ax::mojom::Role role = ax_node->GetRole();
switch (role) {
case ax::mojom::Role::kEmbeddedObject:
case ax::mojom::Role::kRegion:
case ax::mojom::Role::kPdfRoot:
case ax::mojom::Role::kRootWebArea:
return "span";
case ax::mojom::Role::kParagraph:
return "p";
case ax::mojom::Role::kLink:
return "a";
case ax::mojom::Role::kStaticText:
return "";
case ax::mojom::Role::kHeading:
return GetHeadingHtmlTagForPDF(ax_node, html_tag);
case ax::mojom::Role::kContentInfo:
if (ax_node->GetTextContentUTF8() ==
l10n_util::GetStringUTF8(IDS_PDF_OCR_RESULT_END)) {
return "br";
}
[[fallthrough]];
default:
return html_tag.empty() ? "span" : html_tag;
}
}
std::string GetHeadingHtmlTagForPDF(const ui::AXNode* ax_node,
const std::string& html_tag) {
if (ax_node->GetTextContentLengthUTF8() > (2 * kMaxLineWidth)) {
return "p";
}
ui::AXNode* next = ax_node->GetNextUnignoredSibling();
ui::AXNode* prev = ax_node->GetPreviousUnignoredSibling();
if ((next && next->GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag) ==
html_tag) ||
(prev && prev->GetStringAttribute(ax::mojom::StringAttribute::kHtmlTag) ==
html_tag)) {
return "span";
}
int32_t hierarchical_level =
ax_node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
if (hierarchical_level) {
return base::StringPrintf("h%" PRId32, hierarchical_level);
}
return html_tag;
}
std::string GetAltText(const ui::AXNode* ax_node) {
std::string alt_text =
ax_node->GetStringAttribute(ax::mojom::StringAttribute::kName);
return alt_text;
}
std::u16string GetTextContent(const ui::AXNode* ax_node,
bool is_pdf,
bool is_docs) {
if (is_docs) {
if (!ax_node->GetTextContentLengthUTF16()) {
std::u16string nodeText = GetNameAttributeText(ax_node);
if (!nodeText.empty()) {
return nodeText + u" ";
}
} else {
if (ax_node->GetRole() == ax::mojom::Role::kStaticText) {
return u"";
}
}
}
if (is_pdf) {
std::u16string filtered_string(ax_node->GetTextContentUTF16());
if (is_pdf && filtered_string.size() > 0) {
size_t pos = filtered_string.find_first_of(u"\n\r");
while (pos != std::string::npos && pos < filtered_string.size() - 2) {
filtered_string.replace(pos, 1, u" ");
pos = filtered_string.find_first_of(u"\n\r");
}
}
return filtered_string;
}
return ax_node->GetTextContentUTF16();
}
std::u16string GetPrefixText(const ui::AXNode* ax_node,
bool is_pdf,
bool is_docs) {
auto original_text = GetTextContent(ax_node, is_pdf, is_docs);
auto* node = ax_node->GetPreviousUnignoredInTreeOrder();
auto prefix_text = GetTextContent(node, is_pdf, is_docs);
while (prefix_text.size() < kMinPrefixLength ||
prefix_text == original_text || IsIgnored(node, is_pdf)) {
auto* previous = node->GetPreviousUnignoredInTreeOrder();
if (!previous) {
break;
}
node = previous;
prefix_text = GetTextContent(node, is_pdf, is_docs);
}
return prefix_text;
}
std::u16string GetNameAttributeText(const ui::AXNode* ax_node) {
DCHECK(ax_node);
std::u16string node_text;
if (ax_node->HasStringAttribute(ax::mojom::StringAttribute::kName)) {
node_text =
ax_node->GetString16Attribute(ax::mojom::StringAttribute::kName);
}
for (auto it = ax_node->UnignoredChildrenBegin();
it != ax_node->UnignoredChildrenEnd(); ++it) {
if (node_text.empty()) {
node_text = GetNameAttributeText(it.get());
} else {
node_text += u" " + GetNameAttributeText(it.get());
}
}
return node_text;
}
}