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 Scrollbar::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) { #if HAVE(ACCESSIBILITY) if (AXObjectCache::accessibilityEnabled()) { if (parent() && parent()->isFrameView()) { Document* document = static_cast<FrameView*>(parent())->frame()->document(); AXObjectCache* cache = document->axObjectCache(); AccessibilityScrollbar* axObject = static_cast<AccessibilityScrollbar*>(cache->getOrCreate(ScrollBarRole)); axObject->setScrollbar(this); cache->postNotification(axObject, document, AXObjectCache::AXValueChanged, true); } } #endif // Ignore perpendicular scrolls. if ((m_orientation == HorizontalScrollbar) ? (direction == ScrollUp || direction == ScrollDown) : (direction == ScrollLeft || direction == ScrollRight)) return false; float step = 0; switch (granularity) { case ScrollByLine: step = m_lineStep; break; case ScrollByPage: step = m_pageStep; break; case ScrollByDocument: step = m_totalSize; break; case ScrollByPixel: step = m_pixelStep; break; } if (direction == ScrollUp || direction == ScrollLeft) multiplier = -multiplier; if (client()) return client()->scroll(m_orientation, granularity, step, multiplier); return setCurrentPos(max(min(m_currentPos + (step * multiplier), static_cast<float>(m_totalSize - m_visibleSize)), 0.0f), NotFromScrollAnimator); }
static AtkObject* accessibilityRootObjectWrapper(AtkObject* atkObject) { if (!AXObjectCache::accessibilityEnabled()) AXObjectCache::enableAccessibility(); WebPageAccessibilityObject* accessible = WEB_PAGE_ACCESSIBILITY_OBJECT(atkObject); if (!accessible->m_page) return 0; Page* corePage = accessible->m_page->corePage(); if (!corePage) return 0; Frame& coreFrame = corePage->mainFrame(); if (!coreFrame.document()) return 0; AXObjectCache* cache = coreFrame.document()->axObjectCache(); if (!cache) return nullptr; AccessibilityObject* coreRootObject = cache->rootObject(); if (!coreRootObject) return 0; AtkObject* rootObject = coreRootObject->wrapper(); if (!rootObject || !ATK_IS_OBJECT(rootObject)) return 0; return rootObject; }
void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&) { if (!AXObjectCache::accessibilityEnabled()) return; if (!m_selection.start().isNotNull() || !m_selection.end().isNotNull()) return; RenderObject* focusedNode = m_selection.end().containerNode()->renderer(); AXObjectCache* cache = m_frame->document()->existingAXObjectCache(); if (!cache) return; AccessibilityObject* accessibilityObject = cache->getOrCreate(focusedNode); if (!accessibilityObject) return; int offset; RefPtr<AccessibilityObject> object = objectFocusedAndCaretOffsetUnignored(accessibilityObject, offset); if (!object) return; emitTextSelectionChange(object.get(), m_selection, offset); maybeEmitTextFocusChange(WTFMove(object)); }
void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos) { // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence. // This also allows callers to check for failure by looking at textMarkerData upon return. memset(&textMarkerData, 0, sizeof(TextMarkerData)); if (visiblePos.isNull()) return; Position deepPos = visiblePos.deepEquivalent(); Node* domNode = deepPos.deprecatedNode(); ASSERT(domNode); if (!domNode) return; if (domNode->isHTMLElement()) { HTMLInputElement* inputElement = domNode->toInputElement(); if (inputElement && inputElement->isPasswordField()) return; } // find or create an accessibility object for this node AXObjectCache* cache = domNode->document()->axObjectCache(); RefPtr<AccessibilityObject> obj = cache->getOrCreate(domNode); textMarkerData.axID = obj.get()->axObjectID(); textMarkerData.node = domNode; textMarkerData.offset = deepPos.deprecatedEditingOffset(); textMarkerData.affinity = visiblePos.affinity(); cache->setNodeInUse(domNode); }
bool AXObject::accessibilityIsIgnored() const { AXObjectCache* cache = axObjectCache(); if (!cache) return true; AXComputedObjectAttributeCache* attributeCache = cache->computedObjectAttributeCache(); if (attributeCache) { AXObjectInclusion ignored = attributeCache->getIgnored(axObjectID()); switch (ignored) { case IgnoreObject: return true; case IncludeObject: return false; case DefaultBehavior: break; } } bool result = computeAccessibilityIsIgnored(); if (attributeCache) attributeCache->setIgnored(axObjectID(), result ? IgnoreObject : IncludeObject); return result; }
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); }
void AccessibilityMenuList::didUpdateActiveOption(int optionIndex) { Ref<Document> document(m_renderer->document()); AXObjectCache* cache = document->axObjectCache(); const auto& childObjects = children(); if (!childObjects.isEmpty()) { ASSERT(childObjects.size() == 1); ASSERT(childObjects[0]->isMenuListPopup()); // We might be calling this method in situations where the renderers for list items // associated to the menu list have not been created (e.g. they might be rendered // in the UI process, as it's the case in the GTK+ port, which uses GtkMenuItem). // So, we need to make sure that the accessibility popup object has some children // before asking it to update its active option, or it will read invalid memory. // You can reproduce the issue in the GTK+ port by removing this check and running // accessibility/insert-selected-option-into-select-causes-crash.html (will crash). int popupChildrenSize = static_cast<int>(childObjects[0]->children().size()); if (childObjects[0]->isMenuListPopup() && optionIndex >= 0 && optionIndex < popupChildrenSize) { if (AccessibilityMenuListPopup* popup = toAccessibilityMenuListPopup(childObjects[0].get())) popup->didUpdateActiveOption(optionIndex); } } cache->postNotification(this, &document.get(), AXObjectCache::AXMenuListValueChanged, TargetElement, PostSynchronously); }
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; }
void SimpleEditCommand::notifyAccessibilityForTextChange(Node* node, AXTextEditType type, const String& text, const VisiblePosition& position) { if (!AXObjectCache::accessibilityEnabled()) return; AXObjectCache* cache = document().existingAXObjectCache(); if (!cache) return; cache->postTextStateChangeNotification(node, type, text, position); }
void AccessibilityMenuListPopup::didUpdateActiveOption(int optionIndex) { ASSERT_ARG(optionIndex, optionIndex >= 0); ASSERT_ARG(optionIndex, optionIndex < static_cast<int>(m_children.size())); AXObjectCache* cache = axObjectCache(); RefPtr<AccessibilityObject> child = m_children[optionIndex].get(); cache->postNotification(child.get(), document(), AXObjectCache::AXFocusedUIElementChanged, TargetElement, PostSynchronously); cache->postNotification(child.get(), document(), AXObjectCache::AXMenuListItemSelected, TargetElement, PostSynchronously); }
void EditCommand::postTextStateChangeNotification(AXTextEditType type, const String& text, const VisiblePosition& position) { if (!AXObjectCache::accessibilityEnabled()) return; if (!text.length()) return; AXObjectCache* cache = document().existingAXObjectCache(); if (!cache) return; Node* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole); cache->postTextStateChangeNotification(node, type, text, position); }
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 TreeScopeAdopter::moveTreeToNewScope(Node& root) const { ASSERT(needsScopeChange()); #if !ENABLE(OILPAN) oldScope().guardRef(); #endif // If an element is moved from a document and then eventually back again the collection cache for // that element may contain stale data as changes made to it will have updated the DOMTreeVersion // of the document it was moved to. By increasing the DOMTreeVersion of the donating document here // we ensure that the collection cache will be invalidated as needed when the element is moved back. Document& oldDocument = oldScope().document(); Document& newDocument = newScope().document(); bool willMoveToNewDocument = oldDocument != newDocument; AXObjectCache* axObjectCache = oldDocument.existingAXObjectCache(); if (willMoveToNewDocument) oldDocument.incDOMTreeVersion(); for (Node& node : NodeTraversal::inclusiveDescendantsOf(root)) { updateTreeScope(node); if (willMoveToNewDocument) { if (axObjectCache) axObjectCache->remove(&node); moveNodeToNewDocument(node, oldDocument, newDocument); } else if (node.hasRareData()) { NodeRareData* rareData = node.rareData(); if (rareData->nodeLists()) rareData->nodeLists()->adoptTreeScope(); } if (!node.isElementNode()) continue; if (node.hasSyntheticAttrChildNodes()) { WillBeHeapVector<RefPtrWillBeMember<Attr>>& attrs = *toElement(node).attrNodeList(); for (unsigned i = 0; i < attrs.size(); ++i) moveTreeToNewScope(*attrs[i]); } for (ShadowRoot* shadow = node.youngestShadowRoot(); shadow; shadow = shadow->olderShadowRoot()) { shadow->setParentTreeScope(newScope()); if (willMoveToNewDocument) moveTreeToNewDocument(*shadow, oldDocument, newDocument); } } #if !ENABLE(OILPAN) oldScope().guardDeref(); #endif }
AccessibilityScrollbar* AccessibilityScrollView::addChildScrollbar(Scrollbar* scrollbar) { if (!scrollbar) return nullptr; AXObjectCache* cache = axObjectCache(); if (!cache) return nullptr; auto& scrollBarObject = downcast<AccessibilityScrollbar>(*cache->getOrCreate(scrollbar)); scrollBarObject.setParent(this); m_children.append(&scrollBarObject); return &scrollBarObject; }
AccessibilityScrollbar* AccessibilityScrollView::addChildScrollbar(Scrollbar* scrollbar) { if (!scrollbar) return nullptr; AXObjectCache* cache = axObjectCache(); if (!cache) return nullptr; AccessibilityScrollbar* scrollBarObject = toAccessibilityScrollbar(cache->getOrCreate(scrollbar)); scrollBarObject->setParent(this); m_children.append(scrollBarObject); return scrollBarObject; }
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); }
AccessibilityObject* AccessibilityScrollView::parentObject() const { if (!is<FrameView>(m_scrollView)) return nullptr; AXObjectCache* cache = axObjectCache(); if (!cache) return nullptr; HTMLFrameOwnerElement* owner = downcast<FrameView>(*m_scrollView).frame().ownerElement(); if (owner && owner->renderer()) return cache->getOrCreate(owner); return nullptr; }
void AccessibilityMenuListPopup::childrenChanged() { AXObjectCache* cache = axObjectCache(); for (size_t i = m_children.size(); i > 0 ; --i) { AccessibilityObject* child = m_children[i - 1].get(); if (child->actionElement() && !child->actionElement()->inRenderedDocument()) { child->detachFromParent(); cache->remove(child->axObjectID()); } } m_children.clear(); m_haveChildren = false; addChildren(); }
AccessibilityObject* AccessibilityScrollView::parentObjectIfExists() const { if (!m_scrollView || !m_scrollView->isFrameView()) return nullptr; AXObjectCache* cache = axObjectCache(); if (!cache) return nullptr; HTMLFrameOwnerElement* owner = toFrameView(m_scrollView)->frame().ownerElement(); if (owner && owner->renderer()) return cache->get(owner); return nullptr; }
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 AccessibilityMenuList::didUpdateActiveOption(int optionIndex) { const AccessibilityChildrenVector& childObjects = children(); if (childObjects.isEmpty()) return; ASSERT(childObjects.size() == 1); ASSERT(childObjects[0]->isMenuListPopup()); RefPtr<Document> document = m_renderer->document(); AXObjectCache* cache = document->axObjectCache(); if (AccessibilityMenuListPopup* popup = static_cast<AccessibilityMenuListPopup*>(childObjects[0].get())) popup->didUpdateActiveOption(optionIndex); cache->postNotification(this, document.get(), AXObjectCache::AXMenuListValueChanged, true, PostSynchronously); }
void AccessibilityMenuList::didUpdateActiveOption(int optionIndex) { Ref<Document> document(m_renderer->document()); AXObjectCache* cache = document->axObjectCache(); const AccessibilityChildrenVector& childObjects = children(); if (!childObjects.isEmpty()) { ASSERT(childObjects.size() == 1); ASSERT(childObjects[0]->isMenuListPopup()); if (childObjects[0]->isMenuListPopup()) { if (AccessibilityMenuListPopup* popup = toAccessibilityMenuListPopup(childObjects[0].get())) popup->didUpdateActiveOption(optionIndex); } } cache->postNotification(this, &document.get(), AXObjectCache::AXMenuListValueChanged, TargetElement, PostSynchronously); }
void AccessibilitySlider::addChildren() { ASSERT(!m_haveChildren); m_haveChildren = true; AXObjectCache* cache = m_renderer->document()->axObjectCache(); AccessibilitySliderThumb* thumb = static_cast<AccessibilitySliderThumb*>(cache->getOrCreate(SliderThumbRole)); thumb->setParent(this); // Before actually adding the value indicator to the hierarchy, // allow the platform to make a final decision about it. if (thumb->accessibilityIsIgnored()) cache->remove(thumb->axObjectID()); else m_children.append(thumb); }
static void notifyAccessibilityStatus(WebKitWebFrame* frame, WebKitLoadStatus loadStatus) { if (loadStatus != WEBKIT_LOAD_PROVISIONAL && loadStatus != WEBKIT_LOAD_FAILED && loadStatus != WEBKIT_LOAD_FINISHED) return; WebKitWebFramePrivate* priv = frame->priv; if (!priv->coreFrame || !priv->coreFrame->document()) return; RenderView* contentRenderer = priv->coreFrame->contentRenderer(); if (!contentRenderer) return; AXObjectCache* axObjectCache = priv->coreFrame->document()->axObjectCache(); if (!axObjectCache) return; AccessibilityObject* coreAxObject = axObjectCache->getOrCreate(contentRenderer); if (!coreAxObject) return; AtkObject* axObject = coreAxObject->wrapper(); if (!axObject || !ATK_IS_DOCUMENT(axObject)) return; switch (loadStatus) { case WEBKIT_LOAD_PROVISIONAL: g_signal_emit_by_name(axObject, "state-change", "busy", true); if (core(frame)->loader()->loadType() == FrameLoadTypeReload) g_signal_emit_by_name(axObject, "reload"); break; case WEBKIT_LOAD_FAILED: g_signal_emit_by_name(axObject, "load-stopped"); g_signal_emit_by_name(axObject, "state-change", "busy", false); break; case WEBKIT_LOAD_FINISHED: g_signal_emit_by_name(axObject, "load-complete"); g_signal_emit_by_name(axObject, "state-change", "busy", false); default: break; } }
void TextFinder::reportFindInPageResultToAccessibility(int identifier) { AXObjectCache* axObjectCache = ownerFrame().frame()->document()->existingAXObjectCache(); if (!axObjectCache) return; AXObject* startObject = axObjectCache->get(m_activeMatch->startContainer()); AXObject* endObject = axObjectCache->get(m_activeMatch->endContainer()); if (!startObject || !endObject) return; WebLocalFrameImpl* mainFrameImpl = ownerFrame().viewImpl()->mainFrameImpl(); if (mainFrameImpl && mainFrameImpl->client()) { mainFrameImpl->client()->handleAccessibilityFindInPageResult( identifier, m_activeMatchIndexInCurrentFrame + 1, WebAXObject(startObject), m_activeMatch->startOffset(), WebAXObject(endObject), m_activeMatch->endOffset()); } }
void WebRemoteFrameImpl::setReplicatedOrigin(const WebSecurityOrigin& origin) const { ASSERT(frame()); frame()->securityContext()->setReplicatedOrigin(origin); // If the origin of a remote frame changed, the accessibility object for the owner // element now points to a different child. // // TODO(dmazzoni, dcheng): there's probably a better way to solve this. // Run SitePerProcessAccessibilityBrowserTest.TwoCrossSiteNavigations to // ensure an alternate fix works. http://crbug.com/566222 FrameOwner* owner = frame()->owner(); if (owner && owner->isLocal()) { HTMLElement* ownerElement = toHTMLFrameOwnerElement(owner); AXObjectCache* cache = ownerElement->document().existingAXObjectCache(); if (cache) cache->childrenChanged(ownerElement); } }
VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData) { VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity); Position deepPos = visiblePos.deepEquivalent(); if (deepPos.isNull()) return VisiblePosition(); RenderObject* renderer = deepPos.node()->renderer(); if (!renderer) return VisiblePosition(); AXObjectCache* cache = renderer->document()->axObjectCache(); if (!cache->isIDinUse(textMarkerData.axID)) return VisiblePosition(); if (deepPos.node() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset) return VisiblePosition(); return visiblePos; }
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 AccessibilityMenuList::addChildren() { m_haveChildren = true; AXObjectCache* cache = m_renderer->document()->axObjectCache(); AccessibilityObject* list = cache->getOrCreate(MenuListPopupRole); if (!list) return; if (list->accessibilityPlatformIncludesObject() == IgnoreObject) { cache->remove(list->axObjectID()); return; } static_cast<AccessibilityMenuListPopup*>(list)->setMenuList(this); m_children.append(list); list->addChildren(); }