Resolve unused parameter warning in FindController.cpp
[WebKit-https.git] / Source / WebCore / accessibility / atk / WebKitAccessibleInterfaceText.cpp
1 /*
2  * Copyright (C) 2008 Nuanti Ltd.
3  * Copyright (C) 2009 Jan Alonzo
4  * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
5  * Copyright (C) 2013 Samsung Electronics. All rights reserved.
6  *
7  * Portions from Mozilla a11y, copyright as follows:
8  *
9  * The Original Code is mozilla.org code.
10  *
11  * The Initial Developer of the Original Code is
12  * Sun Microsystems, Inc.
13  * Portions created by the Initial Developer are Copyright (C) 2002
14  * the Initial Developer. All Rights Reserved.
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Library General Public
18  * License as published by the Free Software Foundation; either
19  * version 2 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Library General Public License for more details.
25  *
26  * You should have received a copy of the GNU Library General Public License
27  * along with this library; see the file COPYING.LIB.  If not, write to
28  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29  * Boston, MA 02110-1301, USA.
30  */
31
32 #include "config.h"
33 #include "WebKitAccessibleInterfaceText.h"
34
35 #if HAVE(ACCESSIBILITY)
36
37 #include "AccessibilityObject.h"
38 #include "Document.h"
39 #include "Font.h"
40 #include "FrameView.h"
41 #include "HostWindow.h"
42 #include "InlineTextBox.h"
43 #include "NotImplemented.h"
44 #include "RenderListItem.h"
45 #include "RenderListMarker.h"
46 #include "RenderText.h"
47 #include "TextEncoding.h"
48 #include "TextIterator.h"
49 #include "VisibleUnits.h"
50 #include "WebKitAccessibleUtil.h"
51 #include "WebKitAccessibleWrapperAtk.h"
52 #include "htmlediting.h"
53 #include <wtf/NotFound.h>
54 #include <wtf/gobject/GOwnPtr.h>
55 #include <wtf/text/CString.h>
56
57 #if PLATFORM(GTK)
58 #include <libgail-util/gail-util.h>
59 #include <pango/pango.h>
60 #endif
61
62 using namespace WebCore;
63
64 static AccessibilityObject* core(AtkText* text)
65 {
66     if (!WEBKIT_IS_ACCESSIBLE(text))
67         return 0;
68
69     return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(text));
70 }
71
72 static gchar* textForRenderer(RenderObject* renderer)
73 {
74     GString* resultText = g_string_new(0);
75
76     if (!renderer)
77         return g_string_free(resultText, FALSE);
78
79     // For RenderBlocks, piece together the text from the RenderText objects they contain.
80     for (RenderObject* object = renderer->firstChild(); object; object = object->nextSibling()) {
81         if (object->isBR()) {
82             g_string_append(resultText, "\n");
83             continue;
84         }
85
86         RenderText* renderText;
87         if (object->isText())
88             renderText = toRenderText(object);
89         else {
90             // List item's markers will be treated in an special way
91             // later on this function, so ignore them here.
92             if (object->isReplaced() && !object->isListMarker())
93                 g_string_append_unichar(resultText, objectReplacementCharacter);
94
95             // We need to check children, if any, to consider when
96             // current object is not a text object but some of its
97             // children are, in order not to miss those portions of
98             // text by not properly handling those situations
99             if (object->firstChild()) {
100                 GOwnPtr<char> objectText(textForRenderer(object));
101                 g_string_append(resultText, objectText.get());
102             }
103             continue;
104         }
105
106         InlineTextBox* box = renderText ? renderText->firstTextBox() : 0;
107         while (box) {
108             // WebCore introduces line breaks in the text that do not reflect
109             // the layout you see on the screen, replace them with spaces.
110             String text = String(renderText->characters(), renderText->textLength()).replace("\n", " ");
111             g_string_append(resultText, text.substring(box->start(), box->end() - box->start() + 1).utf8().data());
112
113             // Newline chars in the source result in separate text boxes, so check
114             // before adding a newline in the layout. See bug 25415 comment #78.
115             // If the next sibling is a BR, we'll add the newline when we examine that child.
116             if (!box->nextOnLineExists() && !(object->nextSibling() && object->nextSibling()->isBR())) {
117                 // If there was a '\n' in the last position of the
118                 // current text box, it would have been converted to a
119                 // space in String::replace(), so remove it first.
120                 if (renderText->characters()[box->end()] == '\n')
121                     g_string_erase(resultText, resultText->len - 1, -1);
122
123                 g_string_append(resultText, "\n");
124             }
125             box = box->nextTextBox();
126         }
127     }
128
129     // Insert the text of the marker for list item in the right place, if present
130     if (renderer->isListItem()) {
131         String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
132         if (renderer->style()->direction() == LTR)
133             g_string_prepend(resultText, markerText.utf8().data());
134         else
135             g_string_append(resultText, markerText.utf8().data());
136     }
137
138     return g_string_free(resultText, FALSE);
139 }
140
141 static gchar* textForObject(const AccessibilityObject* coreObject)
142 {
143     GString* str = g_string_new(0);
144
145     // For text controls, we can get the text line by line.
146     if (coreObject->isTextControl()) {
147         unsigned textLength = coreObject->textLength();
148         int lineNumber = 0;
149         PlainTextRange range = coreObject->doAXRangeForLine(lineNumber);
150         while (range.length) {
151             // When a line of text wraps in a text area, the final space is removed.
152             if (range.start + range.length < textLength)
153                 range.length -= 1;
154             String lineText = coreObject->doAXStringForRange(range);
155             g_string_append(str, lineText.utf8().data());
156             g_string_append(str, "\n");
157             range = coreObject->doAXRangeForLine(++lineNumber);
158         }
159     } else if (coreObject->isAccessibilityRenderObject()) {
160         GOwnPtr<gchar> rendererText(textForRenderer(coreObject->renderer()));
161         g_string_append(str, rendererText.get());
162     }
163
164     return g_string_free(str, FALSE);
165 }
166
167 static gchar* webkitAccessibleTextGetText(AtkText*, gint startOffset, gint endOffset);
168
169 #if PLATFORM(GTK)
170 static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
171 {
172     GailTextUtil* gailTextUtil = gail_text_util_new();
173     GOwnPtr<char> text(webkitAccessibleTextGetText(textObject, 0, -1));
174     gail_text_util_text_setup(gailTextUtil, text.get());
175     return gailTextUtil;
176 }
177
178 static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
179 {
180     AccessibilityObject* coreObject = core(textObject);
181
182     Document* document = coreObject->document();
183     if (!document)
184         return 0;
185
186     HostWindow* hostWindow = document->view()->hostWindow();
187     if (!hostWindow)
188         return 0;
189     PlatformPageClient webView = hostWindow->platformPageClient();
190     if (!webView)
191         return 0;
192
193     // Create a string with the layout as it appears on the screen
194     GOwnPtr<char> objectText(textForObject(coreObject));
195     PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), objectText.get());
196     return layout;
197 }
198 #endif
199
200 static int baselinePositionForRenderObject(RenderObject* renderObject)
201 {
202     // FIXME: This implementation of baselinePosition originates from RenderObject.cpp and was
203     // removed in r70072. The implementation looks incorrect though, because this is not the
204     // baseline of the underlying RenderObject, but of the AccessibilityRenderObject.
205     const FontMetrics& fontMetrics = renderObject->firstLineStyle()->fontMetrics();
206     return fontMetrics.ascent() + (renderObject->firstLineStyle()->computedLineHeight() - fontMetrics.height()) / 2;
207 }
208
209 static AtkAttributeSet* getAttributeSetForAccessibilityObject(const AccessibilityObject* object)
210 {
211     if (!object->isAccessibilityRenderObject())
212         return 0;
213
214     RenderObject* renderer = object->renderer();
215     RenderStyle* style = renderer->style();
216
217     AtkAttributeSet* result = 0;
218     GOwnPtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
219     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_SIZE), buffer.get());
220
221     Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor);
222     if (bgColor.isValid()) {
223         buffer.set(g_strdup_printf("%i,%i,%i", bgColor.red(), bgColor.green(), bgColor.blue()));
224         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_BG_COLOR), buffer.get());
225     }
226
227     Color fgColor = style->visitedDependentColor(CSSPropertyColor);
228     if (fgColor.isValid()) {
229         buffer.set(g_strdup_printf("%i,%i,%i", fgColor.red(), fgColor.green(), fgColor.blue()));
230         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FG_COLOR), buffer.get());
231     }
232
233     int baselinePosition;
234     bool includeRise = true;
235     switch (style->verticalAlign()) {
236     case SUB:
237         baselinePosition = -1 * baselinePositionForRenderObject(renderer);
238         break;
239     case SUPER:
240         baselinePosition = baselinePositionForRenderObject(renderer);
241         break;
242     case BASELINE:
243         baselinePosition = 0;
244         break;
245     default:
246         includeRise = false;
247         break;
248     }
249
250     if (includeRise) {
251         buffer.set(g_strdup_printf("%i", baselinePosition));
252         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_RISE), buffer.get());
253     }
254
255     if (!style->textIndent().isUndefined()) {
256         int indentation = valueForLength(style->textIndent(), object->size().width(), &renderer->view());
257         buffer.set(g_strdup_printf("%i", indentation));
258         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INDENT), buffer.get());
259     }
260
261     String fontFamilyName = style->font().firstFamily();
262     if (fontFamilyName.left(8) == "-webkit-")
263         fontFamilyName = fontFamilyName.substring(8);
264
265     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FAMILY_NAME), fontFamilyName.utf8().data());
266
267     int fontWeight = -1;
268     switch (style->font().weight()) {
269     case FontWeight100:
270         fontWeight = 100;
271         break;
272     case FontWeight200:
273         fontWeight = 200;
274         break;
275     case FontWeight300:
276         fontWeight = 300;
277         break;
278     case FontWeight400:
279         fontWeight = 400;
280         break;
281     case FontWeight500:
282         fontWeight = 500;
283         break;
284     case FontWeight600:
285         fontWeight = 600;
286         break;
287     case FontWeight700:
288         fontWeight = 700;
289         break;
290     case FontWeight800:
291         fontWeight = 800;
292         break;
293     case FontWeight900:
294         fontWeight = 900;
295     }
296     if (fontWeight > 0) {
297         buffer.set(g_strdup_printf("%i", fontWeight));
298         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_WEIGHT), buffer.get());
299     }
300
301     switch (style->textAlign()) {
302     case TASTART:
303     case TAEND:
304         break;
305     case LEFT:
306     case WEBKIT_LEFT:
307         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "left");
308         break;
309     case RIGHT:
310     case WEBKIT_RIGHT:
311         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "right");
312         break;
313     case CENTER:
314     case WEBKIT_CENTER:
315         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "center");
316         break;
317     case JUSTIFY:
318         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "fill");
319     }
320
321     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_UNDERLINE), (style->textDecoration() & TextDecorationUnderline) ? "single" : "none");
322
323     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->font().italic() ? "italic" : "normal");
324
325     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STRIKETHROUGH), (style->textDecoration() & TextDecorationLineThrough) ? "true" : "false");
326
327     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INVISIBLE), (style->visibility() == HIDDEN) ? "true" : "false");
328
329     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->isReadOnly() ? "false" : "true");
330
331     String language = object->language();
332     if (!language.isEmpty())
333         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE), language.utf8().data());
334
335     return result;
336 }
337
338 static gint compareAttribute(const AtkAttribute* a, const AtkAttribute* b)
339 {
340     return g_strcmp0(a->name, b->name) || g_strcmp0(a->value, b->value);
341 }
342
343 // Returns an AtkAttributeSet with the elements of attributeSet1 which
344 // are either not present or different in attributeSet2. Neither
345 // attributeSet1 nor attributeSet2 should be used after calling this.
346 static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* attributeSet1, AtkAttributeSet* attributeSet2)
347 {
348     if (!attributeSet2)
349         return attributeSet1;
350
351     AtkAttributeSet* currentSet = attributeSet1;
352     AtkAttributeSet* found;
353     AtkAttributeSet* toDelete = 0;
354
355     while (currentSet) {
356         found = g_slist_find_custom(attributeSet2, currentSet->data, (GCompareFunc)compareAttribute);
357         if (found) {
358             AtkAttributeSet* nextSet = currentSet->next;
359             toDelete = g_slist_prepend(toDelete, currentSet->data);
360             attributeSet1 = g_slist_delete_link(attributeSet1, currentSet);
361             currentSet = nextSet;
362         } else
363             currentSet = currentSet->next;
364     }
365
366     atk_attribute_set_free(attributeSet2);
367     atk_attribute_set_free(toDelete);
368     return attributeSet1;
369 }
370
371 static guint accessibilityObjectLength(const AccessibilityObject* object)
372 {
373     // Non render objects are not taken into account
374     if (!object->isAccessibilityRenderObject())
375         return 0;
376
377     // For those objects implementing the AtkText interface we use the
378     // well known API to always get the text in a consistent way
379     AtkObject* atkObj = ATK_OBJECT(object->wrapper());
380     if (ATK_IS_TEXT(atkObj)) {
381         GOwnPtr<gchar> text(webkitAccessibleTextGetText(ATK_TEXT(atkObj), 0, -1));
382         return g_utf8_strlen(text.get(), -1);
383     }
384
385     // Even if we don't expose list markers to Assistive
386     // Technologies, we need to have a way to measure their length
387     // for those cases when it's needed to take it into account
388     // separately (as in getAccessibilityObjectForOffset)
389     RenderObject* renderer = object->renderer();
390     if (renderer && renderer->isListMarker()) {
391         RenderListMarker* marker = toRenderListMarker(renderer);
392         return marker->text().length() + marker->suffix().length();
393     }
394
395     return 0;
396 }
397
398 static const AccessibilityObject* getAccessibilityObjectForOffset(const AccessibilityObject* object, guint offset, gint* startOffset, gint* endOffset)
399 {
400     const AccessibilityObject* result;
401     guint length = accessibilityObjectLength(object);
402     if (length > offset) {
403         *startOffset = 0;
404         *endOffset = length;
405         result = object;
406     } else {
407         *startOffset = -1;
408         *endOffset = -1;
409         result = 0;
410     }
411
412     if (!object->firstChild())
413         return result;
414
415     AccessibilityObject* child = object->firstChild();
416     guint currentOffset = 0;
417     guint childPosition = 0;
418     while (child && currentOffset <= offset) {
419         guint childLength = accessibilityObjectLength(child);
420         currentOffset = childLength + childPosition;
421         if (currentOffset > offset) {
422             gint childStartOffset;
423             gint childEndOffset;
424             const AccessibilityObject* grandChild = getAccessibilityObjectForOffset(child, offset-childPosition,  &childStartOffset, &childEndOffset);
425             if (childStartOffset >= 0) {
426                 *startOffset = childStartOffset + childPosition;
427                 *endOffset = childEndOffset + childPosition;
428                 result = grandChild;
429             }
430         } else {
431             childPosition += childLength;
432             child = child->nextSibling();
433         }
434     }
435     return result;
436 }
437
438 static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset)
439 {
440     const AccessibilityObject* child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset);
441     if (!child) {
442         *startOffset = -1;
443         *endOffset = -1;
444         return 0;
445     }
446
447     AtkAttributeSet* defaultAttributes = getAttributeSetForAccessibilityObject(element);
448     AtkAttributeSet* childAttributes = getAttributeSetForAccessibilityObject(child);
449
450     return attributeSetDifference(childAttributes, defaultAttributes);
451 }
452
453 static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoordType coords)
454 {
455     GOwnPtr<char> textContent(webkitAccessibleTextGetText(text, startOffset, -1));
456     gint textLength = g_utf8_strlen(textContent.get(), -1);
457
458     // The first case (endOffset of -1) should work, but seems broken for all Gtk+ apps.
459     gint rangeLength = length;
460     if (rangeLength < 0 || rangeLength > textLength)
461         rangeLength = textLength;
462     AccessibilityObject* coreObject = core(text);
463
464     IntRect extents = coreObject->doAXBoundsForRange(PlainTextRange(startOffset, rangeLength));
465     switch (coords) {
466     case ATK_XY_SCREEN:
467         if (Document* document = coreObject->document())
468             extents = document->view()->contentsToScreen(extents);
469         break;
470     case ATK_XY_WINDOW:
471         // No-op
472         break;
473     }
474
475     return extents;
476 }
477
478 static int offsetAdjustmentForListItem(const AccessibilityObject* object)
479 {
480     // We need to adjust the offsets for the list item marker in
481     // Left-To-Right text, since we expose it together with the text.
482     RenderObject* renderer = object->renderer();
483     if (renderer && renderer->isListItem() && renderer->style()->direction() == LTR)
484         return toRenderListItem(renderer)->markerTextWithSuffix().length();
485
486     return 0;
487 }
488
489 static int webCoreOffsetToAtkOffset(const AccessibilityObject* object, int offset)
490 {
491     if (!object->isListItem())
492         return offset;
493
494     return offset + offsetAdjustmentForListItem(object);
495 }
496
497 static int atkOffsetToWebCoreOffset(AtkText* text, int offset)
498 {
499     AccessibilityObject* coreObject = core(text);
500     if (!coreObject || !coreObject->isListItem())
501         return offset;
502
503     return offset - offsetAdjustmentForListItem(coreObject);
504 }
505
506 static Node* getNodeForAccessibilityObject(AccessibilityObject* coreObject)
507 {
508     if (!coreObject->isNativeTextControl())
509         return coreObject->node();
510
511     // For text controls, we get the first visible position on it (which will
512     // belong to its inner element, unreachable from the DOM) and return its
513     // parent node, so we have a "bounding node" for the accessibility object.
514     VisiblePosition positionInTextControlInnerElement = coreObject->visiblePositionForIndex(0, true);
515     Node* innerMostNode = positionInTextControlInnerElement.deepEquivalent().anchorNode();
516     if (!innerMostNode)
517         return 0;
518
519     return innerMostNode->parentNode();
520 }
521
522 static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, VisibleSelection& selection, gint& startOffset, gint& endOffset)
523 {
524     // Default values, unless the contrary is proved.
525     startOffset = 0;
526     endOffset = 0;
527
528     Node* node = getNodeForAccessibilityObject(coreObject);
529     if (!node)
530         return;
531
532     if (selection.isNone())
533         return;
534
535     // We need to limit our search to positions that fall inside the domain of the current object.
536     Position firstValidPosition = firstPositionInOrBeforeNode(node->firstDescendant());
537     Position lastValidPosition = lastPositionInOrAfterNode(node->lastDescendant());
538
539     // Early return with proper values if the selection falls entirely out of the object.
540     if (!selectionBelongsToObject(coreObject, selection)) {
541         startOffset = comparePositions(selection.start(), firstValidPosition) < 0 ? 0 : accessibilityObjectLength(coreObject);
542         endOffset = startOffset;
543         return;
544     }
545
546     // Find the proper range for the selection that falls inside the object.
547     Position nodeRangeStart = selection.start();
548     if (comparePositions(nodeRangeStart, firstValidPosition) < 0)
549         nodeRangeStart = firstValidPosition;
550
551     Position nodeRangeEnd = selection.end();
552     if (comparePositions(nodeRangeEnd, lastValidPosition) > 0)
553         nodeRangeEnd = lastValidPosition;
554
555     // Calculate position of the selected range inside the object.
556     Position parentFirstPosition = firstPositionInOrBeforeNode(node);
557     RefPtr<Range> rangeInParent = Range::create(&node->document(), parentFirstPosition, nodeRangeStart);
558
559     // Set values for start offsets and calculate initial range length.
560     // These values might be adjusted later to cover special cases.
561     startOffset = webCoreOffsetToAtkOffset(coreObject, TextIterator::rangeLength(rangeInParent.get(), true));
562     RefPtr<Range> nodeRange = Range::create(&node->document(), nodeRangeStart, nodeRangeEnd);
563     int rangeLength = TextIterator::rangeLength(nodeRange.get(), true);
564
565     // Special cases that are only relevant when working with *_END boundaries.
566     if (selection.affinity() == UPSTREAM) {
567         VisiblePosition visibleStart(nodeRangeStart, UPSTREAM);
568         VisiblePosition visibleEnd(nodeRangeEnd, UPSTREAM);
569
570         // We need to adjust offsets when finding wrapped lines so the position at the end
571         // of the line is properly taking into account when calculating the offsets.
572         if (isEndOfLine(visibleStart) && !lineBreakExistsAtVisiblePosition(visibleStart)) {
573             if (isStartOfLine(visibleStart.next()))
574                 rangeLength++;
575
576             if (!isEndOfBlock(visibleStart))
577                 startOffset = std::max(startOffset - 1, 0);
578         }
579
580         if (isEndOfLine(visibleEnd) && !lineBreakExistsAtVisiblePosition(visibleEnd) && !isEndOfBlock(visibleEnd))
581             rangeLength--;
582     }
583
584     endOffset = std::min(startOffset + rangeLength, static_cast<int>(accessibilityObjectLength(coreObject)));
585 }
586
587 static gchar* webkitAccessibleTextGetText(AtkText* text, gint startOffset, gint endOffset)
588 {
589     AccessibilityObject* coreObject = core(text);
590
591     int end = endOffset;
592     if (endOffset == -1) {
593         end = coreObject->stringValue().length();
594         if (!end)
595             end = coreObject->textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)).length();
596     }
597
598     String ret;
599     if (coreObject->isTextControl())
600         ret = coreObject->doAXStringForRange(PlainTextRange(0, endOffset));
601     else {
602         ret = coreObject->stringValue();
603         if (!ret)
604             ret = coreObject->textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren));
605     }
606
607     if (!ret.length()) {
608         // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
609         // In such instances, there may also be embedded objects. The object replacement character
610         // is something ATs want included and we have to account for the fact that it is multibyte.
611         GOwnPtr<char> objectText(textForObject(coreObject));
612         ret = String::fromUTF8(objectText.get());
613         if (!end)
614             end = ret.length();
615     }
616
617     // Prefix a item number/bullet if needed
618     if (coreObject->roleValue() == ListItemRole) {
619         RenderObject* objRenderer = coreObject->renderer();
620         if (objRenderer && objRenderer->isListItem()) {
621             String markerText = toRenderListItem(objRenderer)->markerTextWithSuffix();
622             ret = objRenderer->style()->direction() == LTR ? markerText + ret : ret + markerText;
623             if (endOffset == -1)
624                 end += markerText.length();
625         }
626     }
627
628     ret = ret.substring(startOffset, end - startOffset);
629     return g_strdup(ret.utf8().data());
630 }
631
632 enum GetTextRelativePosition {
633     GetTextPositionAt,
634     GetTextPositionBefore,
635     GetTextPositionAfter
636 };
637
638 // Convenience function to be used in early returns.
639 static char* emptyTextSelectionAtOffset(int offset, int* startOffset, int* endOffset)
640 {
641     *startOffset = offset;
642     *endOffset = offset;
643     return g_strdup("");
644 }
645
646 static char* webkitAccessibleTextGetChar(AtkText* text, int offset, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
647 {
648     int actualOffset = offset;
649     if (textPosition == GetTextPositionBefore)
650         actualOffset--;
651     else if (textPosition == GetTextPositionAfter)
652         actualOffset++;
653
654     GOwnPtr<char> textData(webkitAccessibleTextGetText(text, 0, -1));
655     int textLength = g_utf8_strlen(textData.get(), -1);
656
657     *startOffset = std::max(0, actualOffset);
658     *startOffset = std::min(*startOffset, textLength);
659
660     *endOffset = std::max(0, actualOffset + 1);
661     *endOffset = std::min(*endOffset, textLength);
662
663     if (*startOffset == *endOffset)
664         return g_strdup("");
665
666     return g_utf8_substring(textData.get(), *startOffset, *endOffset);
667 }
668
669 static VisiblePosition nextWordStartPosition(const VisiblePosition &position)
670 {
671     VisiblePosition positionAfterCurrentWord = nextWordPosition(position);
672
673     // In order to skip spaces when moving right, we advance one word further
674     // and then move one word back. This will put us at the beginning of the
675     // word following.
676     VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
677
678     if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
679         positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
680
681     bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(position));
682     if (movingBackwardsMovedPositionToStartOfCurrentWord)
683         positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
684
685     return positionAfterCurrentWord;
686 }
687
688 static VisiblePosition previousWordEndPosition(const VisiblePosition &position)
689 {
690     // We move forward and then backward to position ourselves at the beginning
691     // of the current word for this boundary, making the most of the semantics
692     // of previousWordPosition() and nextWordPosition().
693     VisiblePosition positionAtStartOfCurrentWord = previousWordPosition(nextWordPosition(position));
694     VisiblePosition positionAtPreviousWord = previousWordPosition(position);
695
696     // Need to consider special cases (punctuation) when we are in the last word of a sentence.
697     if (isStartOfWord(position) && positionAtPreviousWord != position && positionAtPreviousWord == positionAtStartOfCurrentWord)
698         return nextWordPosition(positionAtStartOfCurrentWord);
699
700     // In order to skip spaces when moving left, we advance one word backwards
701     // and then move one word forward. This will put us at the beginning of
702     // the word following.
703     VisiblePosition positionBeforeSpacingAndPreceedingWord = previousWordPosition(positionAtStartOfCurrentWord);
704
705     if (positionBeforeSpacingAndPreceedingWord != positionAtStartOfCurrentWord)
706         positionAtStartOfCurrentWord = nextWordPosition(positionBeforeSpacingAndPreceedingWord);
707
708     bool movingForwardMovedPositionToEndOfCurrentWord = nextWordPosition(positionAtStartOfCurrentWord) == previousWordPosition(nextWordPosition(position));
709     if (movingForwardMovedPositionToEndOfCurrentWord)
710         positionAtStartOfCurrentWord = positionBeforeSpacingAndPreceedingWord;
711
712     return positionAtStartOfCurrentWord;
713 }
714
715 static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject* /*coreObject*/, const VisiblePosition& position, AtkTextBoundary boundaryType)
716 {
717     VisiblePosition startPosition;
718     VisiblePosition endPosition;
719
720     switch (boundaryType) {
721     case ATK_TEXT_BOUNDARY_WORD_START:
722         // isStartOfWord() returns true both when at the beginning of a "real" word
723         // as when at the beginning of a whitespace range between two "real" words,
724         // since that whitespace is considered a "word" as well. And in case we are
725         // already at the beginning of a "real" word we do not need to look backwards.
726         if (isStartOfWord(position) && isWhitespace(position.characterBefore()))
727             startPosition = position;
728         else
729             startPosition = previousWordPosition(position);
730         endPosition = nextWordStartPosition(startPosition);
731
732         // We need to make sure that we look for the word in the current line when
733         // we are at the beginning of a new line, and not look into the previous one
734         // at all, which might happen when lines belong to different nodes.
735         if (isStartOfLine(position) && isStartOfLine(endPosition)) {
736             startPosition = endPosition;
737             endPosition = nextWordStartPosition(startPosition);
738         }
739         break;
740
741     case ATK_TEXT_BOUNDARY_WORD_END:
742         startPosition = previousWordEndPosition(position);
743         endPosition = nextWordPosition(startPosition);
744         break;
745
746     default:
747         ASSERT_NOT_REACHED();
748     }
749
750     VisibleSelection selectedWord(startPosition, endPosition);
751
752     // We mark the selection as 'upstream' so we can use that information later,
753     // when finding the actual offsets in getSelectionOffsetsForObject().
754     if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END)
755         selectedWord.setAffinity(UPSTREAM);
756
757     return selectedWord;
758 }
759
760 static int numberOfReplacedElementsBeforeOffset(AtkText* text, unsigned offset)
761 {
762     GOwnPtr<char> textForObject(webkitAccessibleTextGetText(text, 0, offset));
763     String textBeforeOffset = String::fromUTF8(textForObject.get());
764
765     int count = 0;
766     size_t index = textBeforeOffset.find(objectReplacementCharacter, 0);
767     while (index < offset && index != WTF::notFound) {
768         index = textBeforeOffset.find(objectReplacementCharacter, index + 1);
769         count++;
770     }
771     return count;
772 }
773
774 static char* webkitAccessibleTextGetWordForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
775 {
776     AccessibilityObject* coreObject = core(text);
777     Document* document = coreObject->document();
778     if (!document)
779         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
780
781     Node* node = getNodeForAccessibilityObject(coreObject);
782     if (!node)
783         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
784
785     int actualOffset = atkOffsetToWebCoreOffset(text, offset);
786
787     // Besides of the usual conversion from ATK offsets to WebCore offsets,
788     // we need to consider the potential embedded objects that might have been
789     // inserted in the text exposed through AtkText when calculating the offset.
790     actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
791
792     VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
793     VisibleSelection currentWord = wordAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
794
795     // Take into account other relative positions, if needed, by
796     // calculating the new position that we would need to consider.
797     VisiblePosition newPosition = caretPosition;
798     switch (textPosition) {
799     case GetTextPositionAt:
800         break;
801
802     case GetTextPositionBefore:
803         // Early return if asking for the previous word while already at the beginning.
804         if (isFirstVisiblePositionInNode(currentWord.visibleStart(), node))
805             return emptyTextSelectionAtOffset(0, startOffset, endOffset);
806
807         if (isStartOfLine(currentWord.end()))
808             newPosition = currentWord.visibleStart().previous();
809         else
810             newPosition = startOfWord(currentWord.start(), LeftWordIfOnBoundary);
811         break;
812
813     case GetTextPositionAfter:
814         // Early return if asking for the following word while already at the end.
815         if (isLastVisiblePositionInNode(currentWord.visibleEnd(), node))
816             return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
817
818         if (isEndOfLine(currentWord.end()))
819             newPosition = currentWord.visibleEnd().next();
820         else
821             newPosition = endOfWord(currentWord.end(), RightWordIfOnBoundary);
822         break;
823
824     default:
825         ASSERT_NOT_REACHED();
826     }
827
828     // Determine the relevant word we are actually interested in
829     // and calculate the ATK offsets for it, then return everything.
830     VisibleSelection selectedWord = newPosition != caretPosition ? wordAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentWord;
831     getSelectionOffsetsForObject(coreObject, selectedWord, *startOffset, *endOffset);
832     return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
833 }
834
835 static gchar* webkitAccessibleTextGetTextForOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset)
836 {
837     AccessibilityObject* coreObject = core(text);
838     if (!coreObject || !coreObject->isAccessibilityRenderObject())
839         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
840
841     if (boundaryType == ATK_TEXT_BOUNDARY_CHAR)
842         return webkitAccessibleTextGetChar(text, offset, textPosition, startOffset, endOffset);
843
844     if (boundaryType == ATK_TEXT_BOUNDARY_WORD_START || boundaryType == ATK_TEXT_BOUNDARY_WORD_END)
845         return webkitAccessibleTextGetWordForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
846
847 #if PLATFORM(GTK)
848     // FIXME: Get rid of the code below once every single part above
849     // has been properly implemented without using Pango/Cairo.
850     GailOffsetType offsetType = GAIL_AT_OFFSET;
851     switch (textPosition) {
852     case GetTextPositionBefore:
853         offsetType = GAIL_BEFORE_OFFSET;
854         break;
855
856     case GetTextPositionAt:
857         break;
858
859     case GetTextPositionAfter:
860         offsetType = GAIL_AFTER_OFFSET;
861         break;
862
863     default:
864         ASSERT_NOT_REACHED();
865     }
866
867     // Make sure we always return valid valid values for offsets.
868     *startOffset = 0;
869     *endOffset = 0;
870
871     return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), offsetType, boundaryType, offset, startOffset, endOffset);
872 #endif
873
874     notImplemented();
875     return 0;
876 }
877
878 static gchar* webkitAccessibleTextGetTextAfterOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
879 {
880     return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAfter, startOffset, endOffset);
881 }
882
883 static gchar* webkitAccessibleTextGetTextAtOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
884 {
885     return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAt, startOffset, endOffset);
886 }
887
888 static gchar* webkitAccessibleTextGetTextBeforeOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
889 {
890     return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionBefore, startOffset, endOffset);
891 }
892
893 static gunichar webkitAccessibleTextGetCharacterAtOffset(AtkText*, gint)
894 {
895     notImplemented();
896     return 0;
897 }
898
899 static gint webkitAccessibleTextGetCaretOffset(AtkText* text)
900 {
901     // coreObject is the unignored object whose offset the caller is requesting.
902     // focusedObject is the object with the caret. It is likely ignored -- unless it's a link.
903     AccessibilityObject* coreObject = core(text);
904     if (!coreObject->isAccessibilityRenderObject())
905         return 0;
906
907     // We need to make sure we pass a valid object as reference.
908     if (coreObject->accessibilityIsIgnored())
909         coreObject = coreObject->parentObjectUnignored();
910     if (!coreObject)
911         return 0;
912
913     int offset;
914     if (!objectFocusedAndCaretOffsetUnignored(coreObject, offset))
915         return 0;
916
917     return webCoreOffsetToAtkOffset(coreObject, offset);
918 }
919
920 static AtkAttributeSet* webkitAccessibleTextGetRunAttributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset)
921 {
922     AccessibilityObject* coreObject = core(text);
923     AtkAttributeSet* result;
924
925     if (!coreObject) {
926         *startOffset = 0;
927         *endOffset = atk_text_get_character_count(text);
928         return 0;
929     }
930
931     if (offset == -1)
932         offset = atk_text_get_caret_offset(text);
933
934     result = getRunAttributesFromAccesibilityObject(coreObject, offset, startOffset, endOffset);
935
936     if (*startOffset < 0) {
937         *startOffset = offset;
938         *endOffset = offset;
939     }
940
941     return result;
942 }
943
944 static AtkAttributeSet* webkitAccessibleTextGetDefaultAttributes(AtkText* text)
945 {
946     AccessibilityObject* coreObject = core(text);
947     if (!coreObject || !coreObject->isAccessibilityRenderObject())
948         return 0;
949
950     return getAttributeSetForAccessibilityObject(coreObject);
951 }
952
953 static void webkitAccessibleTextGetCharacterExtents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
954 {
955     IntRect extents = textExtents(text, offset, 1, coords);
956     *x = extents.x();
957     *y = extents.y();
958     *width = extents.width();
959     *height = extents.height();
960 }
961
962 static void webkitAccessibleTextGetRangeExtents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect)
963 {
964     IntRect extents = textExtents(text, startOffset, endOffset - startOffset, coords);
965     rect->x = extents.x();
966     rect->y = extents.y();
967     rect->width = extents.width();
968     rect->height = extents.height();
969 }
970
971 static gint webkitAccessibleTextGetCharacterCount(AtkText* text)
972 {
973     return accessibilityObjectLength(core(text));
974 }
975
976 static gint webkitAccessibleTextGetOffsetAtPoint(AtkText* text, gint x, gint y, AtkCoordType)
977 {
978     // FIXME: Use the AtkCoordType
979     // TODO: Is it correct to ignore range.length?
980     IntPoint pos(x, y);
981     PlainTextRange range = core(text)->doAXRangeForPosition(pos);
982     return range.start;
983 }
984
985 static gint webkitAccessibleTextGetNSelections(AtkText* text)
986 {
987     AccessibilityObject* coreObject = core(text);
988     VisibleSelection selection = coreObject->selection();
989
990     // Only range selections are needed for the purpose of this method
991     if (!selection.isRange())
992         return 0;
993
994     // We don't support multiple selections for now, so there's only
995     // two possibilities
996     // Also, we don't want to do anything if the selection does not
997     // belong to the currently selected object. We have to check since
998     // there's no way to get the selection for a given object, only
999     // the global one (the API is a bit confusing)
1000     return selectionBelongsToObject(coreObject, selection) ? 1 : 0;
1001 }
1002
1003 static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
1004 {
1005     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1006     if (selectionNum)
1007         return 0;
1008
1009     // Get the offsets of the selection for the selected object
1010     AccessibilityObject* coreObject = core(text);
1011     VisibleSelection selection = coreObject->selection();
1012     getSelectionOffsetsForObject(coreObject, selection, *startOffset, *endOffset);
1013
1014     // Return 0 instead of "", as that's the expected result for
1015     // this AtkText method when there's no selection
1016     if (*startOffset == *endOffset)
1017         return 0;
1018
1019     return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
1020 }
1021
1022 static gboolean webkitAccessibleTextAddSelection(AtkText*, gint, gint)
1023 {
1024     notImplemented();
1025     return FALSE;
1026 }
1027
1028 static gboolean webkitAccessibleTextSetSelection(AtkText* text, gint selectionNum, gint startOffset, gint endOffset)
1029 {
1030     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1031     if (selectionNum)
1032         return FALSE;
1033
1034     AccessibilityObject* coreObject = core(text);
1035     if (!coreObject->isAccessibilityRenderObject())
1036         return FALSE;
1037
1038     // Consider -1 and out-of-bound values and correct them to length
1039     gint textCount = webkitAccessibleTextGetCharacterCount(text);
1040     if (startOffset < 0 || startOffset > textCount)
1041         startOffset = textCount;
1042     if (endOffset < 0 || endOffset > textCount)
1043         endOffset = textCount;
1044
1045     // We need to adjust the offsets for the list item marker.
1046     int offsetAdjustment = offsetAdjustmentForListItem(coreObject);
1047     if (offsetAdjustment) {
1048         if (startOffset < offsetAdjustment || endOffset < offsetAdjustment)
1049             return FALSE;
1050
1051         startOffset = atkOffsetToWebCoreOffset(text, startOffset);
1052         endOffset = atkOffsetToWebCoreOffset(text, endOffset);
1053     }
1054
1055     PlainTextRange textRange(startOffset, endOffset - startOffset);
1056     VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1057     if (range.isNull())
1058         return FALSE;
1059
1060     coreObject->setSelectedVisiblePositionRange(range);
1061     return TRUE;
1062 }
1063
1064 static gboolean webkitAccessibleTextRemoveSelection(AtkText* text, gint selectionNum)
1065 {
1066     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1067     if (selectionNum)
1068         return FALSE;
1069
1070     // Do nothing if current selection doesn't belong to the object
1071     if (!webkitAccessibleTextGetNSelections(text))
1072         return FALSE;
1073
1074     // Set a new 0-sized selection to the caret position, in order
1075     // to simulate selection removal (GAIL style)
1076     gint caretOffset = webkitAccessibleTextGetCaretOffset(text);
1077     return webkitAccessibleTextSetSelection(text, selectionNum, caretOffset, caretOffset);
1078 }
1079
1080 static gboolean webkitAccessibleTextSetCaretOffset(AtkText* text, gint offset)
1081 {
1082     AccessibilityObject* coreObject = core(text);
1083
1084     if (!coreObject->isAccessibilityRenderObject())
1085         return FALSE;
1086
1087     // We need to adjust the offsets for the list item marker.
1088     int offsetAdjustment = offsetAdjustmentForListItem(coreObject);
1089     if (offsetAdjustment) {
1090         if (offset < offsetAdjustment)
1091             return FALSE;
1092
1093         offset = atkOffsetToWebCoreOffset(text, offset);
1094     }
1095
1096     PlainTextRange textRange(offset, 0);
1097     VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1098     if (range.isNull())
1099         return FALSE;
1100
1101     coreObject->setSelectedVisiblePositionRange(range);
1102     return TRUE;
1103 }
1104
1105 void webkitAccessibleTextInterfaceInit(AtkTextIface* iface)
1106 {
1107     iface->get_text = webkitAccessibleTextGetText;
1108     iface->get_text_after_offset = webkitAccessibleTextGetTextAfterOffset;
1109     iface->get_text_at_offset = webkitAccessibleTextGetTextAtOffset;
1110     iface->get_text_before_offset = webkitAccessibleTextGetTextBeforeOffset;
1111     iface->get_character_at_offset = webkitAccessibleTextGetCharacterAtOffset;
1112     iface->get_caret_offset = webkitAccessibleTextGetCaretOffset;
1113     iface->get_run_attributes = webkitAccessibleTextGetRunAttributes;
1114     iface->get_default_attributes = webkitAccessibleTextGetDefaultAttributes;
1115     iface->get_character_extents = webkitAccessibleTextGetCharacterExtents;
1116     iface->get_range_extents = webkitAccessibleTextGetRangeExtents;
1117     iface->get_character_count = webkitAccessibleTextGetCharacterCount;
1118     iface->get_offset_at_point = webkitAccessibleTextGetOffsetAtPoint;
1119     iface->get_n_selections = webkitAccessibleTextGetNSelections;
1120     iface->get_selection = webkitAccessibleTextGetSelection;
1121     iface->add_selection = webkitAccessibleTextAddSelection;
1122     iface->remove_selection = webkitAccessibleTextRemoveSelection;
1123     iface->set_selection = webkitAccessibleTextSetSelection;
1124     iface->set_caret_offset = webkitAccessibleTextSetCaretOffset;
1125 }
1126
1127 #endif