michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "InterfaceInitFuncs.h" michael@0: michael@0: #include "Accessible-inl.h" michael@0: #include "HyperTextAccessible-inl.h" michael@0: #include "nsMai.h" michael@0: michael@0: #include "nsIAccessibleTypes.h" michael@0: #include "nsIPersistentProperties2.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: #include "mozilla/Likely.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::a11y; michael@0: michael@0: static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED]; michael@0: michael@0: static AtkAttributeSet* michael@0: ConvertToAtkTextAttributeSet(nsIPersistentProperties* aAttributes) michael@0: { michael@0: if (!aAttributes) michael@0: return nullptr; michael@0: michael@0: AtkAttributeSet* objAttributeSet = nullptr; michael@0: nsCOMPtr propEnum; michael@0: nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: bool hasMore = false; michael@0: while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr sup; michael@0: rv = propEnum->GetNext(getter_AddRefs(sup)); michael@0: NS_ENSURE_SUCCESS(rv, objAttributeSet); michael@0: michael@0: nsCOMPtr propElem(do_QueryInterface(sup)); michael@0: NS_ENSURE_TRUE(propElem, objAttributeSet); michael@0: michael@0: nsAutoCString name; michael@0: rv = propElem->GetKey(name); michael@0: NS_ENSURE_SUCCESS(rv, objAttributeSet); michael@0: michael@0: nsAutoString value; michael@0: rv = propElem->GetValue(value); michael@0: NS_ENSURE_SUCCESS(rv, objAttributeSet); michael@0: michael@0: AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute)); michael@0: objAttr->name = g_strdup(name.get()); michael@0: objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get()); michael@0: objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); michael@0: michael@0: // Handle attributes where atk has its own name. michael@0: const char* atkName = nullptr; michael@0: nsAutoString atkValue; michael@0: if (name.EqualsLiteral("color")) { michael@0: // The format of the atk attribute is r,g,b and the gecko one is michael@0: // rgb(r,g,b). michael@0: atkValue = Substring(value, 5, value.Length() - 1); michael@0: atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FG_COLOR]; michael@0: } else if (name.EqualsLiteral("background-color")) { michael@0: // The format of the atk attribute is r,g,b and the gecko one is michael@0: // rgb(r,g,b). michael@0: atkValue = Substring(value, 5, value.Length() - 1); michael@0: atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_BG_COLOR]; michael@0: } else if (name.EqualsLiteral("font-family")) { michael@0: atkValue = value; michael@0: atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FAMILY_NAME]; michael@0: } else if (name.Equals("font-size")) { michael@0: // ATK wants the number of pixels without px at the end. michael@0: atkValue = StringHead(value, value.Length() - 2); michael@0: atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_SIZE]; michael@0: } else if (name.EqualsLiteral("font-weight")) { michael@0: atkValue = value; michael@0: atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_WEIGHT]; michael@0: } else if (name.EqualsLiteral("invalid")) { michael@0: atkValue = value; michael@0: atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_INVALID]; michael@0: } michael@0: michael@0: if (atkName) { michael@0: objAttr = static_cast(g_malloc(sizeof(AtkAttribute))); michael@0: objAttr->name = g_strdup(atkName); michael@0: objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(atkValue).get()); michael@0: objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); michael@0: } michael@0: } michael@0: michael@0: // libatk-adaptor will free it michael@0: return objAttributeSet; michael@0: } michael@0: michael@0: static void michael@0: ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString) michael@0: { michael@0: // convert each char to "*" when it's "password text" michael@0: if (accWrap->NativeRole() == roles::PASSWORD_TEXT) { michael@0: for (uint32_t i = 0; i < aString.Length(); i++) michael@0: aString.Replace(i, 1, NS_LITERAL_STRING("*")); michael@0: } michael@0: } michael@0: michael@0: extern "C" { michael@0: michael@0: static gchar* michael@0: getTextCB(AtkText *aText, gint aStartOffset, gint aEndOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return nullptr; michael@0: michael@0: nsAutoString autoStr; michael@0: text->TextSubstring(aStartOffset, aEndOffset, autoStr); michael@0: michael@0: ConvertTexttoAsterisks(accWrap, autoStr); michael@0: NS_ConvertUTF16toUTF8 cautoStr(autoStr); michael@0: michael@0: //copy and return, libspi will free it. michael@0: return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; michael@0: } michael@0: michael@0: static gchar* michael@0: getTextAfterOffsetCB(AtkText *aText, gint aOffset, michael@0: AtkTextBoundary aBoundaryType, michael@0: gint *aStartOffset, gint *aEndOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return nullptr; michael@0: michael@0: nsAutoString autoStr; michael@0: int32_t startOffset = 0, endOffset = 0; michael@0: text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); michael@0: michael@0: *aStartOffset = startOffset; michael@0: *aEndOffset = endOffset; michael@0: michael@0: ConvertTexttoAsterisks(accWrap, autoStr); michael@0: NS_ConvertUTF16toUTF8 cautoStr(autoStr); michael@0: return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; michael@0: } michael@0: michael@0: static gchar* michael@0: getTextAtOffsetCB(AtkText *aText, gint aOffset, michael@0: AtkTextBoundary aBoundaryType, michael@0: gint *aStartOffset, gint *aEndOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return nullptr; michael@0: michael@0: nsAutoString autoStr; michael@0: int32_t startOffset = 0, endOffset = 0; michael@0: text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); michael@0: *aStartOffset = startOffset; michael@0: *aEndOffset = endOffset; michael@0: michael@0: ConvertTexttoAsterisks(accWrap, autoStr); michael@0: NS_ConvertUTF16toUTF8 cautoStr(autoStr); michael@0: return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; michael@0: } michael@0: michael@0: static gunichar michael@0: getCharacterAtOffsetCB(AtkText* aText, gint aOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return 0; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return 0; michael@0: michael@0: // char16_t is unsigned short in Mozilla, gnuichar is guint32 in glib. michael@0: return static_cast(text->CharAt(aOffset)); michael@0: } michael@0: michael@0: static gchar* michael@0: getTextBeforeOffsetCB(AtkText *aText, gint aOffset, michael@0: AtkTextBoundary aBoundaryType, michael@0: gint *aStartOffset, gint *aEndOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return nullptr; michael@0: michael@0: nsAutoString autoStr; michael@0: int32_t startOffset = 0, endOffset = 0; michael@0: text->TextBeforeOffset(aOffset, aBoundaryType, michael@0: &startOffset, &endOffset, autoStr); michael@0: *aStartOffset = startOffset; michael@0: *aEndOffset = endOffset; michael@0: michael@0: ConvertTexttoAsterisks(accWrap, autoStr); michael@0: NS_ConvertUTF16toUTF8 cautoStr(autoStr); michael@0: return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; michael@0: } michael@0: michael@0: static gint michael@0: getCaretOffsetCB(AtkText *aText) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return 0; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return 0; michael@0: michael@0: return static_cast(text->CaretOffset()); michael@0: } michael@0: michael@0: static AtkAttributeSet* michael@0: getRunAttributesCB(AtkText *aText, gint aOffset, michael@0: gint *aStartOffset, michael@0: gint *aEndOffset) michael@0: { michael@0: *aStartOffset = -1; michael@0: *aEndOffset = -1; michael@0: michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return nullptr; michael@0: michael@0: int32_t startOffset = 0, endOffset = 0; michael@0: nsCOMPtr attributes = michael@0: text->TextAttributes(false, aOffset, &startOffset, &endOffset); michael@0: michael@0: *aStartOffset = startOffset; michael@0: *aEndOffset = endOffset; michael@0: michael@0: return ConvertToAtkTextAttributeSet(attributes); michael@0: } michael@0: michael@0: static AtkAttributeSet* michael@0: getDefaultAttributesCB(AtkText *aText) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr attributes = text->DefaultTextAttributes(); michael@0: return ConvertToAtkTextAttributeSet(attributes); michael@0: } michael@0: michael@0: static void michael@0: getCharacterExtentsCB(AtkText *aText, gint aOffset, michael@0: gint *aX, gint *aY, michael@0: gint *aWidth, gint *aHeight, michael@0: AtkCoordType aCoords) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if(!accWrap || !aX || !aY || !aWidth || !aHeight) michael@0: return; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return; michael@0: michael@0: uint32_t geckoCoordType; michael@0: if (aCoords == ATK_XY_SCREEN) michael@0: geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; michael@0: else michael@0: geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; michael@0: michael@0: nsIntRect rect = text->CharBounds(aOffset, geckoCoordType); michael@0: *aX = rect.x; michael@0: *aY = rect.y; michael@0: *aWidth = rect.width; michael@0: *aHeight = rect.height; michael@0: } michael@0: michael@0: static void michael@0: getRangeExtentsCB(AtkText *aText, gint aStartOffset, gint aEndOffset, michael@0: AtkCoordType aCoords, AtkTextRectangle *aRect) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if(!accWrap || !aRect) michael@0: return; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return; michael@0: michael@0: uint32_t geckoCoordType; michael@0: if (aCoords == ATK_XY_SCREEN) michael@0: geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; michael@0: else michael@0: geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; michael@0: michael@0: nsIntRect rect = text->TextBounds(aStartOffset, aEndOffset, geckoCoordType); michael@0: aRect->x = rect.x; michael@0: aRect->y = rect.y; michael@0: aRect->width = rect.width; michael@0: aRect->height = rect.height; michael@0: } michael@0: michael@0: static gint michael@0: getCharacterCountCB(AtkText *aText) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return 0; michael@0: michael@0: HyperTextAccessible* textAcc = accWrap->AsHyperText(); michael@0: return textAcc->IsDefunct() ? michael@0: 0 : static_cast(textAcc->CharacterCount()); michael@0: } michael@0: michael@0: static gint michael@0: getOffsetAtPointCB(AtkText *aText, michael@0: gint aX, gint aY, michael@0: AtkCoordType aCoords) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return -1; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return -1; michael@0: michael@0: return static_cast( michael@0: text->OffsetAtPoint(aX, aY, michael@0: (aCoords == ATK_XY_SCREEN ? michael@0: nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : michael@0: nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE))); michael@0: } michael@0: michael@0: static gint michael@0: getTextSelectionCountCB(AtkText *aText) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return 0; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return 0; michael@0: michael@0: return text->SelectionCount(); michael@0: } michael@0: michael@0: static gchar* michael@0: getTextSelectionCB(AtkText *aText, gint aSelectionNum, michael@0: gint *aStartOffset, gint *aEndOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return nullptr; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return nullptr; michael@0: michael@0: int32_t startOffset = 0, endOffset = 0; michael@0: text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset); michael@0: michael@0: *aStartOffset = startOffset; michael@0: *aEndOffset = endOffset; michael@0: michael@0: return getTextCB(aText, *aStartOffset, *aEndOffset); michael@0: } michael@0: michael@0: // set methods michael@0: static gboolean michael@0: addTextSelectionCB(AtkText *aText, michael@0: gint aStartOffset, michael@0: gint aEndOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return FALSE; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return FALSE; michael@0: michael@0: return text->AddToSelection(aStartOffset, aEndOffset); michael@0: } michael@0: michael@0: static gboolean michael@0: removeTextSelectionCB(AtkText *aText, michael@0: gint aSelectionNum) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return FALSE; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return FALSE; michael@0: michael@0: return text->RemoveFromSelection(aSelectionNum); michael@0: } michael@0: michael@0: static gboolean michael@0: setTextSelectionCB(AtkText *aText, gint aSelectionNum, michael@0: gint aStartOffset, gint aEndOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return FALSE; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole()) michael@0: return FALSE; michael@0: michael@0: return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset); michael@0: } michael@0: michael@0: static gboolean michael@0: setCaretOffsetCB(AtkText *aText, gint aOffset) michael@0: { michael@0: AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); michael@0: if (!accWrap) michael@0: return FALSE; michael@0: michael@0: HyperTextAccessible* text = accWrap->AsHyperText(); michael@0: if (!text || !text->IsTextRole() || !text->IsValidOffset(aOffset)) michael@0: return FALSE; michael@0: michael@0: text->SetCaretOffset(aOffset); michael@0: return TRUE; michael@0: } michael@0: } michael@0: michael@0: void michael@0: textInterfaceInitCB(AtkTextIface* aIface) michael@0: { michael@0: NS_ASSERTION(aIface, "Invalid aIface"); michael@0: if (MOZ_UNLIKELY(!aIface)) michael@0: return; michael@0: michael@0: aIface->get_text = getTextCB; michael@0: aIface->get_text_after_offset = getTextAfterOffsetCB; michael@0: aIface->get_text_at_offset = getTextAtOffsetCB; michael@0: aIface->get_character_at_offset = getCharacterAtOffsetCB; michael@0: aIface->get_text_before_offset = getTextBeforeOffsetCB; michael@0: aIface->get_caret_offset = getCaretOffsetCB; michael@0: aIface->get_run_attributes = getRunAttributesCB; michael@0: aIface->get_default_attributes = getDefaultAttributesCB; michael@0: aIface->get_character_extents = getCharacterExtentsCB; michael@0: aIface->get_range_extents = getRangeExtentsCB; michael@0: aIface->get_character_count = getCharacterCountCB; michael@0: aIface->get_offset_at_point = getOffsetAtPointCB; michael@0: aIface->get_n_selections = getTextSelectionCountCB; michael@0: aIface->get_selection = getTextSelectionCB; michael@0: michael@0: // set methods michael@0: aIface->add_selection = addTextSelectionCB; michael@0: aIface->remove_selection = removeTextSelectionCB; michael@0: aIface->set_selection = setTextSelectionCB; michael@0: aIface->set_caret_offset = setCaretOffsetCB; michael@0: michael@0: // Cache the string values of the atk text attribute names. michael@0: for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++) michael@0: sAtkTextAttrNames[i] = michael@0: atk_text_attribute_get_name(static_cast(i)); michael@0: }