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 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 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::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); } }
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()); } }
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 AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* criteria, AccessibilityChildrenVector& results) { ASSERT(criteria); if (!criteria) return; AccessibilityObject* startObject = criteria->startObject; AccessibilityChildrenVector searchStack; searchStack.append(this); bool isForward = criteria->searchDirection == SearchDirectionNext; bool didFindStartObject = !criteria->startObject; // FIXME: Iterate the AccessibilityObject cache creating and adding objects if nessesary. while (!searchStack.isEmpty()) { AccessibilityObject* searchObject = searchStack.last().get(); searchStack.removeLast(); if (didFindStartObject) { if (isAccessibilityObjectSearchMatch(searchObject, criteria) && isAccessibilityTextSearchMatch(searchObject, criteria)) { results.append(searchObject); // Enough results were found to stop searching. if (results.size() >= criteria->resultsLimit) break; } } else if (searchObject == startObject) didFindStartObject = true; AccessibilityChildrenVector searchChildren = searchObject->children(); size_t childrenSize = searchChildren.size(); for (size_t i = isForward ? childrenSize : 0; isForward ? i > 0 : i < childrenSize; isForward ? i-- : i++) { // FIXME: Handle attachments. searchStack.append(searchChildren.at(isForward ? i - 1 : i).get()); } } }
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 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 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); }
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; }
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; }
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); }
AXObject* AXTableRow::headerObject() { if (!m_renderer || !m_renderer->isTableRow()) return 0; AccessibilityChildrenVector rowChildren = children(); if (!rowChildren.size()) return 0; // check the first element in the row to see if it is a TH element AXObject* cell = rowChildren[0].get(); if (!cell->isTableCell()) return 0; RenderObject* cellRenderer = toAXTableCell(cell)->renderer(); if (!cellRenderer) return 0; Node* cellNode = cellRenderer->node(); if (!cellNode || !cellNode->hasTagName(thTag)) return 0; return cell; }