Rename AtomicString to AtomString
[WebKit-https.git] / Source / WebCore / accessibility / atk / WebKitAccessibleUtil.cpp
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  * Copyright (C) 2009 Jan Alonzo
4  * Copyright (C) 2009, 2010, 2012 Igalia S.L.
5  *
6  * Portions from Mozilla a11y, copyright as follows:
7  *
8  * The Original Code is mozilla.org code.
9  *
10  * The Initial Developer of the Original Code is
11  * Sun Microsystems, Inc.
12  * Portions created by the Initial Developer are Copyright (C) 2002
13  * the Initial Developer. All Rights Reserved.
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Library General Public
17  * License as published by the Free Software Foundation; either
18  * version 2 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Library General Public License for more details.
24  *
25  * You should have received a copy of the GNU Library General Public License
26  * along with this library; see the file COPYING.LIB.  If not, write to
27  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28  * Boston, MA 02110-1301, USA.
29  */
30
31 #include "config.h"
32 #include "WebKitAccessibleUtil.h"
33
34 #if HAVE(ACCESSIBILITY)
35
36 #include "AXObjectCache.h"
37 #include "AccessibilityObject.h"
38 #include "FrameView.h"
39 #include "IntRect.h"
40 #include "Node.h"
41 #include "Range.h"
42 #include "RenderObject.h"
43 #include "TextIterator.h"
44 #include "VisibleSelection.h"
45
46 #include <wtf/text/AtomString.h>
47 #include <wtf/text/CString.h>
48
49 using namespace WebCore;
50
51 AtkAttributeSet* addToAtkAttributeSet(AtkAttributeSet* attributeSet, const char* name, const char* value)
52 {
53     AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
54     attribute->name = g_strdup(name);
55     attribute->value = g_strdup(value);
56     attributeSet = g_slist_prepend(attributeSet, attribute);
57     return attributeSet;
58 }
59
60 void contentsRelativeToAtkCoordinateType(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width, gint* height)
61 {
62     FrameView* frameView = coreObject->documentFrameView();
63
64     if (frameView) {
65         switch (coordType) {
66         case ATK_XY_WINDOW:
67             rect = frameView->contentsToWindow(rect);
68             break;
69         case ATK_XY_SCREEN:
70             rect = frameView->contentsToScreen(rect);
71             break;
72 #if ATK_CHECK_VERSION(2, 30, 0)
73         case ATK_XY_PARENT:
74             RELEASE_ASSERT_NOT_REACHED();
75 #endif
76         }
77     }
78
79     if (x)
80         *x = rect.x();
81     if (y)
82         *y = rect.y();
83     if (width)
84         *width = rect.width();
85     if (height)
86         *height = rect.height();
87 }
88
89 // FIXME: Different kinds of elements are putting the title tag to use
90 // in different AX fields. This might not be 100% correct but we will
91 // keep it now in order to achieve consistency with previous behavior.
92 static bool titleTagShouldBeUsedInDescriptionField(AccessibilityObject* coreObject)
93 {
94     return (coreObject->isLink() && !coreObject->isImageMapLink()) || coreObject->isImage();
95 }
96
97 // This should be the "visible" text that's actually on the screen if possible.
98 // If there's alternative text, that can override the title.
99 String accessibilityTitle(AccessibilityObject* coreObject)
100 {
101     Vector<AccessibilityText> textOrder;
102     coreObject->accessibilityText(textOrder);
103
104     for (const AccessibilityText& text : textOrder) {
105         // Once we encounter visible text, or the text from our children that should be used foremost.
106         if (text.textSource == AccessibilityTextSource::Visible || text.textSource == AccessibilityTextSource::Children)
107             return text.text;
108
109         // If there's an element that labels this object and it's not exposed, then we should use
110         // that text as our title.
111         if (text.textSource == AccessibilityTextSource::LabelByElement && !coreObject->exposesTitleUIElement())
112             return text.text;
113
114         // Elements of role AccessibilityRole::Toolbar will return its title as AccessibilityTextSource::Alternative.
115         if (coreObject->roleValue() == AccessibilityRole::Toolbar && text.textSource == AccessibilityTextSource::Alternative)
116             return text.text;
117
118         // FIXME: The title tag is used in certain cases for the title. This usage should
119         // probably be in the description field since it's not "visible".
120         if (text.textSource == AccessibilityTextSource::TitleTag && !titleTagShouldBeUsedInDescriptionField(coreObject))
121             return text.text;
122     }
123
124     return String();
125 }
126
127 String accessibilityDescription(AccessibilityObject* coreObject)
128 {
129     Vector<AccessibilityText> textOrder;
130     coreObject->accessibilityText(textOrder);
131
132     bool visibleTextAvailable = false;
133     for (const AccessibilityText& text : textOrder) {
134         if (text.textSource == AccessibilityTextSource::Alternative)
135             return text.text;
136
137         switch (text.textSource) {
138         case AccessibilityTextSource::Visible:
139         case AccessibilityTextSource::Children:
140         case AccessibilityTextSource::LabelByElement:
141             visibleTextAvailable = true;
142         default:
143             break;
144         }
145
146         if (text.textSource == AccessibilityTextSource::TitleTag && !visibleTextAvailable)
147             return text.text;
148     }
149
150     return String();
151 }
152
153 bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
154 {
155     if (!coreObject || !coreObject->isAccessibilityRenderObject())
156         return false;
157
158     if (selection.isNone())
159         return false;
160
161     RefPtr<Range> range = selection.toNormalizedRange();
162     if (!range)
163         return false;
164
165     // We want to check that both the selection intersects the node
166     // AND that the selection is not just "touching" one of the
167     // boundaries for the selected node. We want to check whether the
168     // node is actually inside the region, at least partially.
169     auto& node = *coreObject->node();
170     auto* lastDescendant = node.lastDescendant();
171     unsigned lastOffset = lastOffsetInNode(lastDescendant);
172     auto intersectsResult = range->intersectsNode(node);
173     return !intersectsResult.hasException()
174         && intersectsResult.releaseReturnValue()
175         && (&range->endContainer() != &node || range->endOffset())
176         && (&range->startContainer() != lastDescendant || range->startOffset() != lastOffset);
177 }
178
179 AccessibilityObject* objectFocusedAndCaretOffsetUnignored(AccessibilityObject* referenceObject, int& offset)
180 {
181     // Indication that something bogus has transpired.
182     offset = -1;
183
184     Document* document = referenceObject->document();
185     if (!document)
186         return nullptr;
187
188     Node* focusedNode = referenceObject->selection().end().containerNode();
189     if (!focusedNode)
190         return nullptr;
191
192     RenderObject* focusedRenderer = focusedNode->renderer();
193     if (!focusedRenderer)
194         return nullptr;
195
196     AccessibilityObject* focusedObject = document->axObjectCache()->getOrCreate(focusedRenderer);
197     if (!focusedObject)
198         return nullptr;
199
200     // Look for the actual (not ignoring accessibility) selected object.
201     AccessibilityObject* firstUnignoredParent = focusedObject;
202     if (firstUnignoredParent->accessibilityIsIgnored())
203         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
204     if (!firstUnignoredParent)
205         return nullptr;
206
207     // Don't ignore links if the offset is being requested for a link
208     // or if the link is a block.
209     if (!referenceObject->isLink() && firstUnignoredParent->isLink()
210         && !(firstUnignoredParent->renderer() && !firstUnignoredParent->renderer()->isInline()))
211         firstUnignoredParent = firstUnignoredParent->parentObjectUnignored();
212     if (!firstUnignoredParent)
213         return nullptr;
214
215     // The reference object must either coincide with the focused
216     // object being considered, or be a descendant of it.
217     if (referenceObject->isDescendantOfObject(firstUnignoredParent))
218         referenceObject = firstUnignoredParent;
219
220     Node* startNode = nullptr;
221     if (firstUnignoredParent != referenceObject || firstUnignoredParent->isTextControl()) {
222         // We need to use the first child's node of the reference
223         // object as the start point to calculate the caret offset
224         // because we want it to be relative to the object of
225         // reference, not just to the focused object (which could have
226         // previous siblings which should be taken into account too).
227         AccessibilityObject* axFirstChild = referenceObject->firstChild();
228         if (axFirstChild)
229             startNode = axFirstChild->node();
230     }
231     // Getting the Position of a PseudoElement now triggers an assertion.
232     // This can occur when clicking on empty space in a render block.
233     if (!startNode || startNode->isPseudoElement())
234         startNode = firstUnignoredParent->node();
235
236     // Check if the node for the first parent object not ignoring
237     // accessibility is null again before using it. This might happen
238     // with certain kind of accessibility objects, such as the root
239     // one (the scroller containing the webArea object).
240     if (!startNode)
241         return nullptr;
242
243     VisiblePosition startPosition = VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
244     VisiblePosition endPosition = firstUnignoredParent->selection().visibleEnd();
245
246     if (startPosition == endPosition)
247         offset = 0;
248     else if (!isStartOfLine(endPosition)) {
249         RefPtr<Range> range = makeRange(startPosition, endPosition.previous());
250         offset = TextIterator::rangeLength(range.get(), true) + 1;
251     } else {
252         RefPtr<Range> range = makeRange(startPosition, endPosition);
253         offset = TextIterator::rangeLength(range.get(), true);
254     }
255
256     return firstUnignoredParent;
257 }
258
259 #endif