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