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 "TextRange-inl.h"
8
9 #include "Accessible-inl.h"
10 #include "nsAccUtils.h"
11
12 namespace mozilla {
13 namespace a11y {
14
15 ////////////////////////////////////////////////////////////////////////////////
16 // TextPoint
17
operator <(const TextPoint & aPoint) const18 bool TextPoint::operator<(const TextPoint& aPoint) const {
19 if (mContainer == aPoint.mContainer) return mOffset < aPoint.mOffset;
20
21 // Build the chain of parents
22 Accessible* p1 = mContainer;
23 Accessible* p2 = aPoint.mContainer;
24 AutoTArray<Accessible*, 30> parents1, parents2;
25 do {
26 parents1.AppendElement(p1);
27 p1 = p1->Parent();
28 } while (p1);
29 do {
30 parents2.AppendElement(p2);
31 p2 = p2->Parent();
32 } while (p2);
33
34 // Find where the parent chain differs
35 uint32_t pos1 = parents1.Length(), pos2 = parents2.Length();
36 for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
37 Accessible* child1 = parents1.ElementAt(--pos1);
38 Accessible* child2 = parents2.ElementAt(--pos2);
39 if (child1 != child2)
40 return child1->IndexInParent() < child2->IndexInParent();
41 }
42
43 NS_ERROR("Broken tree?!");
44 return false;
45 }
46
47 ////////////////////////////////////////////////////////////////////////////////
48 // TextRange
49
TextRange(HyperTextAccessible * aRoot,HyperTextAccessible * aStartContainer,int32_t aStartOffset,HyperTextAccessible * aEndContainer,int32_t aEndOffset)50 TextRange::TextRange(HyperTextAccessible* aRoot,
51 HyperTextAccessible* aStartContainer, int32_t aStartOffset,
52 HyperTextAccessible* aEndContainer, int32_t aEndOffset)
53 : mRoot(aRoot),
54 mStartContainer(aStartContainer),
55 mEndContainer(aEndContainer),
56 mStartOffset(aStartOffset),
57 mEndOffset(aEndOffset) {}
58
EmbeddedChildren(nsTArray<Accessible * > * aChildren) const59 void TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const {
60 if (mStartContainer == mEndContainer) {
61 int32_t startIdx = mStartContainer->GetChildIndexAtOffset(mStartOffset);
62 int32_t endIdx = mStartContainer->GetChildIndexAtOffset(mEndOffset);
63 for (int32_t idx = startIdx; idx <= endIdx; idx++) {
64 Accessible* child = mStartContainer->GetChildAt(idx);
65 if (!child->IsText()) {
66 aChildren->AppendElement(child);
67 }
68 }
69 return;
70 }
71
72 Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset);
73 Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset);
74
75 uint32_t pos1 = 0, pos2 = 0;
76 AutoTArray<Accessible*, 30> parents1, parents2;
77 Accessible* container =
78 CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2);
79
80 // Traverse the tree up to the container and collect embedded objects.
81 for (uint32_t idx = 0; idx < pos1 - 1; idx++) {
82 Accessible* parent = parents1[idx + 1];
83 Accessible* child = parents1[idx];
84 uint32_t childCount = parent->ChildCount();
85 for (uint32_t childIdx = child->IndexInParent(); childIdx < childCount;
86 childIdx++) {
87 Accessible* next = parent->GetChildAt(childIdx);
88 if (!next->IsText()) {
89 aChildren->AppendElement(next);
90 }
91 }
92 }
93
94 // Traverse through direct children in the container.
95 int32_t endIdx = parents2[pos2 - 1]->IndexInParent();
96 int32_t childIdx = parents1[pos1 - 1]->IndexInParent() + 1;
97 for (; childIdx < endIdx; childIdx++) {
98 Accessible* next = container->GetChildAt(childIdx);
99 if (!next->IsText()) {
100 aChildren->AppendElement(next);
101 }
102 }
103
104 // Traverse down from the container to end point.
105 for (int32_t idx = pos2 - 2; idx > 0; idx--) {
106 Accessible* parent = parents2[idx];
107 Accessible* child = parents2[idx - 1];
108 int32_t endIdx = child->IndexInParent();
109 for (int32_t childIdx = 0; childIdx < endIdx; childIdx++) {
110 Accessible* next = parent->GetChildAt(childIdx);
111 if (!next->IsText()) {
112 aChildren->AppendElement(next);
113 }
114 }
115 }
116 }
117
Text(nsAString & aText) const118 void TextRange::Text(nsAString& aText) const {
119 Accessible* current = mStartContainer->GetChildAtOffset(mStartOffset);
120 uint32_t startIntlOffset =
121 mStartOffset - mStartContainer->GetChildOffset(current);
122
123 while (current && TextInternal(aText, current, startIntlOffset)) {
124 current = current->Parent();
125 if (!current) break;
126
127 current = current->NextSibling();
128 }
129 }
130
Bounds(nsTArray<nsIntRect> aRects) const131 void TextRange::Bounds(nsTArray<nsIntRect> aRects) const {}
132
Normalize(ETextUnit aUnit)133 void TextRange::Normalize(ETextUnit aUnit) {}
134
Crop(Accessible * aContainer)135 bool TextRange::Crop(Accessible* aContainer) {
136 uint32_t boundaryPos = 0, containerPos = 0;
137 AutoTArray<Accessible*, 30> boundaryParents, containerParents;
138
139 // Crop the start boundary.
140 Accessible* container = nullptr;
141 Accessible* boundary = mStartContainer->GetChildAtOffset(mStartOffset);
142 if (boundary != aContainer) {
143 CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
144 &containerParents, &containerPos);
145
146 if (boundaryPos == 0) {
147 if (containerPos != 0) {
148 // The container is contained by the start boundary, reduce the range to
149 // the point starting at the container.
150 aContainer->ToTextPoint(mStartContainer.StartAssignment(),
151 &mStartOffset);
152 static_cast<Accessible*>(mStartContainer)->AddRef();
153 } else {
154 // The start boundary and the container are siblings.
155 container = aContainer;
156 }
157 } else if (containerPos != 0) {
158 // The container does not contain the start boundary.
159 boundary = boundaryParents[boundaryPos];
160 container = containerParents[containerPos];
161 }
162
163 if (container) {
164 // If the range start is after the container, then make the range invalid.
165 if (boundary->IndexInParent() > container->IndexInParent()) {
166 return !!(mRoot = nullptr);
167 }
168
169 // If the range starts before the container, then reduce the range to
170 // the point starting at the container.
171 if (boundary->IndexInParent() < container->IndexInParent()) {
172 container->ToTextPoint(mStartContainer.StartAssignment(),
173 &mStartOffset);
174 mStartContainer.get()->AddRef();
175 }
176 }
177
178 boundaryParents.SetLengthAndRetainStorage(0);
179 containerParents.SetLengthAndRetainStorage(0);
180 }
181
182 boundary = mEndContainer->GetChildAtOffset(mEndOffset);
183 if (boundary == aContainer) {
184 return true;
185 }
186
187 // Crop the end boundary.
188 container = nullptr;
189 CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
190 &containerParents, &containerPos);
191
192 if (boundaryPos == 0) {
193 if (containerPos != 0) {
194 aContainer->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset,
195 false);
196 static_cast<Accessible*>(mEndContainer)->AddRef();
197 } else {
198 container = aContainer;
199 }
200 } else if (containerPos != 0) {
201 boundary = boundaryParents[boundaryPos];
202 container = containerParents[containerPos];
203 }
204
205 if (!container) {
206 return true;
207 }
208
209 if (boundary->IndexInParent() < container->IndexInParent()) {
210 return !!(mRoot = nullptr);
211 }
212
213 if (boundary->IndexInParent() > container->IndexInParent()) {
214 container->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
215 static_cast<Accessible*>(mEndContainer)->AddRef();
216 }
217
218 return true;
219 }
220
FindText(const nsAString & aText,EDirection aDirection,nsCaseTreatment aCaseSensitive,TextRange * aFoundRange) const221 void TextRange::FindText(const nsAString& aText, EDirection aDirection,
222 nsCaseTreatment aCaseSensitive,
223 TextRange* aFoundRange) const {}
224
FindAttr(EAttr aAttr,nsIVariant * aValue,EDirection aDirection,TextRange * aFoundRange) const225 void TextRange::FindAttr(EAttr aAttr, nsIVariant* aValue, EDirection aDirection,
226 TextRange* aFoundRange) const {}
227
AddToSelection() const228 void TextRange::AddToSelection() const {}
229
RemoveFromSelection() const230 void TextRange::RemoveFromSelection() const {}
231
Select() const232 void TextRange::Select() const {}
233
ScrollIntoView(EHowToAlign aHow) const234 void TextRange::ScrollIntoView(EHowToAlign aHow) const {}
235
236 ////////////////////////////////////////////////////////////////////////////////
237 // pivate
238
Set(HyperTextAccessible * aRoot,HyperTextAccessible * aStartContainer,int32_t aStartOffset,HyperTextAccessible * aEndContainer,int32_t aEndOffset)239 void TextRange::Set(HyperTextAccessible* aRoot,
240 HyperTextAccessible* aStartContainer, int32_t aStartOffset,
241 HyperTextAccessible* aEndContainer, int32_t aEndOffset) {
242 mRoot = aRoot;
243 mStartContainer = aStartContainer;
244 mEndContainer = aEndContainer;
245 mStartOffset = aStartOffset;
246 mEndOffset = aEndOffset;
247 }
248
TextInternal(nsAString & aText,Accessible * aCurrent,uint32_t aStartIntlOffset) const249 bool TextRange::TextInternal(nsAString& aText, Accessible* aCurrent,
250 uint32_t aStartIntlOffset) const {
251 bool moveNext = true;
252 int32_t endIntlOffset = -1;
253 if (aCurrent->Parent() == mEndContainer &&
254 mEndContainer->GetChildAtOffset(mEndOffset) == aCurrent) {
255 uint32_t currentStartOffset = mEndContainer->GetChildOffset(aCurrent);
256 endIntlOffset = mEndOffset - currentStartOffset;
257 if (endIntlOffset == 0) return false;
258
259 moveNext = false;
260 }
261
262 if (aCurrent->IsTextLeaf()) {
263 aCurrent->AppendTextTo(aText, aStartIntlOffset,
264 endIntlOffset - aStartIntlOffset);
265 if (!moveNext) return false;
266 }
267
268 Accessible* next = aCurrent->FirstChild();
269 if (next) {
270 if (!TextInternal(aText, next, 0)) return false;
271 }
272
273 next = aCurrent->NextSibling();
274 if (next) {
275 if (!TextInternal(aText, next, 0)) return false;
276 }
277
278 return moveNext;
279 }
280
MoveInternal(ETextUnit aUnit,int32_t aCount,HyperTextAccessible & aContainer,int32_t aOffset,HyperTextAccessible * aStopContainer,int32_t aStopOffset)281 void TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
282 HyperTextAccessible& aContainer, int32_t aOffset,
283 HyperTextAccessible* aStopContainer,
284 int32_t aStopOffset) {}
285
CommonParent(Accessible * aAcc1,Accessible * aAcc2,nsTArray<Accessible * > * aParents1,uint32_t * aPos1,nsTArray<Accessible * > * aParents2,uint32_t * aPos2) const286 Accessible* TextRange::CommonParent(Accessible* aAcc1, Accessible* aAcc2,
287 nsTArray<Accessible*>* aParents1,
288 uint32_t* aPos1,
289 nsTArray<Accessible*>* aParents2,
290 uint32_t* aPos2) const {
291 if (aAcc1 == aAcc2) {
292 return aAcc1;
293 }
294
295 MOZ_ASSERT(aParents1->Length() == 0 || aParents2->Length() == 0,
296 "Wrong arguments");
297
298 // Build the chain of parents.
299 Accessible* p1 = aAcc1;
300 Accessible* p2 = aAcc2;
301 do {
302 aParents1->AppendElement(p1);
303 p1 = p1->Parent();
304 } while (p1);
305 do {
306 aParents2->AppendElement(p2);
307 p2 = p2->Parent();
308 } while (p2);
309
310 // Find where the parent chain differs
311 *aPos1 = aParents1->Length();
312 *aPos2 = aParents2->Length();
313 Accessible* parent = nullptr;
314 uint32_t len = 0;
315 for (len = std::min(*aPos1, *aPos2); len > 0; --len) {
316 Accessible* child1 = aParents1->ElementAt(--(*aPos1));
317 Accessible* child2 = aParents2->ElementAt(--(*aPos2));
318 if (child1 != child2) break;
319
320 parent = child1;
321 }
322
323 return parent;
324 }
325
326 } // namespace a11y
327 } // namespace mozilla
328