summaryrefslogtreecommitdiffstats
path: root/accessible/atk/DOMtoATK.cpp
blob: 2c23731bba477847cd56894cb2235e934151015f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* -*- 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 "DOMtoATK.h"
#include "nsUTF8Utils.h"

namespace mozilla {
namespace a11y {

namespace DOMtoATK {

void AddBOMs(nsACString& aDest, const nsACString& aSource) {
  uint32_t destlength = 0;

  // First compute how much room we will need.
  for (uint32_t srci = 0; srci < aSource.Length();) {
    int bytes = UTF8traits::bytes(aSource[srci]);
    if (bytes >= 4) {
      // Non-BMP character, will add a BOM after it.
      destlength += 3;
    }
    // Skip whole character encoding.
    srci += bytes;
    destlength += bytes;
  }

  uint32_t desti = 0;  // Index within aDest.

  // Add BOMs after non-BMP characters.
  aDest.SetLength(destlength);
  for (uint32_t srci = 0; srci < aSource.Length();) {
    uint32_t bytes = UTF8traits::bytes(aSource[srci]);

    MOZ_ASSERT(bytes <= aSource.Length() - srci,
               "We should have the whole sequence");

    // Copy whole sequence.
    aDest.Replace(desti, bytes, Substring(aSource, srci, bytes));
    desti += bytes;
    srci += bytes;

    if (bytes >= 4) {
      // More than 4 bytes in UTF-8 encoding exactly means more than 16 encoded
      // bits.  This is thus a non-BMP character which needed a surrogate
      // pair to get encoded in UTF-16, add a BOM after it.

      // And add a BOM after it.
      aDest.Replace(desti, 3, "\xEF\xBB\xBF");
      desti += 3;
    }
  }
  MOZ_ASSERT(desti == destlength,
             "Incoherency between computed length"
             "and actually translated length");
}

void ATKStringConverterHelper::AdjustOffsets(gint* aStartOffset,
                                             gint* aEndOffset, gint count) {
  MOZ_ASSERT(!mAdjusted,
             "DOMtoATK::ATKStringConverterHelper::AdjustOffsets needs to be "
             "called only once");

  if (*aStartOffset > 0) {
    (*aStartOffset)--;
    mStartShifted = true;
  }

  if (*aEndOffset >= 0 && *aEndOffset < count) {
    (*aEndOffset)++;
    mEndShifted = true;
  }

#ifdef DEBUG
  mAdjusted = true;
#endif
}

gchar* ATKStringConverterHelper::FinishUTF16toUTF8(nsCString& aStr) {
  int skip = 0;

  if (mStartShifted) {
    // AdjustOffsets added a leading character.

    MOZ_ASSERT(aStr.Length() > 0, "There should be a leading character");
    MOZ_ASSERT(
        static_cast<int>(aStr.Length()) >= UTF8traits::bytes(aStr.CharAt(0)),
        "The leading character should be complete");

    // drop first character
    skip = UTF8traits::bytes(aStr.CharAt(0));
  }

  if (mEndShifted) {
    // AdjustOffsets added a trailing character.

    MOZ_ASSERT(aStr.Length() > 0, "There should be a trailing character");

    int trail = -1;
    // Find beginning of last character.
    for (trail = aStr.Length() - 1; trail >= 0; trail--) {
      if (!UTF8traits::isInSeq(aStr.CharAt(trail))) {
        break;
      }
    }
    MOZ_ASSERT(trail >= 0,
               "There should be at least a whole trailing character");
    MOZ_ASSERT(trail + UTF8traits::bytes(aStr.CharAt(trail)) ==
                   static_cast<int>(aStr.Length()),
               "The trailing character should be complete");

    // Drop the last character.
    aStr.Truncate(trail);
  }

  // copy and return, libspi will free it
  return g_strdup(aStr.get() + skip);
}

gchar* ATKStringConverterHelper::ConvertAdjusted(const nsAString& aStr) {
  MOZ_ASSERT(mAdjusted,
             "DOMtoATK::ATKStringConverterHelper::AdjustOffsets needs to be "
             "called before ATKStringConverterHelper::ConvertAdjusted");

  NS_ConvertUTF16toUTF8 cautoStr(aStr);
  if (!cautoStr.get()) {
    return nullptr;
  }

  nsAutoCString cautoStrBOMs;
  AddBOMs(cautoStrBOMs, cautoStr);
  return FinishUTF16toUTF8(cautoStrBOMs);
}

gchar* Convert(const nsAString& aStr) {
  NS_ConvertUTF16toUTF8 cautoStr(aStr);
  if (!cautoStr.get()) {
    return nullptr;
  }

  nsAutoCString cautoStrBOMs;
  AddBOMs(cautoStrBOMs, cautoStr);
  return g_strdup(cautoStrBOMs.get());
}

}  // namespace DOMtoATK

}  // namespace a11y
}  // namespace mozilla