WebKitAccessibleUtil.cpp [plain text]
#include "config.h"
#include "WebKitAccessibleUtil.h"
#if ENABLE(ACCESSIBILITY)
#include "AXObjectCache.h"
#include "AccessibilityObject.h"
#include "FrameView.h"
#include "IntRect.h"
#include "Node.h"
#include "Range.h"
#include "RenderObject.h"
#include "TextIterator.h"
#include "VisibleSelection.h"
#include <wtf/text/AtomString.h>
#include <wtf/text/CString.h>
using namespace WebCore;
AtkAttributeSet* addToAtkAttributeSet(AtkAttributeSet* attributeSet, const char* name, const char* value)
{
AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
attribute->name = g_strdup(name);
attribute->value = g_strdup(value);
attributeSet = g_slist_prepend(attributeSet, attribute);
return attributeSet;
}
void contentsRelativeToAtkCoordinateType(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width, gint* height)
{
FrameView* frameView = coreObject->documentFrameView();
if (frameView) {
switch (coordType) {
case ATK_XY_WINDOW:
rect = frameView->contentsToWindow(rect);
break;
case ATK_XY_SCREEN:
rect = frameView->contentsToScreen(rect);
break;
#if ATK_CHECK_VERSION(2, 30, 0)
case ATK_XY_PARENT:
RELEASE_ASSERT_NOT_REACHED();
#endif
}
}
if (x)
*x = rect.x();
if (y)
*y = rect.y();
if (width)
*width = rect.width();
if (height)
*height = rect.height();
}
static bool titleTagShouldBeUsedInDescriptionField(AccessibilityObject* coreObject)
{
return (coreObject->isLink() && !coreObject->isImageMapLink()) || coreObject->isImage();
}
String accessibilityTitle(AccessibilityObject* coreObject)
{
Vector<AccessibilityText> textOrder;
coreObject->accessibilityText(textOrder);
for (const AccessibilityText& text : textOrder) {
if (text.textSource == AccessibilityTextSource::Visible || text.textSource == AccessibilityTextSource::Children)
return text.text;
if (text.textSource == AccessibilityTextSource::LabelByElement && !coreObject->exposesTitleUIElement())
return text.text;
if (coreObject->roleValue() == AccessibilityRole::Toolbar && text.textSource == AccessibilityTextSource::Alternative)
return text.text;
if (text.textSource == AccessibilityTextSource::TitleTag && !titleTagShouldBeUsedInDescriptionField(coreObject))
return text.text;
}
return String();
}
String accessibilityDescription(AccessibilityObject* coreObject)
{
Vector<AccessibilityText> textOrder;
coreObject->accessibilityText(textOrder);
bool visibleTextAvailable = false;
for (const AccessibilityText& text : textOrder) {
if (text.textSource == AccessibilityTextSource::Alternative)
return text.text;
switch (text.textSource) {
case AccessibilityTextSource::Visible:
case AccessibilityTextSource::Children:
case AccessibilityTextSource::LabelByElement:
visibleTextAvailable = true;
default:
break;
}
if (text.textSource == AccessibilityTextSource::TitleTag && !visibleTextAvailable)
return text.text;
}
return String();
}
bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
{
if (!coreObject || !coreObject->isAccessibilityRenderObject())
return false;
if (selection.isNone())
return false;
RefPtr<Range> range = selection.toNormalizedRange();
if (!range)
return false;
auto& node = *coreObject->node();
auto* lastDescendant = node.lastDescendant();
unsigned lastOffset = lastOffsetInNode(lastDescendant);
auto intersectsResult = range->intersectsNode(node);
return !intersectsResult.hasException()
&& intersectsResult.releaseReturnValue()
&& (&range->endContainer() != &node || range->endOffset())
&& (&range->startContainer() != lastDescendant || range->startOffset() != lastOffset);
}
AXCoreObject* objectFocusedAndCaretOffsetUnignored(AXCoreObject* referenceObject, int& offset)
{
offset = -1;
Document* document = referenceObject->document();
if (!document)
return nullptr;
Node* focusedNode = referenceObject->selection().end().containerNode();
if (!focusedNode)
return nullptr;
RenderObject* focusedRenderer = focusedNode->renderer();
if (!focusedRenderer)
return nullptr;
AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
if (!focusedObject)
return nullptr;
AXCoreObject* firstUnignoredParent = focusedObject;
if (firstUnignoredParent->accessibilityIsIgnored())
firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
if (!firstUnignoredParent)
return nullptr;
if (!referenceObject->isLink() && firstUnignoredParent->isLink()
&& !(firstUnignoredParent->renderer() && !firstUnignoredParent->renderer()->isInline()))
firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
if (!firstUnignoredParent)
return nullptr;
if (referenceObject->isDescendantOfObject(firstUnignoredParent))
referenceObject = firstUnignoredParent;
Node* startNode = nullptr;
if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
AXCoreObject* axFirstChild = referenceObject->firstChild();
if (axFirstChild)
startNode = axFirstChild->node();
}
if (!startNode || startNode->isPseudoElement())
startNode = firstUnignoredParent->node();
if (!startNode)
return nullptr;
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;
}
#endif