AXObject* 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->focusedElement(); if (!focusedNode) focusedNode = focusedDocument; if (focusedNode->hasTagName(areaTag)) return focusedImageMapUIElement(toHTMLAreaElement(focusedNode)); AXObject* obj = focusedNode->document().axObjectCache()->getOrCreate(focusedNode); if (!obj) return 0; if (obj->shouldFocusActiveDescendant()) { if (AXObject* 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; }
AXObject* AXListBox::elementAccessibilityHitTest(const IntPoint& 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 = elementRect(); AXObject* 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); }
AXObject* AXObjectCacheImpl::focusedUIElementForPage(const Page* page) { if (!page->settings().accessibilityEnabled()) return 0; // Cross-process accessibility is not yet implemented. if (!page->focusController().focusedOrMainFrame()->isLocalFrame()) return 0; // get the focused node in the page Document* focusedDocument = toLocalFrame(page->focusController().focusedOrMainFrame())->document(); Node* focusedNode = focusedDocument->focusedElement(); if (!focusedNode) focusedNode = focusedDocument; if (isHTMLAreaElement(*focusedNode)) return focusedImageMapUIElement(toHTMLAreaElement(focusedNode)); AXObject* obj = toAXObjectCacheImpl(focusedNode->document().axObjectCache())->getOrCreate(focusedNode); if (!obj) return 0; if (obj->shouldFocusActiveDescendant()) { if (AXObject* 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; }
void InspectorAccessibilityAgent::getAXNode(ErrorString* errorString, int nodeId, RefPtr<AXNode>& accessibilityNode) { Frame* mainFrame = m_page->mainFrame(); if (!mainFrame->isLocalFrame()) { *errorString = "Can't inspect out of process frames yet"; return; } InspectorDOMAgent* domAgent = toLocalFrame(mainFrame)->instrumentingAgents()->inspectorDOMAgent(); if (!domAgent) { *errorString = "DOM agent must be enabled"; return; } Node* node = domAgent->assertNode(errorString, nodeId); if (!node) return; Document& document = node->document(); OwnPtr<ScopedAXObjectCache> cache = ScopedAXObjectCache::create(document); AXObjectCacheImpl* cacheImpl = toAXObjectCacheImpl(cache->get()); AXObject* axObject = cacheImpl->getOrCreate(node); if (!axObject || axObject->accessibilityIsIgnored()) { accessibilityNode = buildObjectForIgnoredNode(node, axObject, cacheImpl); return; } RefPtr<TypeBuilder::Array<AXProperty>> properties = TypeBuilder::Array<AXProperty>::create(); fillLiveRegionProperties(axObject, properties); fillGlobalStates(axObject, properties); fillWidgetProperties(axObject, properties); fillWidgetStates(axObject, properties); fillRelationships(axObject, properties); accessibilityNode = buildObjectForNode(node, axObject, cacheImpl, properties); }
bool AXScrollView::computeAccessibilityIsIgnored() const { AXObject* webArea = webAreaObject(); if (!webArea) return true; return webArea->accessibilityIsIgnored(); }
AXObject* AXObject::parentObjectUnignored() const { AXObject* parent; for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) { } return parent; }
void AXScrollView::addChildren() { ASSERT(!m_haveChildren); m_haveChildren = true; AXObject* webArea = webAreaObject(); if (webArea && !webArea->accessibilityIsIgnored()) m_children.append(webArea); updateScrollbars(); }
bool AXInlineTextBox::computeAccessibilityIsIgnored(IgnoredReasons* ignoredReasons) const { AXObject* parent = parentObject(); if (!parent) return false; if (!parent->accessibilityIsIgnored()) return false; if (ignoredReasons) parent->computeAccessibilityIsIgnored(ignoredReasons); return true; }
void AXARIAGrid::addChildren() { ASSERT(!m_haveChildren); if (!isAXTable()) { AXRenderObject::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<AXObject*> appendedRows; unsigned columnCount = 0; for (RefPtr<AXObject> 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) { AXTableColumn* column = toAXTableColumn(axCache->getOrCreate(ColumnRole)); column->setColumnIndex((int)i); column->setParent(this); m_columns.append(column); if (!column->accessibilityIsIgnored()) m_children.append(column); } AXObject* headerContainerObject = headerContainer(); if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored()) m_children.append(headerContainerObject); }
AXObject* AXObjectCacheImpl::focusedObject() { if (!accessibilityEnabled()) return 0; // We don't have to return anything if the focused frame is not local; // the remote frame will have its own AXObjectCacheImpl and the focused // object will be sorted out by the browser process. Page* page = m_document->page(); if (!page->focusController().focusedFrame()) return 0; // Get the focused node in the page. Document* focusedDocument = page->focusController().focusedFrame()->document(); Node* focusedNode = focusedDocument->focusedElement(); if (!focusedNode) focusedNode = focusedDocument; // If it's an image map, get the focused link within the image map. if (isHTMLAreaElement(focusedNode)) return focusedImageMapUIElement(toHTMLAreaElement(focusedNode)); // See if there's a page popup, for example a calendar picker. Element* adjustedFocusedElement = focusedDocument->adjustedFocusedElement(); if (isHTMLInputElement(adjustedFocusedElement)) { if (AXObject* axPopup = toHTMLInputElement(adjustedFocusedElement)->popupRootAXObject()) { if (Element* focusedElementInPopup = axPopup->document()->focusedElement()) focusedNode = focusedElementInPopup; } } AXObject* obj = getOrCreate(focusedNode); if (!obj) return 0; if (obj->shouldFocusActiveDescendant()) { if (AXObject* 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; }
void AXListBox::addChildren() { Node* selectNode = m_renderer->node(); if (!selectNode) return; m_haveChildren = true; const Vector<HTMLElement*>& listItems = toHTMLSelectElement(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. AXObject* listOption = listBoxOptionAXObject(listItems[i]); if (listOption && !listOption->accessibilityIsIgnored()) m_children.append(listOption); } }
AXObject* AXObjectCacheImpl::firstAccessibleObjectFromNode(const Node* node) { if (!node) return 0; AXObject* accessibleObject = getOrCreate(node->layoutObject()); while (accessibleObject && accessibleObject->accessibilityIsIgnored()) { node = NodeTraversal::next(*node); while (node && !node->layoutObject()) node = NodeTraversal::nextSkippingChildren(*node); if (!node) return 0; accessibleObject = getOrCreate(node->layoutObject()); } return accessibleObject; }
AXObject* AXObject::firstAccessibleObjectFromNode(const Node* node) { if (!node) return 0; AXObjectCache* cache = node->document().axObjectCache(); AXObject* 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; }
AXObject* AXObjectCacheImpl::getOrCreate(LayoutObject* layoutObject) { if (!layoutObject) return 0; if (AXObject* obj = get(layoutObject)) return obj; AXObject* newObj = createFromRenderer(layoutObject); // Will crash later if we have two objects for the same layoutObject. ASSERT(!get(layoutObject)); getAXID(newObj); m_layoutObjectMapping.set(layoutObject, newObj->axObjectID()); m_objects.set(newObj->axObjectID(), newObj); newObj->init(); newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored()); return newObj; }
AXObject* AXObjectCacheImpl::getOrCreate(AbstractInlineTextBox* inlineTextBox) { if (!inlineTextBox) return 0; if (AXObject* obj = get(inlineTextBox)) return obj; AXObject* newObj = createFromInlineTextBox(inlineTextBox); // Will crash later if we have two objects for the same inlineTextBox. ASSERT(!get(inlineTextBox)); getAXID(newObj); m_inlineTextBoxObjectMapping.set(inlineTextBox, newObj->axObjectID()); m_objects.set(newObj->axObjectID(), newObj); newObj->init(); newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored()); return newObj; }
AXObject* AXObjectCacheImpl::getOrCreate(Node* node) { if (!node) return 0; if (AXObject* obj = get(node)) return obj; // If the node has a layout object, prefer using that as the primary key for the AXObject, // with the exception of an HTMLAreaElement, which is created based on its node. if (node->layoutObject() && !isHTMLAreaElement(node)) return getOrCreate(node->layoutObject()); if (!node->parentElement()) return 0; if (isHTMLHeadElement(node)) return 0; AXObject* newObj = createFromNode(node); // Will crash later if we have two objects for the same node. ASSERT(!get(node)); getAXID(newObj); m_nodeObjectMapping.set(node, newObj->axObjectID()); m_objects.set(newObj->axObjectID(), newObj); newObj->init(); newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored()); if (node->isElementNode()) updateTreeIfElementIdIsAriaOwned(toElement(node)); return newObj; }
void AXTable::addChildren() { ASSERT(!isDetached()); if (!isAXTable()) { AXLayoutObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!m_layoutObject || !m_layoutObject->isTable()) return; LayoutTable* table = toLayoutTable(m_layoutObject); AXObjectCacheImpl& axCache = axObjectCache(); Node* tableNode = table->node(); if (!isHTMLTableElement(tableNode)) return; // Add caption if (HTMLTableCaptionElement* caption = toHTMLTableElement(tableNode)->caption()) { AXObject* captionObject = axCache.getOrCreate(caption); if (captionObject && !captionObject->accessibilityIsIgnored()) m_children.append(captionObject); } // Go through all the available sections to pull out the rows and add them as children. table->recalcSectionsIfNeeded(); LayoutTableSection* tableSection = table->topSection(); if (!tableSection) return; LayoutTableSection* initialTableSection = tableSection; while (tableSection) { HeapHashSet<Member<AXObject>> appendedRows; unsigned numRows = tableSection->numRows(); for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { LayoutTableRow* layoutRow = tableSection->rowLayoutObjectAt(rowIndex); if (!layoutRow) continue; AXObject* rowObject = axCache.getOrCreate(layoutRow); if (!rowObject || !rowObject->isTableRow()) continue; AXTableRow* row = toAXTableRow(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); appendedRows.add(row); } tableSection = table->sectionBelow(tableSection, SkipEmptySections); } // make the columns based on the number of columns in the first body unsigned length = initialTableSection->numEffectiveColumns(); for (unsigned i = 0; i < length; ++i) { AXTableColumn* column = toAXTableColumn(axCache.getOrCreate(ColumnRole)); column->setColumnIndex((int)i); column->setParent(this); m_columns.append(column); if (!column->accessibilityIsIgnored()) m_children.append(column); } AXObject* headerContainerObject = headerContainer(); if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored()) m_children.append(headerContainerObject); }
void AXTable::addChildren() { if (!isAXTable()) { AXRenderObject::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; RenderTableSection* initialTableSection = tableSection; while (tableSection) { HashSet<AXObject*> appendedRows; unsigned numRows = tableSection->numRows(); for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex); if (!renderRow) continue; AXObject* rowObject = axCache->getOrCreate(renderRow); if (!rowObject->isTableRow()) continue; AXTableRow* row = toAXTableRow(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); 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) { AXTableColumn* column = toAXTableColumn(axCache->getOrCreate(ColumnRole)); column->setColumnIndex((int)i); column->setParent(this); m_columns.append(column); if (!column->accessibilityIsIgnored()) m_children.append(column); } AXObject* headerContainerObject = headerContainer(); if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored()) m_children.append(headerContainerObject); }
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) { AXObject* 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->isAXRenderObject()) { AXRenderObject* renderObj = toAXRenderObject(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 AXObjectCacheImpl::notificationPostTimerFired(Timer<AXObjectCacheImpl>*) { RefPtrWillBeRawPtr<Document> protectorForCacheOwner(m_document.get()); m_notificationPostTimer.stop(); unsigned i = 0, count = m_notificationsToPost.size(); for (i = 0; i < count; ++i) { AXObject* obj = m_notificationsToPost[i].first; if (!obj->axObjectID()) continue; if (obj->isDetached()) continue; #if ENABLE(ASSERT) // Make sure none of the layout views are in the process of being layed out. // Notifications should only be sent after the layoutObject has finished if (obj->isAXLayoutObject()) { AXLayoutObject* layoutObj = toAXLayoutObject(obj); LayoutObject* layoutObject = layoutObj->layoutObject(); if (layoutObject && layoutObject->view()) ASSERT(!layoutObject->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(); }