Bug 1877642 - Disable browser_fullscreen-tab-close-race.js on apple_silicon !debug...
[gecko.git] / accessible / atk / nsMaiInterfaceText.cpp
blobb5c0dcc38d402fc4be24af27bf06018add01741a
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"
12 #include "nsMai.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"
22 #include "DOMtoATK.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) {
31 if (!aAttributes) {
32 // This can happen if an Accessible dies in the content process, but the
33 // parent hasn't been udpated yet.
34 return nullptr;
37 AtkAttributeSet* atkAttributeSet = nullptr;
39 for (auto iter : *aAttributes) {
40 AtkAttribute* atkAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
41 nsAutoString value;
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
44 // value string.
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
53 // rgb(r, g, b).
54 auto color = iter.Value<Color>();
55 MOZ_ASSERT(color);
56 value.AppendInt(NS_GET_R(color->mValue));
57 value.Append(',');
58 value.AppendInt(NS_GET_G(color->mValue));
59 value.Append(',');
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);
66 colorValue();
67 } else if (name == nsGkAtoms::backgroundColor) {
68 atkName(ATK_TEXT_ATTR_BG_COLOR);
69 colorValue();
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>();
77 MOZ_ASSERT(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);
85 } else {
86 nsAutoString nameStr;
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;
100 extern "C" {
102 static gchar* getTextCB(AtkText* aText, gint aStartOffset, gint aEndOffset) {
103 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
104 if (!acc || !acc->IsTextRole()) {
105 return nullptr;
107 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
108 if (!text) {
109 return nullptr;
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) {
124 aOffset = count;
126 if (end > count) {
127 end = count;
129 if (aOffset < 0) {
130 aOffset = 0;
132 if (end < 0) {
133 end = 0;
135 *aStartOffset = aOffset;
136 *aEndOffset = end;
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));
149 if (!acc) {
150 return nullptr;
153 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
154 if (!text || !acc->IsTextRole()) {
155 return nullptr;
158 nsAutoString autoStr;
159 int32_t startOffset = 0, endOffset = 0;
160 text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset,
161 autoStr);
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));
178 if (!acc) {
179 return nullptr;
182 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
183 if (!text || !acc->IsTextRole()) {
184 return nullptr;
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));
200 if (!acc) {
201 return 0;
204 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
205 if (text) {
206 return DOMtoATK::ATKCharacter(text, aOffset);
209 return 0;
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));
220 if (!acc) {
221 return nullptr;
224 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
225 if (!text || !acc->IsTextRole()) {
226 return nullptr;
229 nsAutoString autoStr;
230 int32_t startOffset = 0, endOffset = 0;
231 text->TextBeforeOffset(aOffset, aBoundaryType, &startOffset, &endOffset,
232 autoStr);
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));
243 if (!acc) {
244 return -1;
247 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
248 if (!text || !acc->IsTextRole()) {
249 return -1;
252 return static_cast<gint>(text->CaretOffset());
255 static AtkAttributeSet* getRunAttributesCB(AtkText* aText, gint aOffset,
256 gint* aStartOffset,
257 gint* aEndOffset) {
258 *aStartOffset = -1;
259 *aEndOffset = -1;
260 int32_t startOffset = 0, endOffset = 0;
262 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
263 if (!acc) {
264 return nullptr;
267 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
268 if (!text || !acc->IsTextRole()) {
269 return nullptr;
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));
283 if (!acc) {
284 return nullptr;
287 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
288 if (!text || !acc->IsTextRole()) {
289 return nullptr;
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) {
300 return;
302 *aX = *aY = *aWidth = *aHeight = -1;
304 uint32_t geckoCoordType;
305 if (aCoords == ATK_XY_SCREEN) {
306 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
307 } else {
308 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
311 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
312 if (!acc) {
313 return;
316 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
317 if (!text || !acc->IsTextRole()) {
318 return;
321 LayoutDeviceIntRect rect = text->CharBounds(aOffset, geckoCoordType);
323 *aX = rect.x;
324 *aY = rect.y;
325 *aWidth = rect.width;
326 *aHeight = rect.height;
329 static void getRangeExtentsCB(AtkText* aText, gint aStartOffset,
330 gint aEndOffset, AtkCoordType aCoords,
331 AtkTextRectangle* aRect) {
332 if (!aRect) {
333 return;
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;
340 } else {
341 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
344 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
345 if (!acc) {
346 return;
349 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
350 if (!text || !acc->IsTextRole()) {
351 return;
354 LayoutDeviceIntRect rect =
355 text->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
357 aRect->x = rect.x;
358 aRect->y = rect.y;
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());
369 return 0;
372 static gint getOffsetAtPointCB(AtkText* aText, gint aX, gint aY,
373 AtkCoordType aCoords) {
374 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
375 if (!acc) {
376 return -1;
378 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
379 if (!text || !acc->IsTextRole()) {
380 return -1;
382 return static_cast<gint>(text->OffsetAtPoint(
383 aX, aY,
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));
391 if (!acc) {
392 return 0;
395 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
396 if (!text || !acc->IsTextRole()) {
397 return 0;
400 return text->SelectionCount();
403 static gchar* getTextSelectionCB(AtkText* aText, gint aSelectionNum,
404 gint* aStartOffset, gint* aEndOffset) {
405 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
406 if (!acc) {
407 return nullptr;
410 int32_t startOffset = 0, endOffset = 0;
411 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
412 if (!text || !acc->IsTextRole()) {
413 return nullptr;
416 text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset);
417 *aStartOffset = startOffset;
418 *aEndOffset = endOffset;
420 return getTextCB(aText, *aStartOffset, *aEndOffset);
423 // set methods
424 static gboolean addTextSelectionCB(AtkText* aText, gint aStartOffset,
425 gint aEndOffset) {
426 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
427 if (accWrap) {
428 HyperTextAccessible* text = accWrap->AsHyperText();
429 if (!text || !text->IsTextRole()) {
430 return FALSE;
433 return text->AddToSelection(aStartOffset, aEndOffset);
435 if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
436 return proxy->AddToSelection(aStartOffset, aEndOffset);
439 return FALSE;
442 static gboolean removeTextSelectionCB(AtkText* aText, gint aSelectionNum) {
443 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
444 if (accWrap) {
445 HyperTextAccessible* text = accWrap->AsHyperText();
446 if (!text || !text->IsTextRole()) {
447 return FALSE;
450 return text->RemoveFromSelection(aSelectionNum);
452 if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
453 return proxy->RemoveFromSelection(aSelectionNum);
456 return FALSE;
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()) {
463 return FALSE;
465 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
466 if (!text) {
467 return FALSE;
469 return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
472 static gboolean setCaretOffsetCB(AtkText* aText, gint aOffset) {
473 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
474 if (!acc) {
475 return FALSE;
478 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
479 if (!text || !acc->IsTextRole()) {
480 return FALSE;
483 text->SetCaretOffset(aOffset);
484 return TRUE;
487 static gboolean scrollSubstringToCB(AtkText* aText, gint aStartOffset,
488 gint aEndOffset, AtkScrollType aType) {
489 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
490 if (!acc) {
491 return FALSE;
494 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
495 if (!text) {
496 return FALSE;
499 text->ScrollSubstringTo(aStartOffset, aEndOffset, aType);
501 return TRUE;
504 static gboolean scrollSubstringToPointCB(AtkText* aText, gint aStartOffset,
505 gint aEndOffset, AtkCoordType aCoords,
506 gint aX, gint aY) {
507 Accessible* acc = GetInternalObj(ATK_OBJECT(aText));
508 if (!acc) {
509 return FALSE;
512 HyperTextAccessibleBase* text = acc->AsHyperTextBase();
513 if (!text) {
514 return FALSE;
517 text->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoords, aX, aY);
518 return TRUE;
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;
541 // set methods
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));