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->renderer()); if (object->accessibilityIsIgnored()) return false; return true; }
static AtkRole webkit_accessible_get_role(AtkObject* object) { AccessibilityObject* AXObject = core(object); if (!AXObject) return ATK_ROLE_UNKNOWN; // WebCore does not seem to have a role for list items if (AXObject->isGroup()) { AccessibilityObject* parent = AXObject->parentObjectUnignored(); if (parent && parent->isList()) return ATK_ROLE_LIST_ITEM; } // WebCore does not know about paragraph role, label role, or section role if (AXObject->isAccessibilityRenderObject()) { Node* node = static_cast<AccessibilityRenderObject*>(AXObject)->renderer()->node(); if (node) { if (node->hasTagName(HTMLNames::pTag)) return ATK_ROLE_PARAGRAPH; if (node->hasTagName(HTMLNames::labelTag)) return ATK_ROLE_LABEL; if (node->hasTagName(HTMLNames::divTag)) return ATK_ROLE_SECTION; } } // Note: Why doesn't WebCore have a password field for this if (AXObject->isPasswordField()) return ATK_ROLE_PASSWORD_TEXT; return atkRole(AXObject->roleValue()); }
static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset) { AccessibilityObject* coreObject = core(text); String ret; unsigned start = startOffset; int length = endOffset - startOffset; if (coreObject->isTextControl()) ret = coreObject->doAXStringForRange(PlainTextRange(start, length)); else ret = coreObject->textUnderElement().substring(start, length); return g_strdup(ret.utf8().data()); }
String AccessibilityObject::language() const { AccessibilityObject* parent = parentObject(); // as a last resort, fall back to the content language specified in the meta tag if (!parent) { Document* doc = document(); if (doc) return doc->contentLanguage(); return String(); } return parent->language(); }
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); } }
static gint webkitAccessibleGetNChildren(AtkObject* object) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0); AccessibilityObject* coreObject = core(object); // Tables should be treated in a different way because rows should // be bypassed when exposing the accessible hierarchy. if (coreObject->isAccessibilityTable()) return getNChildrenForTable(coreObject); return coreObject->children().size(); }
static void modifyAccessibilityValue(AtkObject* axObject, bool increment) { if (!axObject || !WEBKIT_IS_ACCESSIBLE(axObject)) return; AccessibilityObject* coreObject = webkit_accessible_get_accessibility_object(WEBKIT_ACCESSIBLE(axObject)); if (!coreObject) return; if (increment) coreObject->increment(); else coreObject->decrement(); }
void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* object, AXTextChange textChange, unsigned offset, const String& text) { if (!object || text.isEmpty()) return; AccessibilityObject* parentObject = object->parentObjectUnignored(); if (!parentObject) return; AtkObject* wrapper = parentObject->wrapper(); if (!wrapper || !ATK_IS_TEXT(wrapper)) return; Node* node = object->node(); if (!node) return; // Ensure document's layout is up-to-date before using TextIterator. Document& document = node->document(); document.updateLayout(); // Select the right signal to be emitted CString detail; switch (textChange) { case AXObjectCache::AXTextInserted: detail = "text-insert"; break; case AXObjectCache::AXTextDeleted: detail = "text-remove"; break; } String textToEmit = text; unsigned offsetToEmit = offset; // If the object we're emitting the signal from represents a // password field, we will emit the masked text. if (parentObject->isPasswordField()) { String maskedText = parentObject->passwordFieldValue(); textToEmit = maskedText.substring(offset, text.length()); } else { // Consider previous text objects that might be present for // the current accessibility object to ensure we emit the // right offset (e.g. multiline text areas). RefPtr<Range> range = Range::create(document, node->parentNode(), 0, node, 0); offsetToEmit = offset + TextIterator::rangeLength(range.get()); } g_signal_emit_by_name(wrapper, detail.data(), offsetToEmit, textToEmit.length(), textToEmit.utf8().data()); }
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); }
AtkObject* webkitAccessibleTableCellGetTable(AtkTableCell* cell) { g_return_val_if_fail(ATK_TABLE_CELL(cell), nullptr); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(cell), nullptr); AccessibilityObject* axObject = core(cell); if (!axObject || !axObject->isTableCell()) return nullptr; AtkObject* table = atk_object_get_parent(axObject->wrapper()); if (!table || !ATK_IS_TABLE(table)) return nullptr; return ATK_OBJECT(g_object_ref(table)); }
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(); }
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); } }
static gboolean webkitAccessibleHyperlinkActionDoAction(AtkAction* action, gint index) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), FALSE); g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, FALSE); g_return_val_if_fail(!index, FALSE); if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl)) return FALSE; AccessibilityObject* coreObject = core(action); if (!coreObject) return FALSE; return coreObject->performDefaultAction(); }
AccessibilityTable* AccessibilityARIAGridRow::parentTable() const { // The parent table might not be the direct ancestor of the row unfortunately. ARIA states that role="grid" should // only have "row" elements, but if not, we still should handle it gracefully by finding the right table. for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { // The parent table for an ARIA grid row should be an ARIA table. if (is<AccessibilityTable>(*parent)) { AccessibilityTable& tableParent = downcast<AccessibilityTable>(*parent); if (tableParent.isExposableThroughAccessibility() && tableParent.isAriaTable()) return &tableParent; } } return nullptr; }
static gboolean webkitAccessibleSelectionIsChildSelected(AtkSelection* selection, gint index) { g_return_val_if_fail(ATK_SELECTION(selection), FALSE); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(selection), FALSE); AccessibilityObject* coreSelection = core(selection); if (!coreSelection) return FALSE; AccessibilityObject* option = optionFromList(selection, index); if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) return option->isSelected(); return FALSE; }
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); } }
static const gchar* webkitAccessibleHyperlinkActionGetName(AtkAction* action, gint index) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0); g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0); g_return_val_if_fail(!index, 0); if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl)) return 0; AccessibilityObject* coreObject = core(action); if (!coreObject) return 0; return returnString(coreObject->actionVerb()); }
static AtkAttributeSet* webkitAccessibleGetAttributes(AtkObject* object) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), 0); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), 0); AtkAttributeSet* attributeSet = 0; #if PLATFORM(GTK) attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitGtk"); #elif PLATFORM(EFL) attributeSet = addToAtkAttributeSet(attributeSet, "toolkit", "WebKitEfl"); #endif AccessibilityObject* coreObject = core(object); if (!coreObject) return attributeSet; // Hack needed for WebKit2 tests because obtaining an element by its ID // cannot be done from the UIProcess. Assistive technologies have no need // for this information. Node* node = coreObject->node(); if (node && node->isElementNode()) { String id = toElement(node)->getIdAttribute().string(); if (!id.isEmpty()) attributeSet = addToAtkAttributeSet(attributeSet, "html-id", id.utf8().data()); } int headingLevel = coreObject->headingLevel(); if (headingLevel) { String value = String::number(headingLevel); attributeSet = addToAtkAttributeSet(attributeSet, "level", value.utf8().data()); } // Set the 'layout-guess' attribute to help Assistive // Technologies know when an exposed table is not data table. if (coreObject->isAccessibilityTable() && !coreObject->isDataTable()) attributeSet = addToAtkAttributeSet(attributeSet, "layout-guess", "true"); String placeholder = coreObject->placeholderValue(); if (!placeholder.isEmpty()) attributeSet = addToAtkAttributeSet(attributeSet, "placeholder-text", placeholder.utf8().data()); if (coreObject->ariaHasPopup()) attributeSet = addToAtkAttributeSet(attributeSet, "haspopup", "true"); AccessibilitySortDirection sortDirection = coreObject->sortDirection(); if (sortDirection != SortDirectionNone) { // WAI-ARIA spec says to translate the value as is from the attribute. const AtomicString& sortAttribute = coreObject->getAttribute(HTMLNames::aria_sortAttr); attributeSet = addToAtkAttributeSet(attributeSet, "sort", sortAttribute.string().utf8().data()); } return attributeSet; }
static AtkRole webkitAccessibleGetRole(AtkObject* object) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE(object), ATK_ROLE_UNKNOWN); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(object), ATK_ROLE_UNKNOWN); AccessibilityObject* coreObject = core(object); if (!coreObject) return ATK_ROLE_UNKNOWN; // Note: Why doesn't WebCore have a password field for this if (coreObject->isPasswordField()) return ATK_ROLE_PASSWORD_TEXT; return atkRole(coreObject); }
static AtkObject* webkitAccessibleTableGetCaption(AtkTable* table) { g_return_val_if_fail(ATK_TABLE(table), 0); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(table), 0); AccessibilityObject* accTable = core(table); if (accTable->isAccessibilityRenderObject()) { Node* node = accTable->node(); if (node && isHTMLTableElement(node)) { HTMLTableCaptionElement* caption = toHTMLTableElement(node)->caption(); if (caption) return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->element())->wrapper(); } } return 0; }
static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset) { AccessibilityObject* coreObject = core(text); // FIXME: We need to reimplement visiblePositionRangeForRange here // because the actual function checks the offset is within the // boundaries of text().length(), but text() only works for text // controls... VisiblePosition startPosition = coreObject->visiblePositionForIndex(offset); startPosition.setAffinity(DOWNSTREAM); VisiblePosition endPosition = coreObject->visiblePositionForIndex(offset); VisiblePositionRange range = VisiblePositionRange(startPosition, endPosition); coreObject->setSelectedVisiblePositionRange(range); return TRUE; }
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); } }
static AtkObject* webkit_accessible_table_get_row_header(AtkTable* table, gint row) { AccessibilityObject* accTable = core(table); if (accTable->isAccessibilityRenderObject()) { AccessibilityObject::AccessibilityChildrenVector allRowHeaders; static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders); unsigned rowCount = allRowHeaders.size(); for (unsigned k = 0; k < rowCount; ++k) { AccessibilityObject* rowObject = allRowHeaders[k]->parentObject(); if (static_cast<AccessibilityTableRow*>(rowObject)->rowIndex() == row) return allRowHeaders[k]->wrapper(); } } return 0; }
static const gchar* webkit_accessible_get_description(AtkObject* object) { AccessibilityObject* coreObject = core(object); // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here. if (coreObject->roleValue() == TableRole && coreObject->ariaRoleAttribute() == UnknownRole) { Node* node = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node(); if (node && node->isHTMLElement()) { String summary = static_cast<HTMLTableElement*>(node)->summary(); if (!summary.isEmpty()) return returnString(summary); } } return returnString(coreObject->accessibilityDescription()); }
static gint webkit_accessible_get_index_in_parent(AtkObject* object) { AccessibilityObject* coreObject = core(object); AccessibilityObject* parent = coreObject->parentObjectUnignored(); g_return_val_if_fail(parent, 0); AccessibilityObject::AccessibilityChildrenVector children = parent->children(); unsigned count = children.size(); for (unsigned i = 0; i < count; ++i) { if (children[i] == coreObject) return i; } return 0; }
static gboolean webkitAccessibleSelectionClearSelection(AtkSelection* selection) { AccessibilityObject* coreSelection = core(selection); if (!coreSelection) return FALSE; AccessibilityObject::AccessibilityChildrenVector selectedItems; if (coreSelection->isListBox() || coreSelection->isMenuList()) { // Set the list of selected items to an empty list; then verify that it worked. AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection); listBox->setSelectedChildren(selectedItems); listBox->selectedChildren(selectedItems); return !selectedItems.size(); } return FALSE; }
bool AccessibilitySVGElement::inheritsPresentationalRole() const { if (canSetFocusAttribute()) return false; AccessibilityRole role = roleValue(); if (role != AccessibilityRole::SVGTextPath && role != AccessibilityRole::SVGTSpan) return false; for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { if (is<AccessibilityRenderObject>(*parent) && parent->element()->hasTagName(SVGNames::textTag)) return parent->roleValue() == AccessibilityRole::Presentational; } return false; }
static gchar* webkitAccessibleHyperlinkGetURI(AtkHyperlink* link, gint index) { g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0); g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0); // FIXME: Do NOT support more than one instance of an AtkObject // implementing AtkHyperlinkImpl in every instance of AtkHyperLink g_return_val_if_fail(!index, 0); returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0); AccessibilityObject* coreObject = core(link); if (!coreObject || coreObject->url().isNull()) return 0; return g_strdup(coreObject->url().string().utf8().data()); }
AccessibilityObjectPlatformInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const { AccessibilityObject* parent = parentObject(); if (!parent) return DefaultBehavior; // When a list item is made up entirely of children (e.g. paragraphs) // the list item gets ignored. We need it. if (isGroup() && parent->isList()) return IncludeObject; // Entries and password fields have extraneous children which we want to ignore. if (parent->isPasswordField() || parent->isTextControl()) return IgnoreObject; return DefaultBehavior; }
static gint webkitAccessibleTextGetNSelections(AtkText* text) { AccessibilityObject* coreObject = core(text); VisibleSelection selection = coreObject->selection(); // Only range selections are needed for the purpose of this method if (!selection.isRange()) return 0; // We don't support multiple selections for now, so there's only // two possibilities // Also, we don't want to do anything if the selection does not // belong to the currently selected object. We have to check since // there's no way to get the selection for a given object, only // the global one (the API is a bit confusing) return selectionBelongsToObject(coreObject, selection) ? 1 : 0; }