Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Side by Side Diff: Source/WebCore/accessibility/atk/WebKitAccessibleInterfaceText.cpp

Issue 13726025: Remove GTK AX support, we've never used it (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2008 Nuanti Ltd.
3 * Copyright (C) 2009 Jan Alonzo
4 * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
5 *
6 * Portions from Mozilla a11y, copyright as follows:
7 *
8 * The Original Code is mozilla.org code.
9 *
10 * The Initial Developer of the Original Code is
11 * Sun Microsystems, Inc.
12 * Portions created by the Initial Developer are Copyright (C) 2002
13 * the Initial Developer. All Rights Reserved.
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public License
26 * along with this library; see the file COPYING.LIB. If not, write to
27 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
29 */
30
31 #include "config.h"
32 #include "WebKitAccessibleInterfaceText.h"
33
34 #if HAVE(ACCESSIBILITY)
35
36 #include "AccessibilityObject.h"
37 #include "Document.h"
38 #include "Font.h"
39 #include "FrameView.h"
40 #include "HostWindow.h"
41 #include "InlineTextBox.h"
42 #include "NotImplemented.h"
43 #include "RenderListItem.h"
44 #include "RenderListMarker.h"
45 #include "RenderText.h"
46 #include "TextEncoding.h"
47 #include "TextIterator.h"
48 #include "WebKitAccessibleUtil.h"
49 #include "WebKitAccessibleWrapperAtk.h"
50 #include "htmlediting.h"
51 #include <wtf/gobject/GOwnPtr.h>
52
53 #if PLATFORM(GTK)
54 #include <libgail-util/gail-util.h>
55 #include <pango/pango.h>
56 #endif
57
58 using namespace WebCore;
59
60 static AccessibilityObject* core(AtkText* text)
61 {
62 if (!WEBKIT_IS_ACCESSIBLE(text))
63 return 0;
64
65 return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(text));
66 }
67
68 static gchar* textForRenderer(RenderObject* renderer)
69 {
70 GString* resultText = g_string_new(0);
71
72 if (!renderer)
73 return g_string_free(resultText, FALSE);
74
75 // For RenderBlocks, piece together the text from the RenderText objects the y contain.
76 for (RenderObject* object = renderer->firstChild(); object; object = object- >nextSibling()) {
77 if (object->isBR()) {
78 g_string_append(resultText, "\n");
79 continue;
80 }
81
82 RenderText* renderText;
83 if (object->isText())
84 renderText = toRenderText(object);
85 else {
86 // List item's markers will be treated in an special way
87 // later on this function, so ignore them here.
88 if (object->isReplaced() && !object->isListMarker())
89 g_string_append_unichar(resultText, objectReplacementCharacter);
90
91 // We need to check children, if any, to consider when
92 // current object is not a text object but some of its
93 // children are, in order not to miss those portions of
94 // text by not properly handling those situations
95 if (object->firstChild())
96 g_string_append(resultText, textForRenderer(object));
97
98 continue;
99 }
100
101 InlineTextBox* box = renderText ? renderText->firstTextBox() : 0;
102 while (box) {
103 // WebCore introduces line breaks in the text that do not reflect
104 // the layout you see on the screen, replace them with spaces.
105 String text = String(renderText->characters(), renderText->textLengt h()).replace("\n", " ");
106 g_string_append(resultText, text.substring(box->start(), box->end() - box->start() + 1).utf8().data());
107
108 // Newline chars in the source result in separate text boxes, so che ck
109 // before adding a newline in the layout. See bug 25415 comment #78.
110 // If the next sibling is a BR, we'll add the newline when we examin e that child.
111 if (!box->nextOnLineExists() && !(object->nextSibling() && object->n extSibling()->isBR())) {
112 // If there was a '\n' in the last position of the
113 // current text box, it would have been converted to a
114 // space in String::replace(), so remove it first.
115 if (renderText->characters()[box->end()] == '\n')
116 g_string_erase(resultText, resultText->len - 1, -1);
117
118 g_string_append(resultText, "\n");
119 }
120 box = box->nextTextBox();
121 }
122 }
123
124 // Insert the text of the marker for list item in the right place, if presen t
125 if (renderer->isListItem()) {
126 String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
127 if (renderer->style()->direction() == LTR)
128 g_string_prepend(resultText, markerText.utf8().data());
129 else
130 g_string_append(resultText, markerText.utf8().data());
131 }
132
133 return g_string_free(resultText, FALSE);
134 }
135
136 static gchar* textForObject(AccessibilityObject* coreObject)
137 {
138 GString* str = g_string_new(0);
139
140 // For text controls, we can get the text line by line.
141 if (coreObject->isTextControl()) {
142 unsigned textLength = coreObject->textLength();
143 int lineNumber = 0;
144 PlainTextRange range = coreObject->doAXRangeForLine(lineNumber);
145 while (range.length) {
146 // When a line of text wraps in a text area, the final space is remo ved.
147 if (range.start + range.length < textLength)
148 range.length -= 1;
149 String lineText = coreObject->doAXStringForRange(range);
150 g_string_append(str, lineText.utf8().data());
151 g_string_append(str, "\n");
152 range = coreObject->doAXRangeForLine(++lineNumber);
153 }
154 } else if (coreObject->isAccessibilityRenderObject()) {
155 GOwnPtr<gchar> rendererText(textForRenderer(coreObject->renderer()));
156 g_string_append(str, rendererText.get());
157 }
158
159 return g_string_free(str, FALSE);
160 }
161
162 static gchar* webkitAccessibleTextGetText(AtkText*, gint startOffset, gint endOf fset);
163
164 #if PLATFORM(GTK)
165 static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
166 {
167 GailTextUtil* gailTextUtil = gail_text_util_new();
168 gail_text_util_text_setup(gailTextUtil, webkitAccessibleTextGetText(textObje ct, 0, -1));
169 return gailTextUtil;
170 }
171
172 static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
173 {
174 AccessibilityObject* coreObject = core(textObject);
175
176 Document* document = coreObject->document();
177 if (!document)
178 return 0;
179
180 HostWindow* hostWindow = document->view()->hostWindow();
181 if (!hostWindow)
182 return 0;
183 PlatformPageClient webView = hostWindow->platformPageClient();
184 if (!webView)
185 return 0;
186
187 // Create a string with the layout as it appears on the screen
188 PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*> (webView), textForObject(coreObject));
189 return layout;
190 }
191 #endif
192
193 static int baselinePositionForRenderObject(RenderObject* renderObject)
194 {
195 // FIXME: This implementation of baselinePosition originates from RenderObje ct.cpp and was
196 // removed in r70072. The implementation looks incorrect though, because thi s is not the
197 // baseline of the underlying RenderObject, but of the AccessibilityRenderOb ject.
198 const FontMetrics& fontMetrics = renderObject->firstLineStyle()->fontMetrics ();
199 return fontMetrics.ascent() + (renderObject->firstLineStyle()->computedLineH eight() - fontMetrics.height()) / 2;
200 }
201
202 static AtkAttributeSet* getAttributeSetForAccessibilityObject(const Accessibilit yObject* object)
203 {
204 if (!object->isAccessibilityRenderObject())
205 return 0;
206
207 RenderObject* renderer = object->renderer();
208 RenderStyle* style = renderer->style();
209
210 AtkAttributeSet* result = 0;
211 GOwnPtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
212 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_A TTR_SIZE), buffer.get());
213
214 Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor);
215 if (bgColor.isValid()) {
216 buffer.set(g_strdup_printf("%i,%i,%i", bgColor.red(), bgColor.green(), b gColor.blue()));
217 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_BG_COLOR), buffer.get());
218 }
219
220 Color fgColor = style->visitedDependentColor(CSSPropertyColor);
221 if (fgColor.isValid()) {
222 buffer.set(g_strdup_printf("%i,%i,%i", fgColor.red(), fgColor.green(), f gColor.blue()));
223 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_FG_COLOR), buffer.get());
224 }
225
226 int baselinePosition;
227 bool includeRise = true;
228 switch (style->verticalAlign()) {
229 case SUB:
230 baselinePosition = -1 * baselinePositionForRenderObject(renderer);
231 break;
232 case SUPER:
233 baselinePosition = baselinePositionForRenderObject(renderer);
234 break;
235 case BASELINE:
236 baselinePosition = 0;
237 break;
238 default:
239 includeRise = false;
240 break;
241 }
242
243 if (includeRise) {
244 buffer.set(g_strdup_printf("%i", baselinePosition));
245 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_RISE), buffer.get());
246 }
247
248 if (!style->textIndent().isUndefined()) {
249 int indentation = valueForLength(style->textIndent(), object->size().wid th(), renderer->view());
250 buffer.set(g_strdup_printf("%i", indentation));
251 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_INDENT), buffer.get());
252 }
253
254 String fontFamilyName = style->font().family().family().string();
255 if (fontFamilyName.left(8) == "-webkit-")
256 fontFamilyName = fontFamilyName.substring(8);
257
258 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_A TTR_FAMILY_NAME), fontFamilyName.utf8().data());
259
260 int fontWeight = -1;
261 switch (style->font().weight()) {
262 case FontWeight100:
263 fontWeight = 100;
264 break;
265 case FontWeight200:
266 fontWeight = 200;
267 break;
268 case FontWeight300:
269 fontWeight = 300;
270 break;
271 case FontWeight400:
272 fontWeight = 400;
273 break;
274 case FontWeight500:
275 fontWeight = 500;
276 break;
277 case FontWeight600:
278 fontWeight = 600;
279 break;
280 case FontWeight700:
281 fontWeight = 700;
282 break;
283 case FontWeight800:
284 fontWeight = 800;
285 break;
286 case FontWeight900:
287 fontWeight = 900;
288 }
289 if (fontWeight > 0) {
290 buffer.set(g_strdup_printf("%i", fontWeight));
291 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_WEIGHT), buffer.get());
292 }
293
294 switch (style->textAlign()) {
295 case TASTART:
296 case TAEND:
297 break;
298 case LEFT:
299 case WEBKIT_LEFT:
300 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_JUSTIFICATION), "left");
301 break;
302 case RIGHT:
303 case WEBKIT_RIGHT:
304 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_JUSTIFICATION), "right");
305 break;
306 case CENTER:
307 case WEBKIT_CENTER:
308 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_JUSTIFICATION), "center");
309 break;
310 case JUSTIFY:
311 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_JUSTIFICATION), "fill");
312 }
313
314 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_A TTR_UNDERLINE), (style->textDecoration() & UNDERLINE) ? "single" : "none");
315
316 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_A TTR_STYLE), style->font().italic() ? "italic" : "normal");
317
318 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_A TTR_STRIKETHROUGH), (style->textDecoration() & LINE_THROUGH) ? "true" : "false") ;
319
320 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_A TTR_INVISIBLE), (style->visibility() == HIDDEN) ? "true" : "false");
321
322 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TEXT_A TTR_EDITABLE), object->isReadOnly() ? "false" : "true");
323
324 String language = object->language();
325 if (!language.isEmpty())
326 result = addToAtkAttributeSet(result, atk_text_attribute_get_name(ATK_TE XT_ATTR_LANGUAGE), language.utf8().data());
327
328 return result;
329 }
330
331 static gint compareAttribute(const AtkAttribute* a, const AtkAttribute* b)
332 {
333 return g_strcmp0(a->name, b->name) || g_strcmp0(a->value, b->value);
334 }
335
336 // Returns an AtkAttributeSet with the elements of attributeSet1 which
337 // are either not present or different in attributeSet2. Neither
338 // attributeSet1 nor attributeSet2 should be used after calling this.
339 static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* attributeSet1, A tkAttributeSet* attributeSet2)
340 {
341 if (!attributeSet2)
342 return attributeSet1;
343
344 AtkAttributeSet* currentSet = attributeSet1;
345 AtkAttributeSet* found;
346 AtkAttributeSet* toDelete = 0;
347
348 while (currentSet) {
349 found = g_slist_find_custom(attributeSet2, currentSet->data, (GCompareFu nc)compareAttribute);
350 if (found) {
351 AtkAttributeSet* nextSet = currentSet->next;
352 toDelete = g_slist_prepend(toDelete, currentSet->data);
353 attributeSet1 = g_slist_delete_link(attributeSet1, currentSet);
354 currentSet = nextSet;
355 } else
356 currentSet = currentSet->next;
357 }
358
359 atk_attribute_set_free(attributeSet2);
360 atk_attribute_set_free(toDelete);
361 return attributeSet1;
362 }
363
364 static guint accessibilityObjectLength(const AccessibilityObject* object)
365 {
366 // Non render objects are not taken into account
367 if (!object->isAccessibilityRenderObject())
368 return 0;
369
370 // For those objects implementing the AtkText interface we use the
371 // well known API to always get the text in a consistent way
372 AtkObject* atkObj = ATK_OBJECT(object->wrapper());
373 if (ATK_IS_TEXT(atkObj)) {
374 GOwnPtr<gchar> text(webkitAccessibleTextGetText(ATK_TEXT(atkObj), 0, -1) );
375 return g_utf8_strlen(text.get(), -1);
376 }
377
378 // Even if we don't expose list markers to Assistive
379 // Technologies, we need to have a way to measure their length
380 // for those cases when it's needed to take it into account
381 // separately (as in getAccessibilityObjectForOffset)
382 RenderObject* renderer = object->renderer();
383 if (renderer && renderer->isListMarker()) {
384 RenderListMarker* marker = toRenderListMarker(renderer);
385 return marker->text().length() + marker->suffix().length();
386 }
387
388 return 0;
389 }
390
391 static const AccessibilityObject* getAccessibilityObjectForOffset(const Accessib ilityObject* object, guint offset, gint* startOffset, gint* endOffset)
392 {
393 const AccessibilityObject* result;
394 guint length = accessibilityObjectLength(object);
395 if (length > offset) {
396 *startOffset = 0;
397 *endOffset = length;
398 result = object;
399 } else {
400 *startOffset = -1;
401 *endOffset = -1;
402 result = 0;
403 }
404
405 if (!object->firstChild())
406 return result;
407
408 AccessibilityObject* child = object->firstChild();
409 guint currentOffset = 0;
410 guint childPosition = 0;
411 while (child && currentOffset <= offset) {
412 guint childLength = accessibilityObjectLength(child);
413 currentOffset = childLength + childPosition;
414 if (currentOffset > offset) {
415 gint childStartOffset;
416 gint childEndOffset;
417 const AccessibilityObject* grandChild = getAccessibilityObjectForOff set(child, offset-childPosition, &childStartOffset, &childEndOffset);
418 if (childStartOffset >= 0) {
419 *startOffset = childStartOffset + childPosition;
420 *endOffset = childEndOffset + childPosition;
421 result = grandChild;
422 }
423 } else {
424 childPosition += childLength;
425 child = child->nextSibling();
426 }
427 }
428 return result;
429 }
430
431 static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const Accessibili tyObject* element, gint offset, gint* startOffset, gint* endOffset)
432 {
433 const AccessibilityObject* child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset);
434 if (!child) {
435 *startOffset = -1;
436 *endOffset = -1;
437 return 0;
438 }
439
440 AtkAttributeSet* defaultAttributes = getAttributeSetForAccessibilityObject(e lement);
441 AtkAttributeSet* childAttributes = getAttributeSetForAccessibilityObject(chi ld);
442
443 return attributeSetDifference(childAttributes, defaultAttributes);
444 }
445
446 static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoor dType coords)
447 {
448 gchar* textContent = webkitAccessibleTextGetText(text, startOffset, -1);
449 gint textLength = g_utf8_strlen(textContent, -1);
450
451 // The first case (endOffset of -1) should work, but seems broken for all Gt k+ apps.
452 gint rangeLength = length;
453 if (rangeLength < 0 || rangeLength > textLength)
454 rangeLength = textLength;
455 AccessibilityObject* coreObject = core(text);
456
457 IntRect extents = coreObject->doAXBoundsForRange(PlainTextRange(startOffset, rangeLength));
458 switch (coords) {
459 case ATK_XY_SCREEN:
460 if (Document* document = coreObject->document())
461 extents = document->view()->contentsToScreen(extents);
462 break;
463 case ATK_XY_WINDOW:
464 // No-op
465 break;
466 }
467
468 return extents;
469 }
470
471 static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, Visibl eSelection& selection, gint& startOffset, gint& endOffset)
472 {
473 if (!coreObject->isAccessibilityRenderObject())
474 return;
475
476 // Early return if the selection doesn't affect the selected node.
477 if (!selectionBelongsToObject(coreObject, selection))
478 return;
479
480 // We need to find the exact start and end positions in the
481 // selected node that intersects the selection, to later on get
482 // the right values for the effective start and end offsets.
483 Position nodeRangeStart;
484 Position nodeRangeEnd;
485 Node* node = coreObject->node();
486 RefPtr<Range> selRange = selection.toNormalizedRange();
487
488 // If the selection affects the selected node and its first
489 // possible position is also in the selection, we must set
490 // nodeRangeStart to that position, otherwise to the selection's
491 // start position (it would belong to the node anyway).
492 Node* firstLeafNode = node->firstDescendant();
493 if (selRange->isPointInRange(firstLeafNode, 0, IGNORE_EXCEPTION))
494 nodeRangeStart = firstPositionInOrBeforeNode(firstLeafNode);
495 else
496 nodeRangeStart = selRange->startPosition();
497
498 // If the selection affects the selected node and its last
499 // possible position is also in the selection, we must set
500 // nodeRangeEnd to that position, otherwise to the selection's
501 // end position (it would belong to the node anyway).
502 Node* lastLeafNode = node->lastDescendant();
503 if (selRange->isPointInRange(lastLeafNode, lastOffsetInNode(lastLeafNode), I GNORE_EXCEPTION))
504 nodeRangeEnd = lastPositionInOrAfterNode(lastLeafNode);
505 else
506 nodeRangeEnd = selRange->endPosition();
507
508 // Calculate position of the selected range inside the object.
509 Position parentFirstPosition = firstPositionInOrBeforeNode(node);
510 RefPtr<Range> rangeInParent = Range::create(node->document(), parentFirstPos ition, nodeRangeStart);
511
512 // Set values for start and end offsets.
513 startOffset = TextIterator::rangeLength(rangeInParent.get(), true);
514
515 // We need to adjust the offsets for the list item marker.
516 RenderObject* renderer = coreObject->renderer();
517 if (renderer && renderer->isListItem()) {
518 String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
519 startOffset += markerText.length();
520 }
521
522 RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, no deRangeEnd);
523 endOffset = startOffset + TextIterator::rangeLength(nodeRange.get(), true);
524 }
525
526 static gchar* webkitAccessibleTextGetText(AtkText* text, gint startOffset, gint endOffset)
527 {
528 AccessibilityObject* coreObject = core(text);
529
530 int end = endOffset;
531 if (endOffset == -1) {
532 end = coreObject->stringValue().length();
533 if (!end)
534 end = coreObject->textUnderElement().length();
535 }
536
537 String ret;
538 if (coreObject->isTextControl())
539 ret = coreObject->doAXStringForRange(PlainTextRange(0, endOffset));
540 else {
541 ret = coreObject->stringValue();
542 if (!ret)
543 ret = coreObject->textUnderElement();
544 }
545
546 if (!ret.length()) {
547 // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
548 // In such instances, there may also be embedded objects. The object rep lacement character
549 // is something ATs want included and we have to account for the fact th at it is multibyte.
550 ret = String::fromUTF8(textForObject(coreObject));
551 if (!end)
552 end = ret.length();
553 }
554
555 // Prefix a item number/bullet if needed
556 if (coreObject->roleValue() == ListItemRole) {
557 RenderObject* objRenderer = coreObject->renderer();
558 if (objRenderer && objRenderer->isListItem()) {
559 String markerText = toRenderListItem(objRenderer)->markerTextWithSuf fix();
560 ret = objRenderer->style()->direction() == LTR ? markerText + ret : ret + markerText;
561 if (endOffset == -1)
562 end += markerText.length();
563 }
564 }
565
566 ret = ret.substring(startOffset, end - startOffset);
567 return g_strdup(ret.utf8().data());
568 }
569
570 static gchar* webkitAccessibleTextGetTextAfterOffset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
571 {
572 #if PLATFORM(GTK)
573 return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutFo rAtk(text), GAIL_AFTER_OFFSET, boundaryType, offset, startOffset, endOffset);
574 #else
575 UNUSED_PARAM(text);
576 UNUSED_PARAM(offset);
577 UNUSED_PARAM(boundaryType);
578 UNUSED_PARAM(startOffset);
579 UNUSED_PARAM(endOffset);
580
581 notImplemented();
582 return 0;
583 #endif
584 }
585
586 static gchar* webkitAccessibleTextGetTextAtOffset(AtkText* text, gint offset, At kTextBoundary boundaryType, gint* startOffset, gint* endOffset)
587 {
588 #if PLATFORM(GTK)
589 return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutFo rAtk(text), GAIL_AT_OFFSET, boundaryType, offset, startOffset, endOffset);
590 #else
591 UNUSED_PARAM(text);
592 UNUSED_PARAM(offset);
593 UNUSED_PARAM(boundaryType);
594 UNUSED_PARAM(startOffset);
595 UNUSED_PARAM(endOffset);
596
597 notImplemented();
598 return 0;
599 #endif
600 }
601
602 static gchar* webkitAccessibleTextGetTextBeforeOffset(AtkText* text, gint offset , AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
603 {
604 #if PLATFORM(GTK)
605 return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutFo rAtk(text), GAIL_BEFORE_OFFSET, boundaryType, offset, startOffset, endOffset);
606 #else
607 UNUSED_PARAM(text);
608 UNUSED_PARAM(offset);
609 UNUSED_PARAM(boundaryType);
610 UNUSED_PARAM(startOffset);
611 UNUSED_PARAM(endOffset);
612
613 notImplemented();
614 return 0;
615 #endif
616 }
617
618 static gunichar webkitAccessibleTextGetCharacterAtOffset(AtkText*, gint)
619 {
620 notImplemented();
621 return 0;
622 }
623
624 static gint webkitAccessibleTextGetCaretOffset(AtkText* text)
625 {
626 // coreObject is the unignored object whose offset the caller is requesting.
627 // focusedObject is the object with the caret. It is likely ignored -- unles s it's a link.
628 AccessibilityObject* coreObject = core(text);
629 if (!coreObject->isAccessibilityRenderObject())
630 return 0;
631
632 // We need to make sure we pass a valid object as reference.
633 if (coreObject->accessibilityIsIgnored())
634 coreObject = coreObject->parentObjectUnignored();
635 if (!coreObject)
636 return 0;
637
638 int offset;
639 if (!objectFocusedAndCaretOffsetUnignored(coreObject, offset))
640 return 0;
641
642 RenderObject* renderer = coreObject->renderer();
643 if (renderer && renderer->isListItem()) {
644 String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
645
646 // We need to adjust the offset for the list item marker.
647 offset += markerText.length();
648 }
649
650 // TODO: Verify this for RTL text.
651 return offset;
652 }
653
654 static AtkAttributeSet* webkitAccessibleTextGetRunAttributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset)
655 {
656 AccessibilityObject* coreObject = core(text);
657 AtkAttributeSet* result;
658
659 if (!coreObject) {
660 *startOffset = 0;
661 *endOffset = atk_text_get_character_count(text);
662 return 0;
663 }
664
665 if (offset == -1)
666 offset = atk_text_get_caret_offset(text);
667
668 result = getRunAttributesFromAccesibilityObject(coreObject, offset, startOff set, endOffset);
669
670 if (*startOffset < 0) {
671 *startOffset = offset;
672 *endOffset = offset;
673 }
674
675 return result;
676 }
677
678 static AtkAttributeSet* webkitAccessibleTextGetDefaultAttributes(AtkText* text)
679 {
680 AccessibilityObject* coreObject = core(text);
681 if (!coreObject || !coreObject->isAccessibilityRenderObject())
682 return 0;
683
684 return getAttributeSetForAccessibilityObject(coreObject);
685 }
686
687 static void webkitAccessibleTextGetCharacterExtents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
688 {
689 IntRect extents = textExtents(text, offset, 1, coords);
690 *x = extents.x();
691 *y = extents.y();
692 *width = extents.width();
693 *height = extents.height();
694 }
695
696 static void webkitAccessibleTextGetRangeExtents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect)
697 {
698 IntRect extents = textExtents(text, startOffset, endOffset - startOffset, co ords);
699 rect->x = extents.x();
700 rect->y = extents.y();
701 rect->width = extents.width();
702 rect->height = extents.height();
703 }
704
705 static gint webkitAccessibleTextGetCharacterCount(AtkText* text)
706 {
707 return accessibilityObjectLength(core(text));
708 }
709
710 static gint webkitAccessibleTextGetOffsetAtPoint(AtkText* text, gint x, gint y, AtkCoordType)
711 {
712 // FIXME: Use the AtkCoordType
713 // TODO: Is it correct to ignore range.length?
714 IntPoint pos(x, y);
715 PlainTextRange range = core(text)->doAXRangeForPosition(pos);
716 return range.start;
717 }
718
719 static gint webkitAccessibleTextGetNSelections(AtkText* text)
720 {
721 AccessibilityObject* coreObject = core(text);
722 VisibleSelection selection = coreObject->selection();
723
724 // Only range selections are needed for the purpose of this method
725 if (!selection.isRange())
726 return 0;
727
728 // We don't support multiple selections for now, so there's only
729 // two possibilities
730 // Also, we don't want to do anything if the selection does not
731 // belong to the currently selected object. We have to check since
732 // there's no way to get the selection for a given object, only
733 // the global one (the API is a bit confusing)
734 return selectionBelongsToObject(coreObject, selection) ? 1 : 0;
735 }
736
737 static gchar* webkitAccessibleTextGetSelection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
738 {
739 // Default values, unless the contrary is proved
740 *startOffset = *endOffset = 0;
741
742 // WebCore does not support multiple selection, so anything but 0 does not m ake sense for now.
743 if (selectionNum)
744 return 0;
745
746 // Get the offsets of the selection for the selected object
747 AccessibilityObject* coreObject = core(text);
748 VisibleSelection selection = coreObject->selection();
749 getSelectionOffsetsForObject(coreObject, selection, *startOffset, *endOffset );
750
751 // Return 0 instead of "", as that's the expected result for
752 // this AtkText method when there's no selection
753 if (*startOffset == *endOffset)
754 return 0;
755
756 return webkitAccessibleTextGetText(text, *startOffset, *endOffset);
757 }
758
759 static gboolean webkitAccessibleTextAddSelection(AtkText*, gint, gint)
760 {
761 notImplemented();
762 return FALSE;
763 }
764
765 static gboolean webkitAccessibleTextSetSelection(AtkText* text, gint selectionNu m, gint startOffset, gint endOffset)
766 {
767 // WebCore does not support multiple selection, so anything but 0 does not m ake sense for now.
768 if (selectionNum)
769 return FALSE;
770
771 AccessibilityObject* coreObject = core(text);
772 if (!coreObject->isAccessibilityRenderObject())
773 return FALSE;
774
775 // Consider -1 and out-of-bound values and correct them to length
776 gint textCount = webkitAccessibleTextGetCharacterCount(text);
777 if (startOffset < 0 || startOffset > textCount)
778 startOffset = textCount;
779 if (endOffset < 0 || endOffset > textCount)
780 endOffset = textCount;
781
782 // We need to adjust the offsets for the list item marker.
783 RenderObject* renderer = coreObject->renderer();
784 if (renderer && renderer->isListItem()) {
785 String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
786 int markerLength = markerText.length();
787 if (startOffset < markerLength || endOffset < markerLength)
788 return FALSE;
789
790 startOffset -= markerLength;
791 endOffset -= markerLength;
792 }
793
794 PlainTextRange textRange(startOffset, endOffset - startOffset);
795 VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRa nge);
796 if (range.isNull())
797 return FALSE;
798
799 coreObject->setSelectedVisiblePositionRange(range);
800 return TRUE;
801 }
802
803 static gboolean webkitAccessibleTextRemoveSelection(AtkText* text, gint selectio nNum)
804 {
805 // WebCore does not support multiple selection, so anything but 0 does not m ake sense for now.
806 if (selectionNum)
807 return FALSE;
808
809 // Do nothing if current selection doesn't belong to the object
810 if (!webkitAccessibleTextGetNSelections(text))
811 return FALSE;
812
813 // Set a new 0-sized selection to the caret position, in order
814 // to simulate selection removal (GAIL style)
815 gint caretOffset = webkitAccessibleTextGetCaretOffset(text);
816 return webkitAccessibleTextSetSelection(text, selectionNum, caretOffset, car etOffset);
817 }
818
819 static gboolean webkitAccessibleTextSetCaretOffset(AtkText* text, gint offset)
820 {
821 AccessibilityObject* coreObject = core(text);
822
823 if (!coreObject->isAccessibilityRenderObject())
824 return FALSE;
825
826 RenderObject* renderer = coreObject->renderer();
827 if (renderer && renderer->isListItem()) {
828 String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
829 int markerLength = markerText.length();
830 if (offset < markerLength)
831 return FALSE;
832
833 // We need to adjust the offset for list items.
834 offset -= markerLength;
835 }
836
837 PlainTextRange textRange(offset, 0);
838 VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRa nge);
839 if (range.isNull())
840 return FALSE;
841
842 coreObject->setSelectedVisiblePositionRange(range);
843 return TRUE;
844 }
845
846 void webkitAccessibleTextInterfaceInit(AtkTextIface* iface)
847 {
848 iface->get_text = webkitAccessibleTextGetText;
849 iface->get_text_after_offset = webkitAccessibleTextGetTextAfterOffset;
850 iface->get_text_at_offset = webkitAccessibleTextGetTextAtOffset;
851 iface->get_text_before_offset = webkitAccessibleTextGetTextBeforeOffset;
852 iface->get_character_at_offset = webkitAccessibleTextGetCharacterAtOffset;
853 iface->get_caret_offset = webkitAccessibleTextGetCaretOffset;
854 iface->get_run_attributes = webkitAccessibleTextGetRunAttributes;
855 iface->get_default_attributes = webkitAccessibleTextGetDefaultAttributes;
856 iface->get_character_extents = webkitAccessibleTextGetCharacterExtents;
857 iface->get_range_extents = webkitAccessibleTextGetRangeExtents;
858 iface->get_character_count = webkitAccessibleTextGetCharacterCount;
859 iface->get_offset_at_point = webkitAccessibleTextGetOffsetAtPoint;
860 iface->get_n_selections = webkitAccessibleTextGetNSelections;
861 iface->get_selection = webkitAccessibleTextGetSelection;
862 iface->add_selection = webkitAccessibleTextAddSelection;
863 iface->remove_selection = webkitAccessibleTextRemoveSelection;
864 iface->set_selection = webkitAccessibleTextSetSelection;
865 iface->set_caret_offset = webkitAccessibleTextSetCaretOffset;
866 }
867
868 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698