1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "InterfaceInitFuncs.h"
8 #include "mozilla/a11y/PDocAccessible.h"
9 #include "nsAccessibilityService.h"
10 #include "LocalAccessible-inl.h"
11 #include "HyperTextAccessible-inl.h"
13 #include "RemoteAccessible.h"
14 #include "AccAttributes.h"
16 #include "nsIAccessibleTypes.h"
17 #include "nsISimpleEnumerator.h"
18 #include "nsUTF8Utils.h"
20 #include "mozilla/Likely.h"
24 using namespace mozilla
;
25 using namespace mozilla::a11y
;
27 static const char* sAtkTextAttrNames
[ATK_TEXT_ATTR_LAST_DEFINED
];
29 static AtkAttributeSet
* ConvertToAtkTextAttributeSet(
30 AccAttributes
* aAttributes
) {
32 // This can happen if an Accessible dies in the content process, but the
33 // parent hasn't been udpated yet.
37 AtkAttributeSet
* atkAttributeSet
= nullptr;
39 for (auto iter
: *aAttributes
) {
40 AtkAttribute
* atkAttr
= (AtkAttribute
*)g_malloc(sizeof(AtkAttribute
));
42 // We set atkAttr->name directly for each case. For the value, we set the
43 // value string for each case. atkAttr->value is set at the end based on the
46 // Set atkAttr->name to a specific ATK attribute name.
47 auto atkName
= [&atkAttr
](AtkTextAttribute aAttrNum
) {
48 atkAttr
->name
= g_strdup(sAtkTextAttrNames
[aAttrNum
]);
50 // Set value to a formatted ATK color value.
51 auto colorValue
= [&iter
, &value
] {
52 // The format of the atk attribute is r,g,b and the gecko one is
54 auto color
= iter
.Value
<Color
>();
56 value
.AppendInt(NS_GET_R(color
->mValue
));
58 value
.AppendInt(NS_GET_G(color
->mValue
));
60 value
.AppendInt(NS_GET_B(color
->mValue
));
63 nsAtom
* name
= iter
.Name();
64 if (name
== nsGkAtoms::color
) {
65 atkName(ATK_TEXT_ATTR_FG_COLOR
);
67 } else if (name
== nsGkAtoms::backgroundColor
) {
68 atkName(ATK_TEXT_ATTR_BG_COLOR
);
70 } else if (name
== nsGkAtoms::font_family
) {
71 atkName(ATK_TEXT_ATTR_FAMILY_NAME
);
72 iter
.ValueAsString(value
);
73 } else if (name
== nsGkAtoms::font_size
) {
74 atkName(ATK_TEXT_ATTR_SIZE
);
75 // ATK wants the number of points without pt at the end.
76 auto fontSize
= iter
.Value
<FontSize
>();
78 value
.AppendInt(fontSize
->mValue
);
79 } else if (name
== nsGkAtoms::fontWeight
) {
80 atkName(ATK_TEXT_ATTR_WEIGHT
);
81 iter
.ValueAsString(value
);
82 } else if (name
== nsGkAtoms::invalid
) {
83 atkName(ATK_TEXT_ATTR_INVALID
);
84 iter
.ValueAsString(value
);
87 iter
.NameAsString(nameStr
);
88 atkAttr
->name
= g_strdup(NS_ConvertUTF16toUTF8(nameStr
).get());
89 iter
.ValueAsString(value
);
92 atkAttr
->value
= g_strdup(NS_ConvertUTF16toUTF8(value
).get());
93 atkAttributeSet
= g_slist_prepend(atkAttributeSet
, atkAttr
);
96 // libatk-adaptor will free it
97 return atkAttributeSet
;
102 static gchar
* getTextCB(AtkText
* aText
, gint aStartOffset
, gint aEndOffset
) {
103 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
104 if (!acc
|| !acc
->IsTextRole()) {
107 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
111 return DOMtoATK::NewATKString(text
, aStartOffset
, aEndOffset
);
114 static gint
getCharacterCountCB(AtkText
* aText
);
116 // Note: this does not support magic offsets, which is fine for its callers
117 // which do not implement any.
118 static gchar
* getCharTextAtOffset(AtkText
* aText
, gint aOffset
,
119 gint
* aStartOffset
, gint
* aEndOffset
) {
120 gint end
= aOffset
+ 1;
121 gint count
= getCharacterCountCB(aText
);
123 if (aOffset
> count
) {
135 *aStartOffset
= aOffset
;
138 return getTextCB(aText
, aOffset
, end
);
141 static gchar
* getTextAfterOffsetCB(AtkText
* aText
, gint aOffset
,
142 AtkTextBoundary aBoundaryType
,
143 gint
* aStartOffset
, gint
* aEndOffset
) {
144 if (aBoundaryType
== ATK_TEXT_BOUNDARY_CHAR
) {
145 return getCharTextAtOffset(aText
, aOffset
+ 1, aStartOffset
, aEndOffset
);
148 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
153 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
154 if (!text
|| !acc
->IsTextRole()) {
158 nsAutoString autoStr
;
159 int32_t startOffset
= 0, endOffset
= 0;
160 text
->TextAfterOffset(aOffset
, aBoundaryType
, &startOffset
, &endOffset
,
163 *aStartOffset
= startOffset
;
164 *aEndOffset
= endOffset
;
166 // libspi will free it.
167 return DOMtoATK::Convert(autoStr
);
170 static gchar
* getTextAtOffsetCB(AtkText
* aText
, gint aOffset
,
171 AtkTextBoundary aBoundaryType
,
172 gint
* aStartOffset
, gint
* aEndOffset
) {
173 if (aBoundaryType
== ATK_TEXT_BOUNDARY_CHAR
) {
174 return getCharTextAtOffset(aText
, aOffset
, aStartOffset
, aEndOffset
);
177 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
182 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
183 if (!text
|| !acc
->IsTextRole()) {
187 nsAutoString autoStr
;
188 int32_t startOffset
= 0, endOffset
= 0;
189 text
->TextAtOffset(aOffset
, aBoundaryType
, &startOffset
, &endOffset
, autoStr
);
191 *aStartOffset
= startOffset
;
192 *aEndOffset
= endOffset
;
194 // libspi will free it.
195 return DOMtoATK::Convert(autoStr
);
198 static gunichar
getCharacterAtOffsetCB(AtkText
* aText
, gint aOffset
) {
199 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
204 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
206 return DOMtoATK::ATKCharacter(text
, aOffset
);
212 static gchar
* getTextBeforeOffsetCB(AtkText
* aText
, gint aOffset
,
213 AtkTextBoundary aBoundaryType
,
214 gint
* aStartOffset
, gint
* aEndOffset
) {
215 if (aBoundaryType
== ATK_TEXT_BOUNDARY_CHAR
) {
216 return getCharTextAtOffset(aText
, aOffset
- 1, aStartOffset
, aEndOffset
);
219 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
224 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
225 if (!text
|| !acc
->IsTextRole()) {
229 nsAutoString autoStr
;
230 int32_t startOffset
= 0, endOffset
= 0;
231 text
->TextBeforeOffset(aOffset
, aBoundaryType
, &startOffset
, &endOffset
,
234 *aStartOffset
= startOffset
;
235 *aEndOffset
= endOffset
;
237 // libspi will free it.
238 return DOMtoATK::Convert(autoStr
);
241 static gint
getCaretOffsetCB(AtkText
* aText
) {
242 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
247 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
248 if (!text
|| !acc
->IsTextRole()) {
252 return static_cast<gint
>(text
->CaretOffset());
255 static AtkAttributeSet
* getRunAttributesCB(AtkText
* aText
, gint aOffset
,
260 int32_t startOffset
= 0, endOffset
= 0;
262 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
267 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
268 if (!text
|| !acc
->IsTextRole()) {
272 RefPtr
<AccAttributes
> attributes
=
273 text
->TextAttributes(false, aOffset
, &startOffset
, &endOffset
);
275 *aStartOffset
= startOffset
;
276 *aEndOffset
= endOffset
;
278 return ConvertToAtkTextAttributeSet(attributes
);
281 static AtkAttributeSet
* getDefaultAttributesCB(AtkText
* aText
) {
282 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
287 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
288 if (!text
|| !acc
->IsTextRole()) {
292 RefPtr
<AccAttributes
> attributes
= text
->DefaultTextAttributes();
293 return ConvertToAtkTextAttributeSet(attributes
);
296 static void getCharacterExtentsCB(AtkText
* aText
, gint aOffset
, gint
* aX
,
297 gint
* aY
, gint
* aWidth
, gint
* aHeight
,
298 AtkCoordType aCoords
) {
299 if (!aX
|| !aY
|| !aWidth
|| !aHeight
) {
302 *aX
= *aY
= *aWidth
= *aHeight
= -1;
304 uint32_t geckoCoordType
;
305 if (aCoords
== ATK_XY_SCREEN
) {
306 geckoCoordType
= nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
;
308 geckoCoordType
= nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE
;
311 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
316 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
317 if (!text
|| !acc
->IsTextRole()) {
321 LayoutDeviceIntRect rect
= text
->CharBounds(aOffset
, geckoCoordType
);
325 *aWidth
= rect
.width
;
326 *aHeight
= rect
.height
;
329 static void getRangeExtentsCB(AtkText
* aText
, gint aStartOffset
,
330 gint aEndOffset
, AtkCoordType aCoords
,
331 AtkTextRectangle
* aRect
) {
335 aRect
->x
= aRect
->y
= aRect
->width
= aRect
->height
= -1;
337 uint32_t geckoCoordType
;
338 if (aCoords
== ATK_XY_SCREEN
) {
339 geckoCoordType
= nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
;
341 geckoCoordType
= nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE
;
344 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
349 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
350 if (!text
|| !acc
->IsTextRole()) {
354 LayoutDeviceIntRect rect
=
355 text
->TextBounds(aStartOffset
, aEndOffset
, geckoCoordType
);
359 aRect
->width
= rect
.width
;
360 aRect
->height
= rect
.height
;
363 static gint
getCharacterCountCB(AtkText
* aText
) {
364 if (Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
))) {
365 if (HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase()) {
366 return static_cast<gint
>(text
->CharacterCount());
372 static gint
getOffsetAtPointCB(AtkText
* aText
, gint aX
, gint aY
,
373 AtkCoordType aCoords
) {
374 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
378 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
379 if (!text
|| !acc
->IsTextRole()) {
382 return static_cast<gint
>(text
->OffsetAtPoint(
384 (aCoords
== ATK_XY_SCREEN
385 ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE
386 : nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE
)));
389 static gint
getTextSelectionCountCB(AtkText
* aText
) {
390 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
395 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
396 if (!text
|| !acc
->IsTextRole()) {
400 return text
->SelectionCount();
403 static gchar
* getTextSelectionCB(AtkText
* aText
, gint aSelectionNum
,
404 gint
* aStartOffset
, gint
* aEndOffset
) {
405 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
410 int32_t startOffset
= 0, endOffset
= 0;
411 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
412 if (!text
|| !acc
->IsTextRole()) {
416 text
->SelectionBoundsAt(aSelectionNum
, &startOffset
, &endOffset
);
417 *aStartOffset
= startOffset
;
418 *aEndOffset
= endOffset
;
420 return getTextCB(aText
, *aStartOffset
, *aEndOffset
);
424 static gboolean
addTextSelectionCB(AtkText
* aText
, gint aStartOffset
,
426 AccessibleWrap
* accWrap
= GetAccessibleWrap(ATK_OBJECT(aText
));
428 HyperTextAccessible
* text
= accWrap
->AsHyperText();
429 if (!text
|| !text
->IsTextRole()) {
433 return text
->AddToSelection(aStartOffset
, aEndOffset
);
435 if (RemoteAccessible
* proxy
= GetProxy(ATK_OBJECT(aText
))) {
436 return proxy
->AddToSelection(aStartOffset
, aEndOffset
);
442 static gboolean
removeTextSelectionCB(AtkText
* aText
, gint aSelectionNum
) {
443 AccessibleWrap
* accWrap
= GetAccessibleWrap(ATK_OBJECT(aText
));
445 HyperTextAccessible
* text
= accWrap
->AsHyperText();
446 if (!text
|| !text
->IsTextRole()) {
450 return text
->RemoveFromSelection(aSelectionNum
);
452 if (RemoteAccessible
* proxy
= GetProxy(ATK_OBJECT(aText
))) {
453 return proxy
->RemoveFromSelection(aSelectionNum
);
459 static gboolean
setTextSelectionCB(AtkText
* aText
, gint aSelectionNum
,
460 gint aStartOffset
, gint aEndOffset
) {
461 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
462 if (!acc
|| !acc
->IsTextRole()) {
465 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
469 return text
->SetSelectionBoundsAt(aSelectionNum
, aStartOffset
, aEndOffset
);
472 static gboolean
setCaretOffsetCB(AtkText
* aText
, gint aOffset
) {
473 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
478 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
479 if (!text
|| !acc
->IsTextRole()) {
483 text
->SetCaretOffset(aOffset
);
487 static gboolean
scrollSubstringToCB(AtkText
* aText
, gint aStartOffset
,
488 gint aEndOffset
, AtkScrollType aType
) {
489 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
494 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
499 text
->ScrollSubstringTo(aStartOffset
, aEndOffset
, aType
);
504 static gboolean
scrollSubstringToPointCB(AtkText
* aText
, gint aStartOffset
,
505 gint aEndOffset
, AtkCoordType aCoords
,
507 Accessible
* acc
= GetInternalObj(ATK_OBJECT(aText
));
512 HyperTextAccessibleBase
* text
= acc
->AsHyperTextBase();
517 text
->ScrollSubstringToPoint(aStartOffset
, aEndOffset
, aCoords
, aX
, aY
);
522 void textInterfaceInitCB(AtkTextIface
* aIface
) {
523 NS_ASSERTION(aIface
, "Invalid aIface");
524 if (MOZ_UNLIKELY(!aIface
)) return;
526 aIface
->get_text
= getTextCB
;
527 aIface
->get_text_after_offset
= getTextAfterOffsetCB
;
528 aIface
->get_text_at_offset
= getTextAtOffsetCB
;
529 aIface
->get_character_at_offset
= getCharacterAtOffsetCB
;
530 aIface
->get_text_before_offset
= getTextBeforeOffsetCB
;
531 aIface
->get_caret_offset
= getCaretOffsetCB
;
532 aIface
->get_run_attributes
= getRunAttributesCB
;
533 aIface
->get_default_attributes
= getDefaultAttributesCB
;
534 aIface
->get_character_extents
= getCharacterExtentsCB
;
535 aIface
->get_range_extents
= getRangeExtentsCB
;
536 aIface
->get_character_count
= getCharacterCountCB
;
537 aIface
->get_offset_at_point
= getOffsetAtPointCB
;
538 aIface
->get_n_selections
= getTextSelectionCountCB
;
539 aIface
->get_selection
= getTextSelectionCB
;
542 aIface
->add_selection
= addTextSelectionCB
;
543 aIface
->remove_selection
= removeTextSelectionCB
;
544 aIface
->set_selection
= setTextSelectionCB
;
545 aIface
->set_caret_offset
= setCaretOffsetCB
;
547 if (IsAtkVersionAtLeast(2, 32)) {
548 aIface
->scroll_substring_to
= scrollSubstringToCB
;
549 aIface
->scroll_substring_to_point
= scrollSubstringToPointCB
;
552 // Cache the string values of the atk text attribute names.
553 for (uint32_t i
= 0; i
< ArrayLength(sAtkTextAttrNames
); i
++) {
554 sAtkTextAttrNames
[i
] =
555 atk_text_attribute_get_name(static_cast<AtkTextAttribute
>(i
));