summaryrefslogtreecommitdiffstats
path: root/accessible/atk/DOMtoATK.h
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/atk/DOMtoATK.h')
-rw-r--r--accessible/atk/DOMtoATK.h171
1 files changed, 171 insertions, 0 deletions
diff --git a/accessible/atk/DOMtoATK.h b/accessible/atk/DOMtoATK.h
new file mode 100644
index 0000000000..2476333171
--- /dev/null
+++ b/accessible/atk/DOMtoATK.h
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <glib.h>
+#include <cstdint>
+#include "mozilla/TypedEnumBits.h"
+#include "nsCharTraits.h"
+#include "nsString.h"
+
+/**
+ * ATK offsets are counted in unicode codepoints, while DOM offsets are counted
+ * in UTF-16 code units. That makes a difference for non-BMP characters,
+ * which need two UTF-16 code units to be represented (a pair of surrogates),
+ * while they are just one unicode character.
+ *
+ * To keep synchronization between ATK offsets (unicode codepoints) and DOM
+ * offsets (UTF-16 code units), after translation from UTF-16 to UTF-8 we add a
+ * BOM after each non-BMP character (which would otherwise use 2 UTF-16
+ * code units for only 1 unicode codepoint).
+ *
+ * BOMs (Byte Order Marks, U+FEFF, also known as ZERO WIDTH NO-BREAK SPACE, but
+ * that usage is deprecated) normally only appear at the beginning of unicode
+ * files, but their occurrence within text (notably after cut&paste) is not
+ * uncommon, and are thus considered as non-text.
+ *
+ * Since the selection requested through ATK may not contain both surrogates
+ * at the ends of the selection, we need to fetch one UTF-16 code point more
+ * on both side, and get rid of it before returning the string to ATK. The
+ * ATKStringConverterHelper class maintains this, NewATKString should be used
+ * to call it properly.
+ *
+ * In the end,
+ * - if the start is between the high and low surrogates, the UTF-8 result
+ * includes a BOM from it but not the character
+ * - if the end is between the high and low surrogates, the UTF-8 result
+ * includes the character but *not* the BOM
+ * - all non-BMP characters that are fully in the string are in the UTF-8 result
+ * as character followed by BOM
+ */
+namespace mozilla {
+namespace a11y {
+
+namespace DOMtoATK {
+
+/**
+ * Converts a string of accessible text into ATK gchar* string (by adding
+ * BOMs). This can be used when offsets do not need to be adjusted because
+ * ends of the string can not fall between surrogates.
+ */
+gchar* Convert(const nsAString& aStr);
+
+/**
+ * Add a BOM after each non-BMP character.
+ */
+void AddBOMs(nsACString& aDest, const nsACString& aSource);
+
+/**
+ * Replace all characters with asterisks (e.g. for password fields).
+ */
+void ConvertTexttoAsterisks(nsAString& aString);
+
+/**
+ * Parameterize conversion.
+ */
+enum class AtkStringConvertFlags : uint32_t {
+ None = 0,
+ ConvertTextToAsterisks = 1 << 0,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AtkStringConvertFlags)
+
+class ATKStringConverterHelper {
+ public:
+ ATKStringConverterHelper(void)
+ :
+#ifdef DEBUG
+ mAdjusted(false),
+#endif
+ mStartShifted(false),
+ mEndShifted(false) {
+ }
+
+ /**
+ * In order to properly get non-BMP values, offsets need to be changed
+ * to get one character more on each end, so that ConvertUTF16toUTF8 can
+ * convert surrogates even if the originally requested offsets fall between
+ * them.
+ */
+ void AdjustOffsets(gint* aStartOffset, gint* aEndOffset, gint count);
+
+ /**
+ * Converts a string of accessible text with adjusted offsets into ATK
+ * gchar* string (by adding BOMs). Note, AdjustOffsets has to be called
+ * before getting the text passed to this.
+ */
+ gchar* ConvertAdjusted(const nsAString& aStr);
+
+ private:
+ /**
+ * Remove the additional characters requested by PrepareUTF16toUTF8.
+ */
+ gchar* FinishUTF16toUTF8(nsCString& aStr);
+
+#ifdef DEBUG
+ bool mAdjusted;
+#endif
+ bool mStartShifted;
+ bool mEndShifted;
+};
+
+/**
+ * Get text from aAccessible, using ATKStringConverterHelper to properly
+ * introduce appropriate BOMs.
+ */
+template <class Accessible>
+gchar* NewATKString(Accessible* aAccessible, gint aStartOffset, gint aEndOffset,
+ AtkStringConvertFlags aFlags) {
+ gint startOffset = aStartOffset, endOffset = aEndOffset;
+ ATKStringConverterHelper converter;
+ converter.AdjustOffsets(&startOffset, &endOffset,
+ gint(aAccessible->CharacterCount()));
+ nsAutoString str;
+ aAccessible->TextSubstring(startOffset, endOffset, str);
+
+ if (str.Length() == 0) {
+ // Bogus offsets, or empty string, either way we do not need conversion.
+ return g_strdup("");
+ }
+
+ if (aFlags & AtkStringConvertFlags::ConvertTextToAsterisks) {
+ ConvertTexttoAsterisks(str);
+ }
+ return converter.ConvertAdjusted(str);
+}
+
+/**
+ * Get a character from aAccessible, fetching more data as appropriate to
+ * properly get non-BMP characters or a BOM as appropriate.
+ */
+template <class AccessibleCharAt>
+gunichar ATKCharacter(AccessibleCharAt* aAccessible, gint aOffset) {
+ // char16_t is unsigned short in Mozilla, gnuichar is guint32 in glib.
+ gunichar character = static_cast<gunichar>(aAccessible->CharAt(aOffset));
+
+ if (NS_IS_LOW_SURROGATE(character)) {
+ // Trailing surrogate, return BOM instead.
+ return 0xFEFF;
+ }
+
+ if (NS_IS_HIGH_SURROGATE(character)) {
+ // Heading surrogate, get the trailing surrogate and combine them.
+ gunichar characterLow =
+ static_cast<gunichar>(aAccessible->CharAt(aOffset + 1));
+
+ if (!NS_IS_LOW_SURROGATE(characterLow)) {
+ // It should have been a trailing surrogate... Flag the error.
+ return 0xFFFD;
+ }
+ return SURROGATE_TO_UCS4(character, characterLow);
+ }
+
+ return character;
+}
+
+} // namespace DOMtoATK
+
+} // namespace a11y
+} // namespace mozilla