b336b19f62d167805ec8d1864c9c33b598f5ab7a
[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 "HTMLParserIdioms.h"
42 #include "HostWindow.h"
43 #include "InlineTextBox.h"
44 #include "NotImplemented.h"
45 #include "RenderListItem.h"
46 #include "RenderListMarker.h"
47 #include "RenderText.h"
48 #include "TextEncoding.h"
49 #include "TextIterator.h"
50 #include "VisibleUnits.h"
51 #include "WebKitAccessibleUtil.h"
52 #include "WebKitAccessibleWrapperAtk.h"
53 #include "htmlediting.h"
54 #include <wtf/gobject/GOwnPtr.h>
55 #include <wtf/text/CString.h>
56
57 using namespace WebCore;
58
59 // Text attribute to expose the ARIA 'aria-invalid' attribute. Initially initialized
60 // to ATK_TEXT_ATTR_INVALID (which means 'invalid' text attribute'), will later on
61 // hold a reference to the custom registered AtkTextAttribute that we will use.
62 static AtkTextAttribute atkTextAttributeInvalid = ATK_TEXT_ATTR_INVALID;
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->firstChildSlow(); 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->firstChildSlow()) {
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 int baselinePositionForRenderObject(RenderObject* renderObject)
168 {
169     // FIXME: This implementation of baselinePosition originates from RenderObject.cpp and was
170     // removed in r70072. The implementation looks incorrect though, because this is not the
171     // baseline of the underlying RenderObject, but of the AccessibilityRenderObject.
172     const FontMetrics& fontMetrics = renderObject->firstLineStyle()->fontMetrics();
173     return fontMetrics.ascent() + (renderObject->firstLineStyle()->computedLineHeight() - fontMetrics.height()) / 2;
174 }
175
176 static AtkAttributeSet* getAttributeSetForAccessibilityObject(const AccessibilityObject* object)
177 {
178     if (!object->isAccessibilityRenderObject())
179         return 0;
180
181     RenderObject* renderer = object->renderer();
182     RenderStyle* style = renderer->style();
183
184     AtkAttributeSet* result = 0;
185     GOwnPtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
186     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_SIZE), buffer.get());
187
188     Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor);
189     if (bgColor.isValid()) {
190         buffer.set(g_strdup_printf("%i,%i,%i", bgColor.red(), bgColor.green(), bgColor.blue()));
191         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_BG_COLOR), buffer.get());
192     }
193
194     Color fgColor = style->visitedDependentColor(CSSPropertyColor);
195     if (fgColor.isValid()) {
196         buffer.set(g_strdup_printf("%i,%i,%i", fgColor.red(), fgColor.green(), fgColor.blue()));
197         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FG_COLOR), buffer.get());
198     }
199
200     int baselinePosition;
201     bool includeRise = true;
202     switch (style->verticalAlign()) {
203     case SUB:
204         baselinePosition = -1 * baselinePositionForRenderObject(renderer);
205         break;
206     case SUPER:
207         baselinePosition = baselinePositionForRenderObject(renderer);
208         break;
209     case BASELINE:
210         baselinePosition = 0;
211         break;
212     default:
213         includeRise = false;
214         break;
215     }
216
217     if (includeRise) {
218         buffer.set(g_strdup_printf("%i", baselinePosition));
219         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_RISE), buffer.get());
220     }
221
222     if (!style->textIndent().isUndefined()) {
223         int indentation = valueForLength(style->textIndent(), object->size().width(), &renderer->view());
224         buffer.set(g_strdup_printf("%i", indentation));
225         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INDENT), buffer.get());
226     }
227
228     String fontFamilyName = style->font().firstFamily();
229     if (fontFamilyName.left(8) == "-webkit-")
230         fontFamilyName = fontFamilyName.substring(8);
231
232     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FAMILY_NAME), fontFamilyName.utf8().data());
233
234     int fontWeight = -1;
235     switch (style->font().weight()) {
236     case FontWeight100:
237         fontWeight = 100;
238         break;
239     case FontWeight200:
240         fontWeight = 200;
241         break;
242     case FontWeight300:
243         fontWeight = 300;
244         break;
245     case FontWeight400:
246         fontWeight = 400;
247         break;
248     case FontWeight500:
249         fontWeight = 500;
250         break;
251     case FontWeight600:
252         fontWeight = 600;
253         break;
254     case FontWeight700:
255         fontWeight = 700;
256         break;
257     case FontWeight800:
258         fontWeight = 800;
259         break;
260     case FontWeight900:
261         fontWeight = 900;
262     }
263     if (fontWeight > 0) {
264         buffer.set(g_strdup_printf("%i", fontWeight));
265         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_WEIGHT), buffer.get());
266     }
267
268     switch (style->textAlign()) {
269     case TASTART:
270     case TAEND:
271         break;
272     case LEFT:
273     case WEBKIT_LEFT:
274         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "left");
275         break;
276     case RIGHT:
277     case WEBKIT_RIGHT:
278         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "right");
279         break;
280     case CENTER:
281     case WEBKIT_CENTER:
282         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "center");
283         break;
284     case JUSTIFY:
285         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "fill");
286     }
287
288     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_UNDERLINE), (style->textDecoration() & TextDecorationUnderline) ? "single" : "none");
289
290     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->font().italic() ? "italic" : "normal");
291
292     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STRIKETHROUGH), (style->textDecoration() & TextDecorationLineThrough) ? "true" : "false");
293
294     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INVISIBLE), (style->visibility() == HIDDEN) ? "true" : "false");
295
296     result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->isReadOnly() ? "false" : "true");
297
298     String language = object->language();
299     if (!language.isEmpty())
300         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_LANGUAGE), language.utf8().data());
301
302     String invalidStatus = object->invalidStatus().string();
303     if (invalidStatus != "false") {
304         // Register the custom attribute for 'aria-invalid' if not done yet.
305         if (atkTextAttributeInvalid == ATK_TEXT_ATTR_INVALID)
306             atkTextAttributeInvalid = atk_text_attribute_register("invalid");
307
308         result = addToAtkAttributeSet(result, atk_text_attribute_get_name(atkTextAttributeInvalid), invalidStatus.utf8().data());
309     }
310
311     return result;
312 }
313
314 static gint compareAttribute(const AtkAttribute* a, const AtkAttribute* b)
315 {
316     return g_strcmp0(a->name, b->name) || g_strcmp0(a->value, b->value);
317 }
318
319 // Returns an AtkAttributeSet with the elements of attributeSet1 which
320 // are either not present or different in attributeSet2. Neither
321 // attributeSet1 nor attributeSet2 should be used after calling this.
322 static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* attributeSet1, AtkAttributeSet* attributeSet2)
323 {
324     if (!attributeSet2)
325         return attributeSet1;
326
327     AtkAttributeSet* currentSet = attributeSet1;
328     AtkAttributeSet* found;
329     AtkAttributeSet* toDelete = 0;
330
331     while (currentSet) {
332         found = g_slist_find_custom(attributeSet2, currentSet->data, (GCompareFunc)compareAttribute);
333         if (found) {
334             AtkAttributeSet* nextSet = currentSet->next;
335             toDelete = g_slist_prepend(toDelete, currentSet->data);
336             attributeSet1 = g_slist_delete_link(attributeSet1, currentSet);
337             currentSet = nextSet;
338         } else
339             currentSet = currentSet->next;
340     }
341
342     atk_attribute_set_free(attributeSet2);
343     atk_attribute_set_free(toDelete);
344     return attributeSet1;
345 }
346
347 static gchar* webkitAccessibleTextGetText(AtkText*, gint startOffset, gint endOffset);
348
349 static guint accessibilityObjectLength(const AccessibilityObject* object)
350 {
351     // Non render objects are not taken into account
352     if (!object->isAccessibilityRenderObject())
353         return 0;
354
355     // For those objects implementing the AtkText interface we use the
356     // well known API to always get the text in a consistent way
357     AtkObject* atkObj = ATK_OBJECT(object->wrapper());
358     if (ATK_IS_TEXT(atkObj)) {
359         GOwnPtr<gchar> text(webkitAccessibleTextGetText(ATK_TEXT(atkObj), 0, -1));
360         return g_utf8_strlen(text.get(), -1);
361     }
362
363     // Even if we don't expose list markers to Assistive
364     // Technologies, we need to have a way to measure their length
365     // for those cases when it's needed to take it into account
366     // separately (as in getAccessibilityObjectForOffset)
367     RenderObject* renderer = object->renderer();
368     if (renderer && renderer->isListMarker()) {
369         RenderListMarker& marker = toRenderListMarker(*renderer);
370         return marker.text().length() + marker.suffix().length();
371     }
372
373     return 0;
374 }
375
376 static const AccessibilityObject* getAccessibilityObjectForOffset(const AccessibilityObject* object, guint offset, gint* startOffset, gint* endOffset)
377 {
378     const AccessibilityObject* result;
379     guint length = accessibilityObjectLength(object);
380     if (length > offset) {
381         *startOffset = 0;
382         *endOffset = length;
383         result = object;
384     } else {
385         *startOffset = -1;
386         *endOffset = -1;
387         result = 0;
388     }
389
390     if (!object->firstChild())
391         return result;
392
393     AccessibilityObject* child = object->firstChild();
394     guint currentOffset = 0;
395     guint childPosition = 0;
396     while (child && currentOffset <= offset) {
397         guint childLength = accessibilityObjectLength(child);
398         currentOffset = childLength + childPosition;
399         if (currentOffset > offset) {
400             gint childStartOffset;
401             gint childEndOffset;
402             const AccessibilityObject* grandChild = getAccessibilityObjectForOffset(child, offset-childPosition,  &childStartOffset, &childEndOffset);
403             if (childStartOffset >= 0) {
404                 *startOffset = childStartOffset + childPosition;
405                 *endOffset = childEndOffset + childPosition;
406                 result = grandChild;
407             }
408         } else {
409             childPosition += childLength;
410             child = child->nextSibling();
411         }
412     }
413     return result;
414 }
415
416 static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset)
417 {
418     const AccessibilityObject* child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset);
419     if (!child) {
420         *startOffset = -1;
421         *endOffset = -1;
422         return 0;
423     }
424
425     AtkAttributeSet* defaultAttributes = getAttributeSetForAccessibilityObject(element);
426     AtkAttributeSet* childAttributes = getAttributeSetForAccessibilityObject(child);
427
428     return attributeSetDifference(childAttributes, defaultAttributes);
429 }
430
431 static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoordType coords)
432 {
433     GOwnPtr<char> textContent(webkitAccessibleTextGetText(text, startOffset, -1));
434     gint textLength = g_utf8_strlen(textContent.get(), -1);
435
436     // The first case (endOffset of -1) should work, but seems broken for all Gtk+ apps.
437     gint rangeLength = length;
438     if (rangeLength < 0 || rangeLength > textLength)
439         rangeLength = textLength;
440     AccessibilityObject* coreObject = core(text);
441
442     IntRect extents = coreObject->doAXBoundsForRange(PlainTextRange(startOffset, rangeLength));
443     switch (coords) {
444     case ATK_XY_SCREEN:
445         if (Document* document = coreObject->document())
446             extents = document->view()->contentsToScreen(extents);
447         break;
448     case ATK_XY_WINDOW:
449         // No-op
450         break;
451     }
452
453     return extents;
454 }
455
456 static int offsetAdjustmentForListItem(const AccessibilityObject* object)
457 {
458     // We need to adjust the offsets for the list item marker in
459     // Left-To-Right text, since we expose it together with the text.
460     RenderObject* renderer = object->renderer();
461     if (renderer && renderer->isListItem() && renderer->style()->direction() == LTR)
462         return toRenderListItem(renderer)->markerTextWithSuffix().length();
463
464     return 0;
465 }
466
467 static int webCoreOffsetToAtkOffset(const AccessibilityObject* object, int offset)
468 {
469     if (!object->isListItem())
470         return offset;
471
472     return offset + offsetAdjustmentForListItem(object);
473 }
474
475 static int atkOffsetToWebCoreOffset(AtkText* text, int offset)
476 {
477     AccessibilityObject* coreObject = core(text);
478     if (!coreObject || !coreObject->isListItem())
479         return offset;
480
481     return offset - offsetAdjustmentForListItem(coreObject);
482 }
483
484 static Node* getNodeForAccessibilityObject(AccessibilityObject* coreObject)
485 {
486     if (!coreObject->isNativeTextControl())
487         return coreObject->node();
488
489     // For text controls, we get the first visible position on it (which will
490     // belong to its inner element, unreachable from the DOM) and return its
491     // parent node, so we have a "bounding node" for the accessibility object.
492     VisiblePosition positionInTextControlInnerElement = coreObject->visiblePositionForIndex(0, true);
493     Node* innerMostNode = positionInTextControlInnerElement.deepEquivalent().anchorNode();
494     if (!innerMostNode)
495         return 0;
496
497     return innerMostNode->parentNode();
498 }
499
500 static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, VisibleSelection& selection, gint& startOffset, gint& endOffset)
501 {
502     // Default values, unless the contrary is proved.
503     startOffset = 0;
504     endOffset = 0;
505
506     Node* node = getNodeForAccessibilityObject(coreObject);
507     if (!node)
508         return;
509
510     if (selection.isNone())
511         return;
512
513     // We need to limit our search to positions that fall inside the domain of the current object.
514     Position firstValidPosition = firstPositionInOrBeforeNode(node->firstDescendant());
515     Position lastValidPosition = lastPositionInOrAfterNode(node->lastDescendant());
516
517     // Early return with proper values if the selection falls entirely out of the object.
518     if (!selectionBelongsToObject(coreObject, selection)) {
519         startOffset = comparePositions(selection.start(), firstValidPosition) < 0 ? 0 : accessibilityObjectLength(coreObject);
520         endOffset = startOffset;
521         return;
522     }
523
524     // Find the proper range for the selection that falls inside the object.
525     Position nodeRangeStart = selection.start();
526     if (comparePositions(nodeRangeStart, firstValidPosition) < 0)
527         nodeRangeStart = firstValidPosition;
528
529     Position nodeRangeEnd = selection.end();
530     if (comparePositions(nodeRangeEnd, lastValidPosition) > 0)
531         nodeRangeEnd = lastValidPosition;
532
533     // Calculate position of the selected range inside the object.
534     Position parentFirstPosition = firstPositionInOrBeforeNode(node);
535     RefPtr<Range> rangeInParent = Range::create(&node->document(), parentFirstPosition, nodeRangeStart);
536
537     // Set values for start offsets and calculate initial range length.
538     // These values might be adjusted later to cover special cases.
539     startOffset = webCoreOffsetToAtkOffset(coreObject, TextIterator::rangeLength(rangeInParent.get(), true));
540     RefPtr<Range> nodeRange = Range::create(&node->document(), nodeRangeStart, nodeRangeEnd);
541     int rangeLength = TextIterator::rangeLength(nodeRange.get(), true);
542
543     // Special cases that are only relevant when working with *_END boundaries.
544     if (selection.affinity() == UPSTREAM) {
545         VisiblePosition visibleStart(nodeRangeStart, UPSTREAM);
546         VisiblePosition visibleEnd(nodeRangeEnd, UPSTREAM);
547
548         // We need to adjust offsets when finding wrapped lines so the position at the end
549         // of the line is properly taking into account when calculating the offsets.
550         if (isEndOfLine(visibleStart) && !lineBreakExistsAtVisiblePosition(visibleStart)) {
551             if (isStartOfLine(visibleStart.next()))
552                 rangeLength++;
553
554             if (!isEndOfBlock(visibleStart))
555                 startOffset = std::max(startOffset - 1, 0);
556         }
557
558         if (isEndOfLine(visibleEnd) && !lineBreakExistsAtVisiblePosition(visibleEnd) && !isEndOfBlock(visibleEnd))
559             rangeLength--;
560     }
561
562     endOffset = std::min(startOffset + rangeLength, static_cast<int>(accessibilityObjectLength(coreObject)));
563 }
564
565 static gchar* webkitAccessibleTextGetText(AtkText* text, gint startOffset, gint endOffset)
566 {
567     g_return_val_if_fail(ATK_TEXT(text), 0);
568     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
569
570     AccessibilityObject* coreObject = core(text);
571
572     int end = endOffset;
573     if (endOffset == -1) {
574         end = coreObject->stringValue().length();
575         if (!end)
576             end = coreObject->textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)).length();
577     }
578
579     String ret;
580     if (coreObject->isTextControl())
581         ret = coreObject->doAXStringForRange(PlainTextRange(0, endOffset));
582     else {
583         ret = coreObject->stringValue();
584         if (!ret)
585             ret = coreObject->textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren));
586     }
587
588     if (!ret.length()) {
589         // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
590         // In such instances, there may also be embedded objects. The object replacement character
591         // is something ATs want included and we have to account for the fact that it is multibyte.
592         GOwnPtr<char> objectText(textForObject(coreObject));
593         ret = String::fromUTF8(objectText.get());
594         if (!end)
595             end = ret.length();
596     }
597
598     // Prefix a item number/bullet if needed
599     if (coreObject->roleValue() == ListItemRole) {
600         RenderObject* objRenderer = coreObject->renderer();
601         if (objRenderer && objRenderer->isListItem()) {
602             String markerText = toRenderListItem(objRenderer)->markerTextWithSuffix();
603             ret = objRenderer->style()->direction() == LTR ? markerText + ret : ret + markerText;
604             if (endOffset == -1)
605                 end += markerText.length();
606         }
607     }
608
609 #if ENABLE(INPUT_TYPE_COLOR)
610     if (coreObject->roleValue() == ColorWellRole) {
611         int r, g, b;
612         coreObject->colorValue(r, g, b);
613         return g_strdup_printf("rgb %7.5f %7.5f %7.5f 1", r / 255., g / 255., b / 255.);
614     }
615 #endif
616
617     ret = ret.substring(startOffset, end - startOffset);
618     return g_strdup(ret.utf8().data());
619 }
620
621 enum GetTextRelativePosition {
622     GetTextPositionAt,
623     GetTextPositionBefore,
624     GetTextPositionAfter
625 };
626
627 // Convenience function to be used in early returns.
628 static char* emptyTextSelectionAtOffset(int offset, int* startOffset, int* endOffset)
629 {
630     *startOffset = offset;
631     *endOffset = offset;
632     return g_strdup("");
633 }
634
635 static char* webkitAccessibleTextGetChar(AtkText* text, int offset, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
636 {
637     int actualOffset = offset;
638     if (textPosition == GetTextPositionBefore)
639         actualOffset--;
640     else if (textPosition == GetTextPositionAfter)
641         actualOffset++;
642
643     GOwnPtr<char> textData(webkitAccessibleTextGetText(text, 0, -1));
644     int textLength = g_utf8_strlen(textData.get(), -1);
645
646     *startOffset = std::max(0, actualOffset);
647     *startOffset = std::min(*startOffset, textLength);
648
649     *endOffset = std::max(0, actualOffset + 1);
650     *endOffset = std::min(*endOffset, textLength);
651
652     if (*startOffset == *endOffset)
653         return g_strdup("");
654
655     return g_utf8_substring(textData.get(), *startOffset, *endOffset);
656 }
657
658 static VisiblePosition nextWordStartPosition(const VisiblePosition &position)
659 {
660     VisiblePosition positionAfterCurrentWord = nextWordPosition(position);
661
662     // In order to skip spaces when moving right, we advance one word further
663     // and then move one word back. This will put us at the beginning of the
664     // word following.
665     VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
666
667     if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
668         positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
669
670     bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(position));
671     if (movingBackwardsMovedPositionToStartOfCurrentWord)
672         positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
673
674     return positionAfterCurrentWord;
675 }
676
677 static VisiblePosition previousWordEndPosition(const VisiblePosition &position)
678 {
679     // We move forward and then backward to position ourselves at the beginning
680     // of the current word for this boundary, making the most of the semantics
681     // of previousWordPosition() and nextWordPosition().
682     VisiblePosition positionAtStartOfCurrentWord = previousWordPosition(nextWordPosition(position));
683     VisiblePosition positionAtPreviousWord = previousWordPosition(position);
684
685     // Need to consider special cases (punctuation) when we are in the last word of a sentence.
686     if (isStartOfWord(position) && positionAtPreviousWord != position && positionAtPreviousWord == positionAtStartOfCurrentWord)
687         return nextWordPosition(positionAtStartOfCurrentWord);
688
689     // In order to skip spaces when moving left, we advance one word backwards
690     // and then move one word forward. This will put us at the beginning of
691     // the word following.
692     VisiblePosition positionBeforeSpacingAndPreceedingWord = previousWordPosition(positionAtStartOfCurrentWord);
693
694     if (positionBeforeSpacingAndPreceedingWord != positionAtStartOfCurrentWord)
695         positionAtStartOfCurrentWord = nextWordPosition(positionBeforeSpacingAndPreceedingWord);
696
697     bool movingForwardMovedPositionToEndOfCurrentWord = nextWordPosition(positionAtStartOfCurrentWord) == previousWordPosition(nextWordPosition(position));
698     if (movingForwardMovedPositionToEndOfCurrentWord)
699         positionAtStartOfCurrentWord = positionBeforeSpacingAndPreceedingWord;
700
701     return positionAtStartOfCurrentWord;
702 }
703
704 static VisibleSelection wordAtPositionForAtkBoundary(const AccessibilityObject* /*coreObject*/, const VisiblePosition& position, AtkTextBoundary boundaryType)
705 {
706     VisiblePosition startPosition;
707     VisiblePosition endPosition;
708
709     switch (boundaryType) {
710     case ATK_TEXT_BOUNDARY_WORD_START:
711         // isStartOfWord() returns true both when at the beginning of a "real" word
712         // as when at the beginning of a whitespace range between two "real" words,
713         // since that whitespace is considered a "word" as well. And in case we are
714         // already at the beginning of a "real" word we do not need to look backwards.
715         if (isStartOfWord(position) && isWhitespace(position.characterBefore()))
716             startPosition = position;
717         else
718             startPosition = previousWordPosition(position);
719         endPosition = nextWordStartPosition(startPosition);
720
721         // We need to make sure that we look for the word in the current line when
722         // we are at the beginning of a new line, and not look into the previous one
723         // at all, which might happen when lines belong to different nodes.
724         if (isStartOfLine(position) && isStartOfLine(endPosition)) {
725             startPosition = endPosition;
726             endPosition = nextWordStartPosition(startPosition);
727         }
728         break;
729
730     case ATK_TEXT_BOUNDARY_WORD_END:
731         startPosition = previousWordEndPosition(position);
732         endPosition = nextWordPosition(startPosition);
733         break;
734
735     default:
736         ASSERT_NOT_REACHED();
737     }
738
739     VisibleSelection selectedWord(startPosition, endPosition);
740
741     // We mark the selection as 'upstream' so we can use that information later,
742     // when finding the actual offsets in getSelectionOffsetsForObject().
743     if (boundaryType == ATK_TEXT_BOUNDARY_WORD_END)
744         selectedWord.setAffinity(UPSTREAM);
745
746     return selectedWord;
747 }
748
749 static int numberOfReplacedElementsBeforeOffset(AtkText* text, unsigned offset)
750 {
751     GOwnPtr<char> textForObject(webkitAccessibleTextGetText(text, 0, offset));
752     String textBeforeOffset = String::fromUTF8(textForObject.get());
753
754     int count = 0;
755     size_t index = textBeforeOffset.find(objectReplacementCharacter, 0);
756     while (index < offset && index != WTF::notFound) {
757         index = textBeforeOffset.find(objectReplacementCharacter, index + 1);
758         count++;
759     }
760     return count;
761 }
762
763 static char* webkitAccessibleTextWordForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
764 {
765     AccessibilityObject* coreObject = core(text);
766     Document* document = coreObject->document();
767     if (!document)
768         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
769
770     Node* node = getNodeForAccessibilityObject(coreObject);
771     if (!node)
772         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
773
774     int actualOffset = atkOffsetToWebCoreOffset(text, offset);
775
776     // Besides of the usual conversion from ATK offsets to WebCore offsets,
777     // we need to consider the potential embedded objects that might have been
778     // inserted in the text exposed through AtkText when calculating the offset.
779     actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
780
781     VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
782     VisibleSelection currentWord = wordAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
783
784     // Take into account other relative positions, if needed, by
785     // calculating the new position that we would need to consider.
786     VisiblePosition newPosition = caretPosition;
787     switch (textPosition) {
788     case GetTextPositionAt:
789         break;
790
791     case GetTextPositionBefore:
792         // Early return if asking for the previous word while already at the beginning.
793         if (isFirstVisiblePositionInNode(currentWord.visibleStart(), node))
794             return emptyTextSelectionAtOffset(0, startOffset, endOffset);
795
796         if (isStartOfLine(currentWord.end()))
797             newPosition = currentWord.visibleStart().previous();
798         else
799             newPosition = startOfWord(currentWord.start(), LeftWordIfOnBoundary);
800         break;
801
802     case GetTextPositionAfter:
803         // Early return if asking for the following word while already at the end.
804         if (isLastVisiblePositionInNode(currentWord.visibleEnd(), node))
805             return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
806
807         if (isEndOfLine(currentWord.end()))
808             newPosition = currentWord.visibleEnd().next();
809         else
810             newPosition = endOfWord(currentWord.end(), RightWordIfOnBoundary);
811         break;
812
813     default:
814         ASSERT_NOT_REACHED();
815     }
816
817     // Determine the relevant word we are actually interested in
818     // and calculate the ATK offsets for it, then return everything.
819     VisibleSelection selectedWord = newPosition != caretPosition ? wordAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentWord;
820     getSelectionOffsetsForObject(coreObject, selectedWord, *startOffset, *endOffset);
821     return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
822 }
823
824 static bool isSentenceBoundary(const VisiblePosition &pos)
825 {
826     if (pos.isNull())
827         return false;
828
829     // It's definitely a sentence boundary if there's nothing before.
830     if (pos.previous().isNull())
831         return true;
832
833     // We go backwards and forward to make sure about this.
834     VisiblePosition startOfPreviousSentence = startOfSentence(pos);
835     return startOfPreviousSentence.isNotNull() && pos == endOfSentence(startOfPreviousSentence);
836 }
837
838 static bool isWhiteSpaceBetweenSentences(const VisiblePosition& position)
839 {
840     if (position.isNull())
841         return false;
842
843     if (!isWhitespace(position.characterAfter()))
844         return false;
845
846     VisiblePosition startOfWhiteSpace = startOfWord(position, RightWordIfOnBoundary);
847     VisiblePosition endOfWhiteSpace = endOfWord(startOfWhiteSpace, RightWordIfOnBoundary);
848     if (!isSentenceBoundary(startOfWhiteSpace) && !isSentenceBoundary(endOfWhiteSpace))
849         return false;
850
851     return comparePositions(startOfWhiteSpace, position) <= 0 && comparePositions(endOfWhiteSpace, position) >= 0;
852 }
853
854 static VisibleSelection sentenceAtPositionForAtkBoundary(const AccessibilityObject*, const VisiblePosition& position, AtkTextBoundary boundaryType)
855 {
856     VisiblePosition startPosition;
857     VisiblePosition endPosition;
858
859     bool isAtStartOfSentenceForEndBoundary = isWhiteSpaceBetweenSentences(position) || isSentenceBoundary(position);
860     if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_START || !isAtStartOfSentenceForEndBoundary) {
861         startPosition = isSentenceBoundary(position) ? position : startOfSentence(position);
862         // startOfSentence might stop at a linebreak in the HTML source code,
863         // but we don't want to stop there yet, so keep going.
864         while (!isSentenceBoundary(startPosition) && isHTMLLineBreak(startPosition.characterBefore()))
865             startPosition = startOfSentence(startPosition);
866
867         endPosition = endOfSentence(startPosition);
868     }
869
870     if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_END) {
871         if (isAtStartOfSentenceForEndBoundary) {
872             startPosition = position;
873             endPosition = endOfSentence(endOfWord(position, RightWordIfOnBoundary));
874         }
875
876         // startOfSentence returns a position after any white space previous to
877         // the sentence, so we might need to adjust that offset for this boundary.
878         if (isWhitespace(startPosition.characterBefore()))
879             startPosition = startOfWord(startPosition, LeftWordIfOnBoundary);
880
881         // endOfSentence returns a position after any white space after the
882         // sentence, so we might need to adjust that offset for this boundary.
883         if (isWhitespace(endPosition.characterBefore()))
884             endPosition = startOfWord(endPosition, LeftWordIfOnBoundary);
885
886         // Finally, do some additional adjustments that might be needed if
887         // positions are at the start or the end of a line.
888         if (isStartOfLine(startPosition) && !isStartOfBlock(startPosition))
889             startPosition = startPosition.previous();
890         if (isStartOfLine(endPosition) && !isStartOfBlock(endPosition))
891             endPosition = endPosition.previous();
892     }
893
894     VisibleSelection selectedSentence(startPosition, endPosition);
895
896     // We mark the selection as 'upstream' so we can use that information later,
897     // when finding the actual offsets in getSelectionOffsetsForObject().
898     if (boundaryType == ATK_TEXT_BOUNDARY_SENTENCE_END)
899         selectedSentence.setAffinity(UPSTREAM);
900
901     return selectedSentence;
902 }
903
904 static char* webkitAccessibleTextSentenceForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
905 {
906     AccessibilityObject* coreObject = core(text);
907     Document* document = coreObject->document();
908     if (!document)
909         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
910
911     Node* node = getNodeForAccessibilityObject(coreObject);
912     if (!node)
913         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
914
915     int actualOffset = atkOffsetToWebCoreOffset(text, offset);
916
917     // Besides of the usual conversion from ATK offsets to WebCore offsets,
918     // we need to consider the potential embedded objects that might have been
919     // inserted in the text exposed through AtkText when calculating the offset.
920     actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
921
922     VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
923     VisibleSelection currentSentence = sentenceAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
924
925     // Take into account other relative positions, if needed, by
926     // calculating the new position that we would need to consider.
927     VisiblePosition newPosition = caretPosition;
928     switch (textPosition) {
929     case GetTextPositionAt:
930         break;
931
932     case GetTextPositionBefore:
933         // Early return if asking for the previous sentence while already at the beginning.
934         if (isFirstVisiblePositionInNode(currentSentence.visibleStart(), node))
935             return emptyTextSelectionAtOffset(0, startOffset, endOffset);
936         newPosition = currentSentence.visibleStart().previous();
937         break;
938
939     case GetTextPositionAfter:
940         // Early return if asking for the following word while already at the end.
941         if (isLastVisiblePositionInNode(currentSentence.visibleEnd(), node))
942             return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
943         newPosition = currentSentence.visibleEnd().next();
944         break;
945
946     default:
947         ASSERT_NOT_REACHED();
948     }
949
950     // Determine the relevant sentence we are actually interested in
951     // and calculate the ATK offsets for it, then return everything.
952     VisibleSelection selectedSentence = newPosition != caretPosition ? sentenceAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentSentence;
953     getSelectionOffsetsForObject(coreObject, selectedSentence, *startOffset, *endOffset);
954     return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
955 }
956
957 static VisibleSelection lineAtPositionForAtkBoundary(const AccessibilityObject* coreObject, const VisiblePosition& position, AtkTextBoundary boundaryType)
958 {
959     VisiblePosition startPosition;
960     VisiblePosition endPosition;
961
962     switch (boundaryType) {
963     case ATK_TEXT_BOUNDARY_LINE_START:
964         startPosition = isStartOfLine(position) ? position : logicalStartOfLine(position);
965         endPosition = logicalEndOfLine(position);
966
967         // In addition to checking that we are not at the end of a block, we need
968         // to check that endPosition has not UPSTREAM affinity, since that would
969         // cause trouble inside of text controls (we would be advancing too much).
970         if (!isEndOfBlock(endPosition) && endPosition.affinity() != UPSTREAM)
971             endPosition = endPosition.next();
972         break;
973
974     case ATK_TEXT_BOUNDARY_LINE_END:
975         startPosition = isEndOfLine(position) ? position : logicalStartOfLine(position);
976         if (!isStartOfBlock(startPosition))
977             startPosition = startPosition.previous();
978         endPosition = logicalEndOfLine(position);
979         break;
980
981     default:
982         ASSERT_NOT_REACHED();
983     }
984
985     VisibleSelection selectedLine(startPosition, endPosition);
986
987     // We mark the selection as 'upstream' so we can use that information later,
988     // when finding the actual offsets in getSelectionOffsetsForObject().
989     if (boundaryType == ATK_TEXT_BOUNDARY_LINE_END)
990         selectedLine.setAffinity(UPSTREAM);
991
992     return selectedLine;
993 }
994
995 static char* webkitAccessibleTextLineForBoundary(AtkText* text, int offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, int* startOffset, int* endOffset)
996 {
997     AccessibilityObject* coreObject = core(text);
998     Document* document = coreObject->document();
999     if (!document)
1000         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
1001
1002     Node* node = getNodeForAccessibilityObject(coreObject);
1003     if (!node)
1004         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
1005
1006     int actualOffset = atkOffsetToWebCoreOffset(text, offset);
1007
1008     // Besides the usual conversion from ATK offsets to WebCore offsets,
1009     // we need to consider the potential embedded objects that might have been
1010     // inserted in the text exposed through AtkText when calculating the offset.
1011     actualOffset -= numberOfReplacedElementsBeforeOffset(text, actualOffset);
1012
1013     VisiblePosition caretPosition = coreObject->visiblePositionForIndex(actualOffset);
1014     VisibleSelection currentLine = lineAtPositionForAtkBoundary(coreObject, caretPosition, boundaryType);
1015
1016     // Take into account other relative positions, if needed, by
1017     // calculating the new position that we would need to consider.
1018     VisiblePosition newPosition = caretPosition;
1019     switch (textPosition) {
1020     case GetTextPositionAt:
1021         // No need to do additional work if we are using the "at" position, we just
1022         // explicitly list this case option to catch invalid values in the default case.
1023         break;
1024
1025     case GetTextPositionBefore:
1026         // Early return if asking for the previous line while already at the beginning.
1027         if (isFirstVisiblePositionInNode(currentLine.visibleStart(), node))
1028             return emptyTextSelectionAtOffset(0, startOffset, endOffset);
1029         newPosition = currentLine.visibleStart().previous();
1030         break;
1031
1032     case GetTextPositionAfter:
1033         // Early return if asking for the following word while already at the end.
1034         if (isLastVisiblePositionInNode(currentLine.visibleEnd(), node))
1035             return emptyTextSelectionAtOffset(accessibilityObjectLength(coreObject), startOffset, endOffset);
1036         newPosition = currentLine.visibleEnd().next();
1037         break;
1038
1039     default:
1040         ASSERT_NOT_REACHED();
1041     }
1042
1043     // Determine the relevant line we are actually interested in
1044     // and calculate the ATK offsets for it, then return everything.
1045     VisibleSelection selectedLine = newPosition != caretPosition ? lineAtPositionForAtkBoundary(coreObject, newPosition, boundaryType) : currentLine;
1046     getSelectionOffsetsForObject(coreObject, selectedLine, *startOffset, *endOffset);
1047
1048     // We might need to adjust the start or end offset to include the list item marker,
1049     // if present, when printing the first or the last full line for a list item.
1050     RenderObject* renderer = coreObject->renderer();
1051     if (renderer->isListItem()) {
1052         // For Left-to-Right, the list item marker is at the beginning of the exposed text.
1053         if (renderer->style()->direction() == LTR && isFirstVisiblePositionInNode(selectedLine.visibleStart(), node))
1054             *startOffset = 0;
1055
1056         // For Right-to-Left, the list item marker is at the end of the exposed text.
1057         if (renderer->style()->direction() == RTL && isLastVisiblePositionInNode(selectedLine.visibleEnd(), node))
1058             *endOffset = accessibilityObjectLength(coreObject);
1059     }
1060
1061     return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
1062 }
1063
1064 static gchar* webkitAccessibleTextGetTextForOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, GetTextRelativePosition textPosition, gint* startOffset, gint* endOffset)
1065 {
1066     AccessibilityObject* coreObject = core(text);
1067     if (!coreObject || !coreObject->isAccessibilityRenderObject())
1068         return emptyTextSelectionAtOffset(0, startOffset, endOffset);
1069
1070     switch (boundaryType) {
1071     case ATK_TEXT_BOUNDARY_CHAR:
1072         return webkitAccessibleTextGetChar(text, offset, textPosition, startOffset, endOffset);
1073
1074     case ATK_TEXT_BOUNDARY_WORD_START:
1075     case ATK_TEXT_BOUNDARY_WORD_END:
1076         return webkitAccessibleTextWordForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
1077
1078     case ATK_TEXT_BOUNDARY_LINE_START:
1079     case ATK_TEXT_BOUNDARY_LINE_END:
1080         return webkitAccessibleTextLineForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
1081
1082     case ATK_TEXT_BOUNDARY_SENTENCE_START:
1083     case ATK_TEXT_BOUNDARY_SENTENCE_END:
1084         return webkitAccessibleTextSentenceForBoundary(text, offset, boundaryType, textPosition, startOffset, endOffset);
1085
1086     default:
1087         ASSERT_NOT_REACHED();
1088     }
1089
1090     // This should never be reached.
1091     return 0;
1092 }
1093
1094 static gchar* webkitAccessibleTextGetTextAfterOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1095 {
1096     g_return_val_if_fail(ATK_TEXT(text), 0);
1097     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1098
1099     return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAfter, startOffset, endOffset);
1100 }
1101
1102 static gchar* webkitAccessibleTextGetTextAtOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1103 {
1104     g_return_val_if_fail(ATK_TEXT(text), 0);
1105     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1106
1107     return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionAt, startOffset, endOffset);
1108 }
1109
1110 static gchar* webkitAccessibleTextGetTextBeforeOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
1111 {
1112     g_return_val_if_fail(ATK_TEXT(text), 0);
1113     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1114
1115     return webkitAccessibleTextGetTextForOffset(text, offset, boundaryType, GetTextPositionBefore, startOffset, endOffset);
1116 }
1117
1118 static gunichar webkitAccessibleTextGetCharacterAtOffset(AtkText* text, gint)
1119 {
1120     g_return_val_if_fail(ATK_TEXT(text), 0);
1121     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1122
1123     notImplemented();
1124     return 0;
1125 }
1126
1127 static gint webkitAccessibleTextGetCaretOffset(AtkText* text)
1128 {
1129     g_return_val_if_fail(ATK_TEXT(text), 0);
1130     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1131
1132     // coreObject is the unignored object whose offset the caller is requesting.
1133     // focusedObject is the object with the caret. It is likely ignored -- unless it's a link.
1134     AccessibilityObject* coreObject = core(text);
1135     if (!coreObject->isAccessibilityRenderObject())
1136         return 0;
1137
1138     // We need to make sure we pass a valid object as reference.
1139     if (coreObject->accessibilityIsIgnored())
1140         coreObject = coreObject->parentObjectUnignored();
1141     if (!coreObject)
1142         return 0;
1143
1144     int offset;
1145     if (!objectFocusedAndCaretOffsetUnignored(coreObject, offset))
1146         return 0;
1147
1148     return webCoreOffsetToAtkOffset(coreObject, offset);
1149 }
1150
1151 static AtkAttributeSet* webkitAccessibleTextGetRunAttributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset)
1152 {
1153     g_return_val_if_fail(ATK_TEXT(text), 0);
1154     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1155
1156     AccessibilityObject* coreObject = core(text);
1157     AtkAttributeSet* result;
1158
1159     if (!coreObject) {
1160         *startOffset = 0;
1161         *endOffset = atk_text_get_character_count(text);
1162         return 0;
1163     }
1164
1165     if (offset == -1)
1166         offset = atk_text_get_caret_offset(text);
1167
1168     result = getRunAttributesFromAccesibilityObject(coreObject, offset, startOffset, endOffset);
1169
1170     if (*startOffset < 0) {
1171         *startOffset = offset;
1172         *endOffset = offset;
1173     }
1174
1175     return result;
1176 }
1177
1178 static AtkAttributeSet* webkitAccessibleTextGetDefaultAttributes(AtkText* text)
1179 {
1180     g_return_val_if_fail(ATK_TEXT(text), 0);
1181     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1182
1183     AccessibilityObject* coreObject = core(text);
1184     if (!coreObject || !coreObject->isAccessibilityRenderObject())
1185         return 0;
1186
1187     return getAttributeSetForAccessibilityObject(coreObject);
1188 }
1189
1190 static void webkitAccessibleTextGetCharacterExtents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
1191 {
1192     g_return_if_fail(ATK_TEXT(text));
1193     returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
1194
1195     IntRect extents = textExtents(text, offset, 1, coords);
1196     *x = extents.x();
1197     *y = extents.y();
1198     *width = extents.width();
1199     *height = extents.height();
1200 }
1201
1202 static void webkitAccessibleTextGetRangeExtents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect)
1203 {
1204     g_return_if_fail(ATK_TEXT(text));
1205     returnIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text));
1206
1207     IntRect extents = textExtents(text, startOffset, endOffset - startOffset, coords);
1208     rect->x = extents.x();
1209     rect->y = extents.y();
1210     rect->width = extents.width();
1211     rect->height = extents.height();
1212 }
1213
1214 static gint webkitAccessibleTextGetCharacterCount(AtkText* text)
1215 {
1216     g_return_val_if_fail(ATK_TEXT(text), 0);
1217     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1218
1219     return accessibilityObjectLength(core(text));
1220 }
1221
1222 static gint webkitAccessibleTextGetOffsetAtPoint(AtkText* text, gint x, gint y, AtkCoordType)
1223 {
1224     g_return_val_if_fail(ATK_TEXT(text), 0);
1225     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1226
1227     // FIXME: Use the AtkCoordType
1228     // TODO: Is it correct to ignore range.length?
1229     IntPoint pos(x, y);
1230     PlainTextRange range = core(text)->doAXRangeForPosition(pos);
1231     return range.start;
1232 }
1233
1234 static gint webkitAccessibleTextGetNSelections(AtkText* text)
1235 {
1236     g_return_val_if_fail(ATK_TEXT(text), 0);
1237     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1238
1239     AccessibilityObject* coreObject = core(text);
1240     VisibleSelection selection = coreObject->selection();
1241
1242     // Only range selections are needed for the purpose of this method
1243     if (!selection.isRange())
1244         return 0;
1245
1246     // We don't support multiple selections for now, so there's only
1247     // two possibilities
1248     // Also, we don't want to do anything if the selection does not
1249     // belong to the currently selected object. We have to check since
1250     // there's no way to get the selection for a given object, only
1251     // the global one (the API is a bit confusing)
1252     return selectionBelongsToObject(coreObject, selection) ? 1 : 0;
1253 }
1254
1255 static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
1256 {
1257     g_return_val_if_fail(ATK_TEXT(text), 0);
1258     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), 0);
1259
1260     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1261     if (selectionNum)
1262         return 0;
1263
1264     // Get the offsets of the selection for the selected object
1265     AccessibilityObject* coreObject = core(text);
1266     VisibleSelection selection = coreObject->selection();
1267     getSelectionOffsetsForObject(coreObject, selection, *startOffset, *endOffset);
1268
1269     // Return 0 instead of "", as that's the expected result for
1270     // this AtkText method when there's no selection
1271     if (*startOffset == *endOffset)
1272         return 0;
1273
1274     return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
1275 }
1276
1277 static gboolean webkitAccessibleTextAddSelection(AtkText* text, gint, gint)
1278 {
1279     g_return_val_if_fail(ATK_TEXT(text), FALSE);
1280     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
1281
1282     notImplemented();
1283     return FALSE;
1284 }
1285
1286 static gboolean webkitAccessibleTextSetSelection(AtkText* text, gint selectionNum, gint startOffset, gint endOffset)
1287 {
1288     g_return_val_if_fail(ATK_TEXT(text), FALSE);
1289     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
1290
1291     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1292     if (selectionNum)
1293         return FALSE;
1294
1295     AccessibilityObject* coreObject = core(text);
1296     if (!coreObject->isAccessibilityRenderObject())
1297         return FALSE;
1298
1299     // Consider -1 and out-of-bound values and correct them to length
1300     gint textCount = webkitAccessibleTextGetCharacterCount(text);
1301     if (startOffset < 0 || startOffset > textCount)
1302         startOffset = textCount;
1303     if (endOffset < 0 || endOffset > textCount)
1304         endOffset = textCount;
1305
1306     // We need to adjust the offsets for the list item marker.
1307     int offsetAdjustment = offsetAdjustmentForListItem(coreObject);
1308     if (offsetAdjustment) {
1309         if (startOffset < offsetAdjustment || endOffset < offsetAdjustment)
1310             return FALSE;
1311
1312         startOffset = atkOffsetToWebCoreOffset(text, startOffset);
1313         endOffset = atkOffsetToWebCoreOffset(text, endOffset);
1314     }
1315
1316     PlainTextRange textRange(startOffset, endOffset - startOffset);
1317     VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1318     if (range.isNull())
1319         return FALSE;
1320
1321     coreObject->setSelectedVisiblePositionRange(range);
1322     return TRUE;
1323 }
1324
1325 static gboolean webkitAccessibleTextRemoveSelection(AtkText* text, gint selectionNum)
1326 {
1327     g_return_val_if_fail(ATK_TEXT(text), FALSE);
1328     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
1329
1330     // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
1331     if (selectionNum)
1332         return FALSE;
1333
1334     // Do nothing if current selection doesn't belong to the object
1335     if (!webkitAccessibleTextGetNSelections(text))
1336         return FALSE;
1337
1338     // Set a new 0-sized selection to the caret position, in order
1339     // to simulate selection removal (GAIL style)
1340     gint caretOffset = webkitAccessibleTextGetCaretOffset(text);
1341     return webkitAccessibleTextSetSelection(text, selectionNum, caretOffset, caretOffset);
1342 }
1343
1344 static gboolean webkitAccessibleTextSetCaretOffset(AtkText* text, gint offset)
1345 {
1346     g_return_val_if_fail(ATK_TEXT(text), FALSE);
1347     returnValIfWebKitAccessibleIsInvalid(WEBKIT_ACCESSIBLE(text), FALSE);
1348
1349     AccessibilityObject* coreObject = core(text);
1350     if (!coreObject->isAccessibilityRenderObject())
1351         return FALSE;
1352
1353     // We need to adjust the offsets for the list item marker.
1354     int offsetAdjustment = offsetAdjustmentForListItem(coreObject);
1355     if (offsetAdjustment) {
1356         if (offset < offsetAdjustment)
1357             return FALSE;
1358
1359         offset = atkOffsetToWebCoreOffset(text, offset);
1360     }
1361
1362     PlainTextRange textRange(offset, 0);
1363     VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
1364     if (range.isNull())
1365         return FALSE;
1366
1367     coreObject->setSelectedVisiblePositionRange(range);
1368     return TRUE;
1369 }
1370
1371 void webkitAccessibleTextInterfaceInit(AtkTextIface* iface)
1372 {
1373     iface->get_text = webkitAccessibleTextGetText;
1374     iface->get_text_after_offset = webkitAccessibleTextGetTextAfterOffset;
1375     iface->get_text_at_offset = webkitAccessibleTextGetTextAtOffset;
1376     iface->get_text_before_offset = webkitAccessibleTextGetTextBeforeOffset;
1377     iface->get_character_at_offset = webkitAccessibleTextGetCharacterAtOffset;
1378     iface->get_caret_offset = webkitAccessibleTextGetCaretOffset;
1379     iface->get_run_attributes = webkitAccessibleTextGetRunAttributes;
1380     iface->get_default_attributes = webkitAccessibleTextGetDefaultAttributes;
1381     iface->get_character_extents = webkitAccessibleTextGetCharacterExtents;
1382     iface->get_range_extents = webkitAccessibleTextGetRangeExtents;
1383     iface->get_character_count = webkitAccessibleTextGetCharacterCount;
1384     iface->get_offset_at_point = webkitAccessibleTextGetOffsetAtPoint;
1385     iface->get_n_selections = webkitAccessibleTextGetNSelections;
1386     iface->get_selection = webkitAccessibleTextGetSelection;
1387     iface->add_selection = webkitAccessibleTextAddSelection;
1388     iface->remove_selection = webkitAccessibleTextRemoveSelection;
1389     iface->set_selection = webkitAccessibleTextSetSelection;
1390     iface->set_caret_offset = webkitAccessibleTextSetCaretOffset;
1391 }
1392
1393 #endif