#include "chrome/renderer/accessibility/read_anything/read_aloud_traversal_utils.h"
#include "ui/accessibility/ax_text_utils.h"
namespace a11y {
ReadAloudCurrentGranularity::ReadAloudCurrentGranularity() = default;
ReadAloudCurrentGranularity::ReadAloudCurrentGranularity(
const ReadAloudCurrentGranularity& other) = default;
ReadAloudCurrentGranularity::~ReadAloudCurrentGranularity() = default;
void ReadAloudCurrentGranularity::AddText(ui::AXNodeID id,
int text_start,
int text_end,
const std::u16string& text_to_add) {
DCHECK((text_end - text_start) == (int)text_to_add.size());
ReadAloudTextSegment segment{id, text_start, text_end};
segments[segment.id] = segment;
node_ids.push_back(segment.id);
int current_text_length = text.length();
text += text_to_add;
index_map.insert({{current_text_length, text.length()}, id});
}
std::vector<ReadAloudTextSegment>
ReadAloudCurrentGranularity::GetSegmentsForRange(int start_index,
int end_index) {
if (start_index >= end_index) {
return {};
}
auto start = index_map.upper_bound({start_index, INT_MAX});
if (start == index_map.begin()) {
return {};
}
--start;
auto end = index_map.upper_bound({end_index, INT_MAX});
if (end == index_map.begin()) {
return {};
}
std::vector<ReadAloudTextSegment> ret;
while (start != end) {
auto range = start->first;
if ((start_index >= range.second) || (end_index <= range.first)) {
break;
}
ui::AXNodeID node = start->second;
auto segment = segments[node];
int text_start = (start_index >= range.first)
? (segment.text_start + start_index - range.first)
: segment.text_start;
int text_end = (end_index < range.second)
? (segment.text_start + end_index - range.first)
: segment.text_end;
ret.push_back(ReadAloudTextSegment(node, text_start, text_end));
++start;
}
return ret;
}
void ReadAloudCurrentGranularity::CalculatePlaceholderPhrases() {
if (text.size() == 0) {
phrase_boundaries.clear();
return;
}
std::size_t start = 0;
int count = 0;
do {
if (count % 3 == 0) {
phrase_boundaries.push_back(start);
}
int next_word = GetNextWord(text.substr(start));
if (next_word == 0) {
break;
}
start += next_word;
++count;
if (start >= text.size()) {
break;
}
} while (start);
phrase_boundaries.push_back(text.size());
}
}
namespace {
int GetNextGranularity(const std::u16string& text,
ax::mojom::TextBoundary boundary) {
std::vector<int> offsets;
return ui::FindAccessibleTextBoundary(text, offsets, boundary, 0,
ax::mojom::MoveDirection::kForward,
ax::mojom::TextAffinity::kDefaultValue);
}
}
int GetNextSentence(const std::u16string& text) {
return GetNextGranularity(text, ax::mojom::TextBoundary::kSentenceStart);
}
int GetNextWord(const std::u16string& text) {
return GetNextGranularity(text, ax::mojom::TextBoundary::kWordStart);
}
bool ArePositionsEqual(const ui::AXNodePosition::AXPositionInstance& position,
const ui::AXNodePosition::AXPositionInstance& other) {
return position->GetAnchor() && other->GetAnchor() &&
(position->CompareTo(*other).value_or(-1) == 0) &&
(position->text_offset() == other->text_offset());
}
ui::AXNode* GetAnchorNode(
const ui::AXNodePosition::AXPositionInstance& position) {
if (position->GetAnchor()->HasState(ax::mojom::State::kEditable) &&
position->GetAnchor()->IsText() &&
!position->GetAnchor()->GetLowestPlatformAncestor()->IsText()) {
return position->GetAnchor();
}
bool is_leaf = position->GetAnchor()->IsChildOfLeaf();
return is_leaf ? position->GetAnchor()->GetLowestPlatformAncestor()
: position->GetAnchor();
}
bool IsOpeningPunctuation(char& c) {
return (c == '(' || c == '{' || c == '[' || c == '<');
}
bool ShouldSplitAtParagraph(
const ui::AXNodePosition::AXPositionInstance& position,
const a11y::ReadAloudCurrentGranularity current_granularity) {
return position->AtStartOfParagraph() &&
(current_granularity.node_ids.size() > 0);
}