示例#1
0
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);
    }
}
示例#3
0
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);
    }
}
示例#4
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 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());
    }
}
示例#6
0
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;
}
示例#20
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;
}
示例#22
0
void AccessibilityTable::visibleRows(AccessibilityChildrenVector& rows)
{
    if (!m_renderer)
        return;
    
    updateChildrenIfNecessary();
    
    for (const auto& row : m_rows) {
        if (row && !row->isOffScreen())
            rows.append(row);
    }
}
示例#23
0
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);
    }
}
示例#24
0
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);
    }
}
示例#25
0
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;
}
示例#26
0
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);
    }
}
示例#27
0
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);
}
示例#29
0
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);
    }
}