AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column, unsigned row) { updateChildrenIfNecessary(); if (column >= columnCount() || row >= rowCount()) return 0; // Iterate backwards through the rows in case the desired cell has a rowspan and exists in a previous row. for (unsigned rowIndexCounter = row + 1; rowIndexCounter > 0; --rowIndexCounter) { unsigned rowIndex = rowIndexCounter - 1; AccessibilityChildrenVector children = m_rows[rowIndex]->children(); // Since some cells may have colspans, we have to check the actual range of each // cell to determine which is the right one. for (unsigned colIndexCounter = std::min(static_cast<unsigned>(children.size()), column + 1); colIndexCounter > 0; --colIndexCounter) { unsigned colIndex = colIndexCounter - 1; AccessibilityObject* child = children[colIndex].get(); ASSERT(child->isTableCell()); if (!child->isTableCell()) continue; pair<unsigned, unsigned> columnRange; pair<unsigned, unsigned> rowRange; AccessibilityTableCell* tableCellChild = toAccessibilityTableCell(child); tableCellChild->columnIndexRange(columnRange); tableCellChild->rowIndexRange(rowRange); if ((column >= columnRange.first && column < (columnRange.first + columnRange.second)) && (row >= rowRange.first && row < (rowRange.first + rowRange.second))) return tableCellChild; } } return 0; }
void AccessibilityTableCell::columnHeaders(AccessibilityChildrenVector& headers) { AccessibilityTable* parent = parentTable(); if (!parent) return; std::pair<unsigned, unsigned> rowRange; rowIndexRange(rowRange); std::pair<unsigned, unsigned> colRange; columnIndexRange(colRange); for (unsigned row = 0; row < rowRange.first; row++) { AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(colRange.first, row); if (tableCell == this || headers.contains(tableCell)) continue; std::pair<unsigned, unsigned> childRowRange; tableCell->rowIndexRange(childRowRange); const AtomicString& scope = tableCell->getAttribute(scopeAttr); if (scope == "col" || tableCell->isTableHeaderCell()) headers.append(tableCell); else if (scope == "colgroup" && isTableCellInSameColGroup(tableCell)) headers.append(tableCell); } }
void AccessibilityTableCell::columnHeaders(AccessibilityChildrenVector& headers) { AccessibilityTable* parent = parentTable(); if (!parent) return; // Choose columnHeaders as the place where the "headers" attribute is reported. ariaElementsFromAttribute(headers, headersAttr); // If the headers attribute returned valid values, then do not further search for column headers. if (!headers.isEmpty()) return; std::pair<unsigned, unsigned> rowRange; rowIndexRange(rowRange); std::pair<unsigned, unsigned> colRange; columnIndexRange(colRange); for (unsigned row = 0; row < rowRange.first; row++) { AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(colRange.first, row); if (!tableCell || tableCell == this || headers.contains(tableCell)) continue; std::pair<unsigned, unsigned> childRowRange; tableCell->rowIndexRange(childRowRange); const AtomicString& scope = tableCell->getAttribute(scopeAttr); if (scope == "col" || tableCell->isTableHeaderCell()) headers.append(tableCell); else if (scope == "colgroup" && isTableCellInSameColGroup(tableCell)) headers.append(tableCell); } }
void AccessibilityNodeObject::addChildren() { // If the need to add more children in addition to existing children arises, // childrenChanged should have been called, leaving the object with no children. ASSERT(!m_haveChildren); if (!m_node) return; m_haveChildren = true; // The only time we add children from the DOM tree to a node with a renderer is when it's a canvas. if (renderer() && !m_node->hasTagName(canvasTag)) return; for (Node* child = m_node->firstChild(); child; child = child->nextSibling()) { RefPtr<AccessibilityObject> obj = axObjectCache()->getOrCreate(child); obj->clearChildren(); if (obj->accessibilityIsIgnored()) { AccessibilityChildrenVector children = obj->children(); size_t length = children.size(); for (size_t i = 0; i < length; ++i) m_children.append(children[i]); } else { ASSERT(obj->parentObject() == this); m_children.append(obj); } } }
void AccessibilityObject::accessibleObjectsWithAccessibilitySearchPredicate(AccessibilitySearchPredicate* axSearchPredicate, AccessibilityChildrenVector& axResults) { ASSERT(AXObjectCache::accessibilityEnabled()); if (!axSearchPredicate) return; AccessibilityChildrenVector axChildren; if (axSearchPredicate->axContainerObject) axChildren.append(axSearchPredicate->axContainerObject); bool isSearchDirectionNext = (axSearchPredicate->axSearchDirection == SearchDirectionNext); bool didFindAXStartObject = (!axSearchPredicate->axStartObject); // FIXME: Iterate the AccessibilityObject cache creating and adding objects if nessesary. while (!axChildren.isEmpty() && axResults.size() < axSearchPredicate->resultsLimit) { AccessibilityObject* axChild = axChildren.last().get(); axChildren.removeLast(); if (didFindAXStartObject) { if (isAccessibilityObjectSearchMatch(axChild, axSearchPredicate) && isAccessibilityTextSearchMatch(axChild, axSearchPredicate)) axResults.append(axChild); } else if (axChild == axSearchPredicate->axStartObject) didFindAXStartObject = true; AccessibilityChildrenVector axGrandchildren = axChild->children(); unsigned axGrandchildrenSize = axChild->children().size(); for (unsigned i = (isSearchDirectionNext) ? axGrandchildrenSize : 0; (isSearchDirectionNext) ? i > 0 : i < axGrandchildrenSize; (isSearchDirectionNext) ? i-- : i++) // FIXME: Handle attachments. axChildren.append(axGrandchildren.at((isSearchDirectionNext) ? i - 1 : i).get()); } }
void AccessibilityARIAGridCell::rowIndexRange(pair<unsigned, unsigned>& rowRange) { AccessibilityObject* parent = parentObjectUnignored(); if (!parent) return; if (parent->isTableRow()) { // We already got a table row, use its API. rowRange.first = static_cast<AccessibilityTableRow*>(parent)->rowIndex(); } else if (parent->isAccessibilityTable()) { // We reached the parent table, so we need to inspect its // children to determine the row index for the cell in it. unsigned columnCount = static_cast<AccessibilityTable*>(parent)->columnCount(); if (!columnCount) return; AccessibilityChildrenVector siblings = parent->children(); unsigned childrenSize = siblings.size(); for (unsigned k = 0; k < childrenSize; ++k) { if (siblings[k].get() == this) { rowRange.first = k / columnCount; break; } } } // as far as I can tell, grid cells cannot span rows rowRange.second = 1; }
void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result) { ASSERT(result.isEmpty()); if (!hasChildren()) addChildren(); for (const auto& child : m_children) { if (toAccessibilityListBoxOption(child.get())->isSelected()) result.append(child.get()); } }
AccessibilityObject* AccessibilityARIAGridRow::headerObject() { AccessibilityChildrenVector rowChildren = children(); unsigned childrenCount = rowChildren.size(); for (unsigned i = 0; i < childrenCount; ++i) { AccessibilityObject* cell = rowChildren[i].get(); if (cell->ariaRoleAttribute() == RowHeaderRole) return cell; } return 0; }
void AccessibilityARIAGrid::addChildren() { ASSERT(!m_haveChildren); if (!isAccessibilityTable()) { AccessibilityRenderObject::addChildren(); return; } m_haveChildren = true; if (!m_renderer) return; AXObjectCache* axCache = m_renderer->document().axObjectCache(); // Add the children rows but be mindful in case there are footer sections in this table. HashSet<AccessibilityObject*> appendedRows; unsigned columnCount = 0; AccessibilityChildrenVector footerSections; for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling()) { bool footerSection = false; if (RenderObject* childRenderer = child->renderer()) { if (childRenderer->isTableSection()) { if (RenderTableSection* childSection = toRenderTableSection(childRenderer)) { if (childSection == childSection->table()->footer()) { footerSections.append(child); footerSection = true; } } } } if (!footerSection) addRowDescendant(child.get(), appendedRows, columnCount); } for (const auto& footerSection : footerSections) addRowDescendant(footerSection.get(), appendedRows, columnCount); // make the columns based on the number of columns in the first body for (unsigned i = 0; i < columnCount; ++i) { AccessibilityTableColumn* column = toAccessibilityTableColumn(axCache->getOrCreate(ColumnRole)); column->setColumnIndex((int)i); column->setParent(this); m_columns.append(column); if (!column->accessibilityIsIgnored()) m_children.append(column); } AccessibilityObject* headerContainerObject = headerContainer(); if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored()) m_children.append(headerContainerObject); }
void AccessibilityListBox::visibleChildren(AccessibilityChildrenVector& result) { ASSERT(result.isEmpty()); if (!hasChildren()) addChildren(); unsigned length = m_children.size(); for (unsigned i = 0; i < length; i++) { if (toRenderListBox(m_renderer)->listIndexIsVisible(i)) result.append(m_children[i]); } }
void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result) { ASSERT(result.isEmpty()); if (!hasChildren()) addChildren(); unsigned length = m_children.size(); for (unsigned i = 0; i < length; i++) { if (static_cast<AccessibilityListBoxOption*>(m_children[i].get())->isSelected()) result.append(m_children[i]); } }
void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result) { // The ARIA tree item content are the item that are not other tree items or their containing groups. AccessibilityChildrenVector axChildren = children(); unsigned count = axChildren.size(); for (unsigned k = 0; k < count; ++k) { AccessibilityObject* obj = axChildren[k].get(); AccessibilityRole role = obj->roleValue(); if (role == TreeItemRole || role == GroupRole) continue; result.append(obj); } }
void AccessibilityARIAGrid::addChildren() { ASSERT(!m_haveChildren); if (!isAccessibilityTable()) { AccessibilityRenderObject::addChildren(); return; } m_haveChildren = true; if (!m_renderer) return; AXObjectCache* axCache = m_renderer->document()->axObjectCache(); // add only rows that are labeled as aria rows HashSet<AccessibilityObject*> appendedRows; unsigned columnCount = 0; for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling()) { if (!addTableCellChild(child.get(), appendedRows, columnCount)) { // in case the render tree doesn't match the expected ARIA hierarchy, look at the children if (!child->hasChildren()) child->addChildren(); // The children of this non-row will contain all non-ignored elements (recursing to find them). // This allows the table to dive arbitrarily deep to find the rows. AccessibilityChildrenVector children = child->children(); size_t length = children.size(); for (size_t i = 0; i < length; ++i) addTableCellChild(children[i].get(), appendedRows, columnCount); } } // make the columns based on the number of columns in the first body for (unsigned i = 0; i < columnCount; ++i) { AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->getOrCreate(ColumnRole)); column->setColumnIndex((int)i); column->setParent(this); m_columns.append(column); if (!column->accessibilityIsIgnored()) m_children.append(column); } AccessibilityObject* headerContainerObject = headerContainer(); if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored()) m_children.append(headerContainerObject); }
void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result) { AccessibilityChildrenVector axChildren = children(); unsigned count = axChildren.size(); for (unsigned k = 0; k < count; ++k) { AccessibilityObject* obj = axChildren[k].get(); // Add tree items as the rows. if (obj->roleValue() == TreeItemRole) result.append(obj); // Now see if this item also has rows hiding inside of it. obj->ariaTreeRows(result); } }
void AccessibilityARIAGrid::addRowDescendant(AccessibilityObject* rowChild, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount) { if (!rowChild) return; if (!rowChild->isTableRow()) { // Although a "grid" should have rows as its direct descendants, if this is not a table row, // dive deeper into the descendants to try to find a valid row. AccessibilityChildrenVector children = rowChild->children(); size_t length = children.size(); for (size_t i = 0; i < length; ++i) addRowDescendant(children[i].get(), appendedRows, columnCount); } else addTableCellChild(rowChild, appendedRows, columnCount); }
void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result) { AccessibilityChildrenVector axChildren = children(); unsigned count = axChildren.size(); for (unsigned k = 0; k < count; ++k) { AccessibilityObject* obj = axChildren[k].get(); // Add tree items as the rows. if (obj->roleValue() == TreeItemRole) result.append(obj); // If it's not a tree item, then descend into the group to find more tree items. else obj->ariaTreeRows(result); } }
void AccessibilityARIAGridRow::disclosedRows(AccessibilityChildrenVector& disclosedRows) { // The contiguous disclosed rows will be the rows in the table that // have an aria-level of plus 1 from this row. AccessibilityObject* parent = parentObjectUnignored(); if (!is<AccessibilityTable>(*parent) || !downcast<AccessibilityTable>(*parent).isExposableThroughAccessibility()) return; // Search for rows that match the correct level. // Only take the subsequent rows from this one that are +1 from this row's level. int index = rowIndex(); if (index < 0) return; unsigned level = hierarchicalLevel(); auto& allRows = downcast<AccessibilityTable>(*parent).rows(); int rowCount = allRows.size(); for (int k = index + 1; k < rowCount; ++k) { AccessibilityObject* row = allRows[k].get(); // Stop at the first row that doesn't match the correct level. if (row->hierarchicalLevel() != level + 1) break; disclosedRows.append(row); } }
void AccessibilityListBox::setSelectedChildren(AccessibilityChildrenVector& children) { if (!canSetSelectedChildrenAttribute()) return; Node* selectNode = m_renderer->node(); if (!selectNode) return; // disable any selected options unsigned length = m_children.size(); for (unsigned i = 0; i < length; i++) { AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(m_children[i].get()); if (listBoxOption->isSelected()) listBoxOption->setSelected(false); } length = children.size(); for (unsigned i = 0; i < length; i++) { AccessibilityObject* obj = children[i].get(); if (obj->roleValue() != ListBoxOptionRole) continue; static_cast<AccessibilityListBoxOption*>(obj)->setSelected(true); } }
AccessibilityTableCell* AccessibilityARIAGrid::cellForColumnAndRow(unsigned column, unsigned row) { if (!m_renderer) return 0; updateChildrenIfNecessary(); if (column >= columnCount() || row >= rowCount()) return 0; int intRow = (int)row; int intColumn = (int)column; pair<int, int> columnRange; pair<int, int> rowRange; // Iterate backwards through the rows in case the desired cell has a rowspan and exists // in a previous row. for (; intRow >= 0; --intRow) { AccessibilityObject* tableRow = m_rows[intRow].get(); if (!tableRow) continue; AccessibilityChildrenVector children = tableRow->children(); unsigned childrenLength = children.size(); // Since some cells may have colspans, we have to check the actual range of each // cell to determine which is the right one. for (unsigned k = 0; k < childrenLength; ++k) { AccessibilityObject* child = children[k].get(); if (!child->isTableCell()) continue; AccessibilityTableCell* tableCellChild = static_cast<AccessibilityTableCell*>(child); tableCellChild->columnIndexRange(columnRange); tableCellChild->rowIndexRange(rowRange); if ((intColumn >= columnRange.first && intColumn < (columnRange.first + columnRange.second)) && (intRow >= rowRange.first && intRow < (rowRange.first + rowRange.second))) return tableCellChild; } } return 0; }
void AccessibilityAriaGridCell::columnIndexRange(pair<int, int>& columnRange) { AccessibilityObject* parent = parentObjectUnignored(); if (!parent || !parent->isTableRow()) return; AccessibilityChildrenVector siblings = parent->children(); unsigned childrenSize = siblings.size(); for (unsigned k = 0; k < childrenSize; ++k) { if (siblings[k].get() == this) { columnRange.first = k; break; } } // as far as I can tell, grid cells cannot span columns columnRange.second = 1; }
AccessibilityObject* AccessibilityTableColumn::headerObject() { if (!m_parent) return 0; RenderObject* renderer = m_parent->renderer(); if (!renderer) return 0; if (!m_parent->isAccessibilityTable()) return 0; AccessibilityTable* parentTable = toAccessibilityTable(m_parent); if (parentTable->isAriaTable()) { AccessibilityChildrenVector rowChildren = children(); unsigned childrenCount = rowChildren.size(); for (unsigned i = 0; i < childrenCount; ++i) { AccessibilityObject* cell = rowChildren[i].get(); if (cell->ariaRoleAttribute() == ColumnHeaderRole) return cell; } return 0; } if (!renderer->isTable()) return 0; RenderTable* table = toRenderTable(renderer); AccessibilityObject* headerObject = 0; // try the <thead> section first. this doesn't require th tags headerObject = headerObjectForSection(table->header(), false); if (headerObject) return headerObject; // now try for <th> tags in the first body headerObject = headerObjectForSection(table->firstBody(), true); return headerObject; }
void AccessibilityTable::visibleRows(AccessibilityChildrenVector& rows) { if (!m_renderer) return; updateChildrenIfNecessary(); for (const auto& row : m_rows) { if (row && !row->isOffScreen()) rows.append(row); } }
void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers) { if (!m_renderer) return; updateChildrenIfNecessary(); for (const auto& column : m_columns) { if (AccessibilityObject* header = toAccessibilityTableColumn(column.get())->headerObject()) headers.append(header); } }
void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers) { if (!m_renderer) return; updateChildrenIfNecessary(); for (const auto& row : m_rows) { if (AccessibilityObject* header = toAccessibilityTableRow(row.get())->headerObject()) headers.append(header); } }
AccessibilityObject* AccessibilityTableRow::headerObject() { AccessibilityChildrenVector rowChildren = children(); if (!rowChildren.size()) return 0; // check the first element in the row to see if it is a TH element AccessibilityObject* cell = rowChildren[0].get(); if (!cell->isTableCell()) return 0; RenderObject* cellRenderer = static_cast<AccessibilityTableCell*>(cell)->renderer(); if (!cellRenderer) return 0; Node* cellNode = cellRenderer->element(); if (!cellNode || !cellNode->hasTagName(thTag)) return 0; return cell; }
void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers) { if (!m_renderer) return; updateChildrenIfNecessary(); size_t columnCount = m_columns.size(); for (size_t i = 0; i < columnCount; ++i) { if (AccessibilityObject* header = toAccessibilityTableColumn(m_columns[i].get())->headerObject()) headers.append(header); } }
void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers) { if (!m_renderer) return; updateChildrenIfNecessary(); size_t rowCount = m_rows.size(); for (size_t i = 0; i < rowCount; ++i) { if (AccessibilityObject* header = toAccessibilityTableRow(m_rows[i].get())->headerObject()) headers.append(header); } }
AccessibilityTableCell* AccessibilityARIAGrid::cellForColumnAndRow(unsigned column, unsigned row) { if (!m_renderer) return 0; updateChildrenIfNecessary(); if (column >= columnCount() || row >= rowCount()) return 0; AccessibilityObject* tableRow = m_rows[row].get(); if (!tableRow) return 0; AccessibilityChildrenVector children = tableRow->children(); // in case this row had fewer columns than other rows AccessibilityObject* tableCell = 0; if (column >= children.size()) return 0; tableCell = children[column].get(); return static_cast<AccessibilityTableCell*>(tableCell); }
void AccessibilityTable::visibleRows(AccessibilityChildrenVector& rows) { if (!m_renderer) return; updateChildrenIfNecessary(); size_t rowCount = m_rows.size(); for (size_t i = 0; i < rowCount; ++i) { AccessibilityObject* row = m_rows[i].get(); if (row && !row->isOffScreen()) rows.append(row); } }
void AccessibilityTableCell::rowHeaders(AccessibilityChildrenVector& headers) { AccessibilityTable* parent = parentTable(); if (!parent) return; std::pair<unsigned, unsigned> rowRange; rowIndexRange(rowRange); std::pair<unsigned, unsigned> colRange; columnIndexRange(colRange); for (unsigned column = 0; column < colRange.first; column++) { AccessibilityTableCell* tableCell = parent->cellForColumnAndRow(column, rowRange.first); if (tableCell == this || headers.contains(tableCell)) continue; const AtomicString& scope = tableCell->getAttribute(scopeAttr); if (scope == "row") headers.append(tableCell); else if (scope == "rowgroup" && isTableCellInSameRowGroup(tableCell)) headers.append(tableCell); } }