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()) addRowDescendant(child.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); }
AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const { // Some objects change their role based on their parent. // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop. // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored(). // https://bugs.webkit.org/show_bug.cgi?id=65174 if (role != ListBoxOptionRole && role != MenuItemRole) return role; for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) { AccessibilityRole parentAriaRole = parent->ariaRoleAttribute(); // Selects and listboxes both have options as child roles, but they map to different roles within WebCore. if (role == ListBoxOptionRole && parentAriaRole == MenuRole) return MenuItemRole; // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent. if (role == MenuItemRole && parentAriaRole == GroupRole) return MenuButtonRole; // If the parent had a different role, then we don't need to continue searching up the chain. if (parentAriaRole) break; } return role; }
AccessibilityObject* AccessibilityListBox::elementAccessibilityHitTest(const LayoutPoint& point) const { // the internal HTMLSelectElement methods for returning a listbox option at a point // ignore optgroup elements. if (!m_renderer) return 0; Node* node = m_renderer->node(); if (!node) return 0; LayoutRect parentRect = boundingBoxRect(); AccessibilityObject* listBoxOption = 0; unsigned length = m_children.size(); for (unsigned i = 0; i < length; i++) { LayoutRect rect = toRenderListBox(m_renderer)->itemBoundingBoxRect(parentRect.location(), i); // The cast to HTMLElement below is safe because the only other possible listItem type // would be a WMLElement, but WML builds don't use accessibility features at all. if (rect.contains(point)) { listBoxOption = m_children[i].get(); break; } } if (listBoxOption && !listBoxOption->accessibilityIsIgnored()) return listBoxOption; return axObjectCache()->getOrCreate(m_renderer); }
static gint webkitAccessibleTextGetCaretOffset(AtkText* text) { // coreObject is the unignored object whose offset the caller is requesting. // focusedObject is the object with the caret. It is likely ignored -- unless it's a link. AccessibilityObject* coreObject = core(text); if (!coreObject->isAccessibilityRenderObject()) return 0; // We need to make sure we pass a valid object as reference. if (coreObject->accessibilityIsIgnored()) coreObject = coreObject->parentObjectUnignored(); if (!coreObject) return 0; int offset; if (!objectFocusedAndCaretOffsetUnignored(coreObject, offset)) return 0; RenderObject* renderer = coreObject->renderer(); if (renderer && renderer->isListItem()) { String markerText = toRenderListItem(renderer)->markerTextWithSuffix(); // We need to adjust the offset for the list item marker. offset += markerText.length(); } // TODO: Verify this for RTL text. return offset; }
AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page) { if (!gAccessibilityEnabled) return 0; // get the focused node in the page Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document(); Node* focusedNode = focusedDocument->focusedNode(); if (!focusedNode) focusedNode = focusedDocument; if (focusedNode->hasTagName(areaTag)) return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode)); AccessibilityObject* obj = focusedNode->document()->axObjectCache()->getOrCreate(focusedNode); if (!obj) return 0; if (obj->shouldFocusActiveDescendant()) { if (AccessibilityObject* descendant = obj->activeDescendant()) obj = descendant; } // the HTML element, for example, is focusable but has an AX object that is ignored if (obj->accessibilityIsIgnored()) obj = obj->parentObjectUnignored(); return obj; }
bool AccessibilityList::childHasPseudoVisibleListItemMarkers(RenderObject* listItem) { // Check if the list item has a pseudo-element that should be accessible (e.g. an image or text) Element* listItemElement = downcast<Element>(listItem->node()); if (!listItemElement || !listItemElement->beforePseudoElement()) return false; AccessibilityObject* axObj = axObjectCache()->getOrCreate(listItemElement->beforePseudoElement()->renderer()); if (!axObj) return false; if (!axObj->accessibilityIsIgnored()) return true; for (const auto& child : axObj->children()) { if (!child->accessibilityIsIgnored()) return true; } // Platforms which expose rendered text content through the parent element will treat // those renderers as "ignored" objects. #if PLATFORM(GTK) || PLATFORM(EFL) String text = axObj->textUnderElement(); return !text.isEmpty() && !text.containsOnlyWhitespace(); #else return false; #endif }
AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node) { ASSERT(AXObjectCache::accessibilityEnabled()); if (!node) return 0; Document* document = node->document(); if (!document) return 0; AXObjectCache* cache = document->axObjectCache(); AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer()); while (accessibleObject && accessibleObject->accessibilityIsIgnored()) { node = node->traverseNextNode(); while (node && !node->renderer()) node = node->traverseNextSibling(); if (!node) return 0; accessibleObject = cache->getOrCreate(node->renderer()); } return accessibleObject; }
AccessibilityObject* AccessibilityObject::parentObjectUnignored() const { AccessibilityObject* parent; for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) ; return parent; }
AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node) { if (!node) return 0; Document* document = node->document(); if (!document) return 0; AXObjectCache* cache = document->axObjectCache(); AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer()); while (accessibleObject && accessibleObject->accessibilityIsIgnored()) { node = NodeTraversal::next(node); while (node && !node->renderer()) node = NodeTraversal::nextSkippingChildren(node); if (!node) return 0; accessibleObject = cache->getOrCreate(node->renderer()); } return accessibleObject; }
bool AccessibilityScrollView::computeAccessibilityIsIgnored() const { AccessibilityObject* webArea = webAreaObject(); if (!webArea) return true; return webArea->accessibilityIsIgnored(); }
void AccessibilityScrollView::addChildren() { ASSERT(!m_haveChildren); m_haveChildren = true; AccessibilityObject* webArea = webAreaObject(); if (webArea && !webArea->accessibilityIsIgnored()) m_children.append(webArea); updateScrollbars(); }
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); }
static bool replacedNodeNeedsCharacter(Node* replacedNode) { // we should always be given a rendered node and a replaced node, but be safe // replaced nodes are either attachments (widgets) or images if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) return false; // create an AX object, but skip it if it is not supposed to be seen AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode); if (object->accessibilityIsIgnored()) return false; return true; }
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 AccessibilityListBox::addChildren() { Node* selectNode = m_renderer->node(); if (!selectNode) return; m_haveChildren = true; for (const auto& listItem : toHTMLSelectElement(selectNode)->listItems()) { // The cast to HTMLElement below is safe because the only other possible listItem type // would be a WMLElement, but WML builds don't use accessibility features at all. AccessibilityObject* listOption = listBoxOptionAccessibilityObject(listItem); if (listOption && !listOption->accessibilityIsIgnored()) m_children.append(listOption); } }
void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject* newFocusedRenderer) { if (!newFocusedRenderer) return; Page* page = newFocusedRenderer->document()->page(); if (!page || !page->chrome()->platformPageClient()) return; AccessibilityObject* focusedObject = focusedUIElementForPage(page); if (!focusedObject) return; ASSERT(!focusedObject->accessibilityIsIgnored()); postPlatformNotification(focusedObject, AXFocusedUIElementChanged); }
void AccessibilityTable::addChildren() { if (!isAccessibilityTable()) { AccessibilityRenderObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!m_renderer || !m_renderer->isTable()) return; RenderTable* table = toRenderTable(m_renderer); // Go through all the available sections to pull out the rows and add them as children. table->recalcSectionsIfNeeded(); unsigned maxColumnCount = 0; RenderTableSection* footer = table->footer(); for (RenderTableSection* tableSection = table->topSection(); tableSection; tableSection = table->sectionBelow(tableSection, SkipEmptySections)) { if (tableSection == footer) continue; addChildrenFromSection(tableSection, maxColumnCount); } // Process the footer last, in case it was ordered earlier in the DOM. if (footer) addChildrenFromSection(footer, maxColumnCount); AXObjectCache* axCache = m_renderer->document().axObjectCache(); // make the columns based on the number of columns in the first body unsigned length = maxColumnCount; for (unsigned i = 0; i < length; ++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::addChildren() { Node* selectNode = m_renderer->node(); if (!selectNode) return; m_haveChildren = true; const Vector<Element*>& listItems = static_cast<HTMLSelectElement*>(selectNode)->listItems(); unsigned length = listItems.size(); for (unsigned i = 0; i < length; i++) { // The cast to HTMLElement below is safe because the only other possible listItem type // would be a WMLElement, but WML builds don't use accessibility features at all. AccessibilityObject* listOption = listBoxOptionAccessibilityObject(toHTMLElement(listItems[i])); if (listOption && !listOption->accessibilityIsIgnored()) m_children.append(listOption); } }
static gint webkitAccessibleTextGetCaretOffset(AtkText* text) { // coreObject is the unignored object whose offset the caller is requesting. // focusedObject is the object with the caret. It is likely ignored -- unless it's a link. AccessibilityObject* coreObject = core(text); if (!coreObject->isAccessibilityRenderObject()) return 0; // We need to make sure we pass a valid object as reference. if (coreObject->accessibilityIsIgnored()) coreObject = coreObject->parentObjectUnignored(); if (!coreObject) return 0; int offset; if (!objectFocusedAndCaretOffsetUnignored(coreObject, offset)) return 0; return webCoreOffsetToAtkOffset(coreObject, offset); }
void AccessibilityMenuList::addChildren() { m_haveChildren = true; AXObjectCache* cache = m_renderer->document().axObjectCache(); AccessibilityObject* list = cache->getOrCreate(MenuListPopupRole); if (!list) return; toAccessibilityMockObject(list)->setParent(this); if (list->accessibilityIsIgnored()) { cache->remove(list->axObjectID()); return; } m_children.append(list); list->addChildren(); }
void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*) { RefPtr<Document> protectorForCacheOwner(m_document); m_notificationPostTimer.stop(); unsigned i = 0, count = m_notificationsToPost.size(); for (i = 0; i < count; ++i) { AccessibilityObject* obj = m_notificationsToPost[i].first.get(); if (!obj->axObjectID()) continue; if (!obj->axObjectCache()) continue; #ifndef NDEBUG // Make sure none of the render views are in the process of being layed out. // Notifications should only be sent after the renderer has finished if (obj->isAccessibilityRenderObject()) { AccessibilityRenderObject* renderObj = toAccessibilityRenderObject(obj); RenderObject* renderer = renderObj->renderer(); if (renderer && renderer->view()) ASSERT(!renderer->view()->layoutState()); } #endif AXNotification notification = m_notificationsToPost[i].second; postPlatformNotification(obj, notification); if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored()) childrenChanged(obj->parentObject()); } m_notificationsToPost.clear(); }
void AccessibilityTable::addChildren() { if (!isAccessibilityTable()) { AccessibilityRenderObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!m_renderer || !m_renderer->isTable()) return; RenderTable* table = toRenderTable(m_renderer); AXObjectCache* axCache = m_renderer->document()->axObjectCache(); // go through all the available sections to pull out the rows // and add them as children // FIXME: This will skip a table with just a tfoot. Should fix by using RenderTable::topSection. RenderTableSection* tableSection = table->header(); if (!tableSection) tableSection = table->firstBody(); if (!tableSection) return; RenderTableSection* initialTableSection = tableSection; while (tableSection) { HashSet<AccessibilityObject*> appendedRows; unsigned numRows = tableSection->numRows(); unsigned numCols = tableSection->numColumns(); for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { for (unsigned colIndex = 0; colIndex < numCols; ++colIndex) { RenderTableCell* cell = tableSection->primaryCellAt(rowIndex, colIndex); if (!cell) continue; AccessibilityObject* rowObject = axCache->getOrCreate(cell->parent()); if (!rowObject->isTableRow()) continue; AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(rowObject); // we need to check every cell for a new row, because cell spans // can cause us to mess rows if we just check the first column if (appendedRows.contains(row)) continue; row->setRowIndex((int)m_rows.size()); m_rows.append(row); if (!row->accessibilityIsIgnored()) m_children.append(row); #if PLATFORM(GTK) else m_children.append(row->children()); #endif appendedRows.add(row); } } tableSection = table->sectionBelow(tableSection, SkipEmptySections); } // make the columns based on the number of columns in the first body unsigned length = initialTableSection->numColumns(); for (unsigned i = 0; i < length; ++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 AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>&) { Ref<Document> protectorForCacheOwner(m_document); m_notificationPostTimer.stop(); // In DRT, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior // when the notification list is cleared at the end. Instead copy this list at the start. auto notifications = m_notificationsToPost; m_notificationsToPost.clear(); for (const auto& note : notifications) { AccessibilityObject* obj = note.first.get(); if (!obj->axObjectID()) continue; if (!obj->axObjectCache()) continue; #ifndef NDEBUG // Make sure none of the render views are in the process of being layed out. // Notifications should only be sent after the renderer has finished if (obj->isAccessibilityRenderObject()) { AccessibilityRenderObject* renderObj = toAccessibilityRenderObject(obj); RenderObject* renderer = renderObj->renderer(); if (renderer) ASSERT(!renderer->view().layoutState()); } #endif AXNotification notification = note.second; postPlatformNotification(obj, notification); if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored()) childrenChanged(obj->parentObject()); } }
void AccessibilityTable::addChildren() { if (!isAccessibilityTable()) { AccessibilityRenderObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!m_renderer || !m_renderer->isTable()) return; RenderTable* table = toRenderTable(m_renderer); AXObjectCache* axCache = m_renderer->document().axObjectCache(); // Go through all the available sections to pull out the rows and add them as children. table->recalcSectionsIfNeeded(); RenderTableSection* tableSection = table->topSection(); if (!tableSection) return; unsigned maxColumnCount = 0; while (tableSection) { HashSet<AccessibilityObject*> appendedRows; unsigned numRows = tableSection->numRows(); for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex); if (!renderRow) continue; AccessibilityObject* rowObject = axCache->getOrCreate(renderRow); if (!rowObject->isTableRow()) continue; AccessibilityTableRow* row = toAccessibilityTableRow(rowObject); // We need to check every cell for a new row, because cell spans // can cause us to miss rows if we just check the first column. if (appendedRows.contains(row)) continue; row->setRowIndex(static_cast<int>(m_rows.size())); m_rows.append(row); if (!row->accessibilityIsIgnored()) m_children.append(row); #if PLATFORM(GTK) || PLATFORM(EFL) else m_children.appendVector(row->children()); #endif appendedRows.add(row); } maxColumnCount = std::max(tableSection->numColumns(), maxColumnCount); tableSection = table->sectionBelow(tableSection, SkipEmptySections); } // make the columns based on the number of columns in the first body unsigned length = maxColumnCount; for (unsigned i = 0; i < length; ++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); }
AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset) { // Indication that something bogus has transpired. offset = -1; Document* document = referenceObject->document(); if (!document) return 0; Node* focusedNode = referenceObject->selection().end().containerNode(); if (!focusedNode) return 0; RenderObject* focusedRenderer = focusedNode->renderer(); if (!focusedRenderer) return 0; AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer); if (!focusedObject) return 0; // Look for the actual (not ignoring accessibility) selected object. AccessibilityObject* firstUnignoredParent = focusedObject; if (firstUnignoredParent->accessibilityIsIgnored()) firstUnignoredParent = firstUnignoredParent->parentObjectUnignored(); if (!firstUnignoredParent) return 0; // Don't ignore links if the offset is being requested for a link. if (!referenceObject->isLink() && firstUnignoredParent->isLink()) firstUnignoredParent = firstUnignoredParent->parentObjectUnignored(); if (!firstUnignoredParent) return 0; // The reference object must either coincide with the focused // object being considered, or be a descendant of it. if (referenceObject->isDescendantOfObject(firstUnignoredParent)) referenceObject = firstUnignoredParent; Node* startNode = 0; if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) { // We need to use the first child's node of the reference // object as the start point to calculate the caret offset // because we want it to be relative to the object of // reference, not just to the focused object (which could have // previous siblings which should be taken into account too). AccessibilityObject* axFirstChild = referenceObject->firstChild(); if (axFirstChild) startNode = axFirstChild->node(); } // Getting the Position of a PseudoElement now triggers an assertion. // This can occur when clicking on empty space in a render block. if (!startNode || startNode->isPseudoElement()) startNode = firstUnignoredParent->node(); // Check if the node for the first parent object not ignoring // accessibility is null again before using it. This might happen // with certain kind of accessibility objects, such as the root // one (the scroller containing the webArea object). if (!startNode) return 0; VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM); VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd(); if (startPosition == endPosition) offset = 0; else if (!isStartOfLine(endPosition)) { RefPtr<Range> range = makeRange(startPosition, endPosition.previous()); offset = TextIterator::rangeLength(range.get(), true) + 1; } else { RefPtr<Range> range = makeRange(startPosition, endPosition); offset = TextIterator::rangeLength(range.get(), true); } return firstUnignoredParent; }
void AccessibilityTable::addChildren() { if (!isExposableThroughAccessibility()) { AccessibilityRenderObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!is<RenderTable>(m_renderer)) return; RenderTable& table = downcast<RenderTable>(*m_renderer); // Go through all the available sections to pull out the rows and add them as children. table.recalcSectionsIfNeeded(); if (HTMLTableElement* tableElement = this->tableElement()) { if (HTMLTableCaptionElement* caption = tableElement->caption()) { AccessibilityObject* axCaption = axObjectCache()->getOrCreate(caption); if (axCaption && !axCaption->accessibilityIsIgnored()) m_children.append(axCaption); } } unsigned maxColumnCount = 0; RenderTableSection* footer = table.footer(); for (RenderTableSection* tableSection = table.topSection(); tableSection; tableSection = table.sectionBelow(tableSection, SkipEmptySections)) { if (tableSection == footer) continue; addChildrenFromSection(tableSection, maxColumnCount); } // Process the footer last, in case it was ordered earlier in the DOM. if (footer) addChildrenFromSection(footer, maxColumnCount); AXObjectCache* axCache = m_renderer->document().axObjectCache(); // make the columns based on the number of columns in the first body unsigned length = maxColumnCount; for (unsigned i = 0; i < length; ++i) { auto& column = downcast<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); // Sometimes the cell gets the wrong role initially because it is created before the parent // determines whether it is an accessibility table. Iterate all the cells and allow them to // update their roles now that the table knows its status. // see bug: https://bugs.webkit.org/show_bug.cgi?id=147001 for (const auto& row : m_rows) { for (const auto& cell : row->children()) cell->updateAccessibilityRole(); } }