nsMaiInterfaceText.cpp (16238B)
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/. */ 6 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" 15 16 #include "nsIAccessibleTypes.h" 17 #include "nsISimpleEnumerator.h" 18 #include "nsUTF8Utils.h" 19 20 #include "mozilla/Likely.h" 21 22 #include "DOMtoATK.h" 23 24 using namespace mozilla; 25 using namespace mozilla::a11y; 26 27 static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED]; 28 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; 35 } 36 37 AtkAttributeSet* atkAttributeSet = nullptr; 38 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. 45 46 // Set atkAttr->name to a specific ATK attribute name. 47 auto atkName = [&atkAttr](AtkTextAttribute aAttrNum) { 48 atkAttr->name = g_strdup(sAtkTextAttrNames[aAttrNum]); 49 }; 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)); 61 }; 62 63 nsAtom* name = iter.Name(); 64 if (name == nsGkAtoms::color) { 65 atkName(ATK_TEXT_ATTR_FG_COLOR); 66 colorValue(); 67 } else if (name == nsGkAtoms::background_color) { 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::font_weight) { 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); 90 } 91 92 atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get()); 93 atkAttributeSet = g_slist_prepend(atkAttributeSet, atkAttr); 94 } 95 96 // libatk-adaptor will free it 97 return atkAttributeSet; 98 } 99 100 extern "C" { 101 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; 106 } 107 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 108 if (!text) { 109 return nullptr; 110 } 111 return DOMtoATK::NewATKString(text, aStartOffset, aEndOffset); 112 } 113 114 static gint getCharacterCountCB(AtkText* aText); 115 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); 122 123 if (aOffset > count) { 124 aOffset = count; 125 } 126 if (end > count) { 127 end = count; 128 } 129 if (aOffset < 0) { 130 aOffset = 0; 131 } 132 if (end < 0) { 133 end = 0; 134 } 135 *aStartOffset = aOffset; 136 *aEndOffset = end; 137 138 return getTextCB(aText, aOffset, end); 139 } 140 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); 146 } 147 148 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 149 if (!acc) { 150 return nullptr; 151 } 152 153 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 154 if (!text || !acc->IsTextRole()) { 155 return nullptr; 156 } 157 158 nsAutoString autoStr; 159 int32_t startOffset = 0, endOffset = 0; 160 text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, 161 autoStr); 162 163 *aStartOffset = startOffset; 164 *aEndOffset = endOffset; 165 166 // libspi will free it. 167 return DOMtoATK::Convert(autoStr); 168 } 169 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); 175 } 176 177 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 178 if (!acc) { 179 return nullptr; 180 } 181 182 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 183 if (!text || !acc->IsTextRole()) { 184 return nullptr; 185 } 186 187 nsAutoString autoStr; 188 int32_t startOffset = 0, endOffset = 0; 189 text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); 190 191 *aStartOffset = startOffset; 192 *aEndOffset = endOffset; 193 194 // libspi will free it. 195 return DOMtoATK::Convert(autoStr); 196 } 197 198 static gunichar getCharacterAtOffsetCB(AtkText* aText, gint aOffset) { 199 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 200 if (!acc) { 201 return 0; 202 } 203 204 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 205 if (text) { 206 return DOMtoATK::ATKCharacter(text, aOffset); 207 } 208 209 return 0; 210 } 211 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); 217 } 218 219 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 220 if (!acc) { 221 return nullptr; 222 } 223 224 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 225 if (!text || !acc->IsTextRole()) { 226 return nullptr; 227 } 228 229 nsAutoString autoStr; 230 int32_t startOffset = 0, endOffset = 0; 231 text->TextBeforeOffset(aOffset, aBoundaryType, &startOffset, &endOffset, 232 autoStr); 233 234 *aStartOffset = startOffset; 235 *aEndOffset = endOffset; 236 237 // libspi will free it. 238 return DOMtoATK::Convert(autoStr); 239 } 240 241 static gint getCaretOffsetCB(AtkText* aText) { 242 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 243 if (!acc) { 244 return -1; 245 } 246 247 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 248 if (!text || !acc->IsTextRole()) { 249 return -1; 250 } 251 252 return static_cast<gint>(text->CaretOffset()); 253 } 254 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; 261 262 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 263 if (!acc) { 264 return nullptr; 265 } 266 267 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 268 if (!text || !acc->IsTextRole()) { 269 return nullptr; 270 } 271 272 RefPtr<AccAttributes> attributes = 273 text->TextAttributes(false, aOffset, &startOffset, &endOffset); 274 275 *aStartOffset = startOffset; 276 *aEndOffset = endOffset; 277 278 return ConvertToAtkTextAttributeSet(attributes); 279 } 280 281 static AtkAttributeSet* getDefaultAttributesCB(AtkText* aText) { 282 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 283 if (!acc) { 284 return nullptr; 285 } 286 287 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 288 if (!text || !acc->IsTextRole()) { 289 return nullptr; 290 } 291 292 RefPtr<AccAttributes> attributes = text->DefaultTextAttributes(); 293 return ConvertToAtkTextAttributeSet(attributes); 294 } 295 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; 301 } 302 *aX = *aY = *aWidth = *aHeight = -1; 303 304 uint32_t geckoCoordType; 305 if (aCoords == ATK_XY_SCREEN) { 306 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; 307 } else { 308 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; 309 } 310 311 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 312 if (!acc) { 313 return; 314 } 315 316 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 317 if (!text || !acc->IsTextRole()) { 318 return; 319 } 320 321 LayoutDeviceIntRect rect = text->CharBounds(aOffset, geckoCoordType); 322 323 *aX = rect.x; 324 *aY = rect.y; 325 *aWidth = rect.width; 326 *aHeight = rect.height; 327 } 328 329 static void getRangeExtentsCB(AtkText* aText, gint aStartOffset, 330 gint aEndOffset, AtkCoordType aCoords, 331 AtkTextRectangle* aRect) { 332 if (!aRect) { 333 return; 334 } 335 aRect->x = aRect->y = aRect->width = aRect->height = -1; 336 337 uint32_t geckoCoordType; 338 if (aCoords == ATK_XY_SCREEN) { 339 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; 340 } else { 341 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; 342 } 343 344 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 345 if (!acc) { 346 return; 347 } 348 349 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 350 if (!text || !acc->IsTextRole()) { 351 return; 352 } 353 354 LayoutDeviceIntRect rect = 355 text->TextBounds(aStartOffset, aEndOffset, geckoCoordType); 356 357 aRect->x = rect.x; 358 aRect->y = rect.y; 359 aRect->width = rect.width; 360 aRect->height = rect.height; 361 } 362 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()); 367 } 368 } 369 return 0; 370 } 371 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; 377 } 378 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 379 if (!text || !acc->IsTextRole()) { 380 return -1; 381 } 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))); 387 } 388 389 static gint getTextSelectionCountCB(AtkText* aText) { 390 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 391 if (!acc) { 392 return 0; 393 } 394 395 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 396 if (!text || !acc->IsTextRole()) { 397 return 0; 398 } 399 400 return text->SelectionCount(); 401 } 402 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; 408 } 409 410 int32_t startOffset = 0, endOffset = 0; 411 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 412 if (!text || !acc->IsTextRole()) { 413 return nullptr; 414 } 415 416 text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset); 417 *aStartOffset = startOffset; 418 *aEndOffset = endOffset; 419 420 return getTextCB(aText, *aStartOffset, *aEndOffset); 421 } 422 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; 431 } 432 433 return text->AddToSelection(aStartOffset, aEndOffset); 434 } 435 if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) { 436 return proxy->AddToSelection(aStartOffset, aEndOffset); 437 } 438 439 return FALSE; 440 } 441 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; 448 } 449 450 return text->RemoveFromSelection(aSelectionNum); 451 } 452 if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) { 453 return proxy->RemoveFromSelection(aSelectionNum); 454 } 455 456 return FALSE; 457 } 458 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; 464 } 465 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 466 if (!text) { 467 return FALSE; 468 } 469 return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset); 470 } 471 472 static gboolean setCaretOffsetCB(AtkText* aText, gint aOffset) { 473 Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); 474 if (!acc) { 475 return FALSE; 476 } 477 478 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 479 if (!text || !acc->IsTextRole()) { 480 return FALSE; 481 } 482 483 text->SetCaretOffset(aOffset); 484 return TRUE; 485 } 486 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; 492 } 493 494 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 495 if (!text) { 496 return FALSE; 497 } 498 499 text->ScrollSubstringTo(aStartOffset, aEndOffset, aType); 500 501 return TRUE; 502 } 503 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; 510 } 511 512 HyperTextAccessibleBase* text = acc->AsHyperTextBase(); 513 if (!text) { 514 return FALSE; 515 } 516 517 text->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoords, aX, aY); 518 return TRUE; 519 } 520 } 521 522 void textInterfaceInitCB(AtkTextIface* aIface) { 523 NS_ASSERTION(aIface, "Invalid aIface"); 524 if (MOZ_UNLIKELY(!aIface)) return; 525 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; 540 541 // set methods 542 aIface->add_selection = addTextSelectionCB; 543 aIface->remove_selection = removeTextSelectionCB; 544 aIface->set_selection = setTextSelectionCB; 545 aIface->set_caret_offset = setCaretOffsetCB; 546 547 if (IsAtkVersionAtLeast(2, 32)) { 548 aIface->scroll_substring_to = scrollSubstringToCB; 549 aIface->scroll_substring_to_point = scrollSubstringToPointCB; 550 } 551 552 // Cache the string values of the atk text attribute names. 553 for (uint32_t i = 0; i < std::size(sAtkTextAttrNames); i++) { 554 sAtkTextAttrNames[i] = 555 atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i)); 556 } 557 }