1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #include "ia2AccessibleText.h"
9
10 #include "Accessible2.h"
11 #include "AccessibleText_i.c"
12
13 #include "HyperTextAccessibleWrap.h"
14 #include "HyperTextAccessible-inl.h"
15 #include "ProxyWrappers.h"
16 #include "mozilla/ClearOnShutdown.h"
17
18 using namespace mozilla::a11y;
19
20 StaticRefPtr<HyperTextAccessibleWrap> ia2AccessibleText::sLastTextChangeAcc;
21 StaticAutoPtr<nsString> ia2AccessibleText::sLastTextChangeString;
22 uint32_t ia2AccessibleText::sLastTextChangeStart = 0;
23 uint32_t ia2AccessibleText::sLastTextChangeEnd = 0;
24 bool ia2AccessibleText::sLastTextChangeWasInsert = false;
25
26 // IAccessibleText
27
28 STDMETHODIMP
addSelection(long aStartOffset,long aEndOffset)29 ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset)
30 {
31 A11Y_TRYBLOCK_BEGIN
32
33 MOZ_ASSERT(!HyperTextProxyFor(this));
34
35 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
36 if (textAcc->IsDefunct())
37 return CO_E_OBJNOTCONNECTED;
38
39 return textAcc->AddToSelection(aStartOffset, aEndOffset) ?
40 S_OK : E_INVALIDARG;
41
42 A11Y_TRYBLOCK_END
43 }
44
45 STDMETHODIMP
get_attributes(long aOffset,long * aStartOffset,long * aEndOffset,BSTR * aTextAttributes)46 ia2AccessibleText::get_attributes(long aOffset, long *aStartOffset,
47 long *aEndOffset, BSTR *aTextAttributes)
48 {
49 A11Y_TRYBLOCK_BEGIN
50
51 if (!aStartOffset || !aEndOffset || !aTextAttributes)
52 return E_INVALIDARG;
53
54 *aStartOffset = 0;
55 *aEndOffset = 0;
56 *aTextAttributes = nullptr;
57
58 int32_t startOffset = 0, endOffset = 0;
59 HRESULT hr;
60 MOZ_ASSERT(!HyperTextProxyFor(this));
61 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
62 if (textAcc->IsDefunct()) {
63 return CO_E_OBJNOTCONNECTED;
64 }
65
66 nsCOMPtr<nsIPersistentProperties> attributes =
67 textAcc->TextAttributes(true, aOffset, &startOffset, &endOffset);
68
69 hr = AccessibleWrap::ConvertToIA2Attributes(attributes, aTextAttributes);
70 if (FAILED(hr))
71 return hr;
72
73 *aStartOffset = startOffset;
74 *aEndOffset = endOffset;
75
76 return S_OK;
77
78 A11Y_TRYBLOCK_END
79 }
80
81 STDMETHODIMP
get_caretOffset(long * aOffset)82 ia2AccessibleText::get_caretOffset(long *aOffset)
83 {
84 A11Y_TRYBLOCK_BEGIN
85
86 if (!aOffset)
87 return E_INVALIDARG;
88
89 *aOffset = -1;
90
91 MOZ_ASSERT(!HyperTextProxyFor(this));
92 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
93 if (textAcc->IsDefunct()) {
94 return CO_E_OBJNOTCONNECTED;
95 }
96
97 *aOffset = textAcc->CaretOffset();
98
99 return *aOffset != -1 ? S_OK : S_FALSE;
100
101 A11Y_TRYBLOCK_END
102 }
103
104 STDMETHODIMP
get_characterExtents(long aOffset,enum IA2CoordinateType aCoordType,long * aX,long * aY,long * aWidth,long * aHeight)105 ia2AccessibleText::get_characterExtents(long aOffset,
106 enum IA2CoordinateType aCoordType,
107 long* aX, long* aY,
108 long* aWidth, long* aHeight)
109 {
110 A11Y_TRYBLOCK_BEGIN
111
112 if (!aX || !aY || !aWidth || !aHeight)
113 return E_INVALIDARG;
114 *aX = *aY = *aWidth = *aHeight = 0;
115
116 uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
117 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
118 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
119 nsIntRect rect;
120 MOZ_ASSERT(!HyperTextProxyFor(this));
121 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
122 if (textAcc->IsDefunct())
123 return CO_E_OBJNOTCONNECTED;
124
125 rect = textAcc->CharBounds(aOffset, geckoCoordType);
126
127 *aX = rect.x;
128 *aY = rect.y;
129 *aWidth = rect.width;
130 *aHeight = rect.height;
131 return S_OK;
132
133 A11Y_TRYBLOCK_END
134 }
135
136 STDMETHODIMP
get_nSelections(long * aNSelections)137 ia2AccessibleText::get_nSelections(long* aNSelections)
138 {
139 A11Y_TRYBLOCK_BEGIN
140
141 if (!aNSelections)
142 return E_INVALIDARG;
143 *aNSelections = 0;
144
145 MOZ_ASSERT(!HyperTextProxyFor(this));
146 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
147 if (textAcc->IsDefunct()) {
148 return CO_E_OBJNOTCONNECTED;
149 }
150
151 *aNSelections = textAcc->SelectionCount();
152
153 return S_OK;
154
155 A11Y_TRYBLOCK_END
156 }
157
158 STDMETHODIMP
get_offsetAtPoint(long aX,long aY,enum IA2CoordinateType aCoordType,long * aOffset)159 ia2AccessibleText::get_offsetAtPoint(long aX, long aY,
160 enum IA2CoordinateType aCoordType,
161 long* aOffset)
162 {
163 A11Y_TRYBLOCK_BEGIN
164
165 if (!aOffset)
166 return E_INVALIDARG;
167 *aOffset = 0;
168
169 uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
170 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
171 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
172
173 MOZ_ASSERT(!HyperTextProxyFor(this));
174 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
175 if (textAcc->IsDefunct()) {
176 return CO_E_OBJNOTCONNECTED;
177 }
178
179 *aOffset = textAcc->OffsetAtPoint(aX, aY, geckoCoordType);
180
181 return *aOffset == -1 ? S_FALSE : S_OK;
182
183 A11Y_TRYBLOCK_END
184 }
185
186 STDMETHODIMP
get_selection(long aSelectionIndex,long * aStartOffset,long * aEndOffset)187 ia2AccessibleText::get_selection(long aSelectionIndex, long* aStartOffset,
188 long* aEndOffset)
189 {
190 A11Y_TRYBLOCK_BEGIN
191
192 if (!aStartOffset || !aEndOffset)
193 return E_INVALIDARG;
194 *aStartOffset = *aEndOffset = 0;
195
196 int32_t startOffset = 0, endOffset = 0;
197 MOZ_ASSERT(!HyperTextProxyFor(this));
198 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
199 if (textAcc->IsDefunct()) {
200 return CO_E_OBJNOTCONNECTED;
201 }
202
203 if (!textAcc->SelectionBoundsAt(aSelectionIndex, &startOffset, &endOffset)) {
204 return E_INVALIDARG;
205 }
206
207 *aStartOffset = startOffset;
208 *aEndOffset = endOffset;
209 return S_OK;
210
211 A11Y_TRYBLOCK_END
212 }
213
214 STDMETHODIMP
get_text(long aStartOffset,long aEndOffset,BSTR * aText)215 ia2AccessibleText::get_text(long aStartOffset, long aEndOffset, BSTR* aText)
216 {
217 A11Y_TRYBLOCK_BEGIN
218
219 if (!aText)
220 return E_INVALIDARG;
221
222 *aText = nullptr;
223
224 nsAutoString text;
225 MOZ_ASSERT(!HyperTextProxyFor(this));
226 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
227 if (textAcc->IsDefunct()) {
228 return CO_E_OBJNOTCONNECTED;
229 }
230
231 if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) {
232 return E_INVALIDARG;
233 }
234
235 textAcc->TextSubstring(aStartOffset, aEndOffset, text);
236
237 if (text.IsEmpty())
238 return S_FALSE;
239
240 *aText = ::SysAllocStringLen(text.get(), text.Length());
241 return *aText ? S_OK : E_OUTOFMEMORY;
242
243 A11Y_TRYBLOCK_END
244 }
245
246 STDMETHODIMP
get_textBeforeOffset(long aOffset,enum IA2TextBoundaryType aBoundaryType,long * aStartOffset,long * aEndOffset,BSTR * aText)247 ia2AccessibleText::get_textBeforeOffset(long aOffset,
248 enum IA2TextBoundaryType aBoundaryType,
249 long* aStartOffset, long* aEndOffset,
250 BSTR* aText)
251 {
252 A11Y_TRYBLOCK_BEGIN
253
254 if (!aStartOffset || !aEndOffset || !aText)
255 return E_INVALIDARG;
256
257 *aStartOffset = *aEndOffset = 0;
258 *aText = nullptr;
259
260 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
261 if (textAcc->IsDefunct())
262 return CO_E_OBJNOTCONNECTED;
263
264 if (!textAcc->IsValidOffset(aOffset))
265 return E_INVALIDARG;
266
267 nsAutoString text;
268 int32_t startOffset = 0, endOffset = 0;
269
270 if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) {
271 startOffset = 0;
272 endOffset = textAcc->CharacterCount();
273 textAcc->TextSubstring(startOffset, endOffset, text);
274 } else {
275 AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType);
276 if (boundaryType == -1)
277 return S_FALSE;
278
279 textAcc->TextBeforeOffset(aOffset, boundaryType, &startOffset, &endOffset, text);
280 }
281
282 *aStartOffset = startOffset;
283 *aEndOffset = endOffset;
284
285 if (text.IsEmpty())
286 return S_FALSE;
287
288 *aText = ::SysAllocStringLen(text.get(), text.Length());
289 return *aText ? S_OK : E_OUTOFMEMORY;
290
291 A11Y_TRYBLOCK_END
292 }
293
294 STDMETHODIMP
get_textAfterOffset(long aOffset,enum IA2TextBoundaryType aBoundaryType,long * aStartOffset,long * aEndOffset,BSTR * aText)295 ia2AccessibleText::get_textAfterOffset(long aOffset,
296 enum IA2TextBoundaryType aBoundaryType,
297 long* aStartOffset, long* aEndOffset,
298 BSTR* aText)
299 {
300 A11Y_TRYBLOCK_BEGIN
301
302 if (!aStartOffset || !aEndOffset || !aText)
303 return E_INVALIDARG;
304
305 *aStartOffset = 0;
306 *aEndOffset = 0;
307 *aText = nullptr;
308
309 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
310 if (textAcc->IsDefunct())
311 return CO_E_OBJNOTCONNECTED;
312
313 if (!textAcc->IsValidOffset(aOffset))
314 return E_INVALIDARG;
315
316 nsAutoString text;
317 int32_t startOffset = 0, endOffset = 0;
318
319 if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) {
320 startOffset = 0;
321 endOffset = textAcc->CharacterCount();
322 textAcc->TextSubstring(startOffset, endOffset, text);
323 } else {
324 AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType);
325 if (boundaryType == -1)
326 return S_FALSE;
327 textAcc->TextAfterOffset(aOffset, boundaryType, &startOffset, &endOffset, text);
328 }
329
330 *aStartOffset = startOffset;
331 *aEndOffset = endOffset;
332
333 if (text.IsEmpty())
334 return S_FALSE;
335
336 *aText = ::SysAllocStringLen(text.get(), text.Length());
337 return *aText ? S_OK : E_OUTOFMEMORY;
338
339 A11Y_TRYBLOCK_END
340 }
341
342 STDMETHODIMP
get_textAtOffset(long aOffset,enum IA2TextBoundaryType aBoundaryType,long * aStartOffset,long * aEndOffset,BSTR * aText)343 ia2AccessibleText::get_textAtOffset(long aOffset,
344 enum IA2TextBoundaryType aBoundaryType,
345 long* aStartOffset, long* aEndOffset,
346 BSTR* aText)
347 {
348 A11Y_TRYBLOCK_BEGIN
349
350 if (!aStartOffset || !aEndOffset || !aText)
351 return E_INVALIDARG;
352
353 *aStartOffset = *aEndOffset = 0;
354 *aText = nullptr;
355
356 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
357 if (textAcc->IsDefunct())
358 return CO_E_OBJNOTCONNECTED;
359
360 if (!textAcc->IsValidOffset(aOffset))
361 return E_INVALIDARG;
362
363 nsAutoString text;
364 int32_t startOffset = 0, endOffset = 0;
365 if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) {
366 startOffset = 0;
367 endOffset = textAcc->CharacterCount();
368 textAcc->TextSubstring(startOffset, endOffset, text);
369 } else {
370 AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType);
371 if (boundaryType == -1)
372 return S_FALSE;
373 textAcc->TextAtOffset(aOffset, boundaryType, &startOffset, &endOffset, text);
374 }
375
376 *aStartOffset = startOffset;
377 *aEndOffset = endOffset;
378
379 if (text.IsEmpty())
380 return S_FALSE;
381
382 *aText = ::SysAllocStringLen(text.get(), text.Length());
383 return *aText ? S_OK : E_OUTOFMEMORY;
384
385 A11Y_TRYBLOCK_END
386 }
387
388 STDMETHODIMP
removeSelection(long aSelectionIndex)389 ia2AccessibleText::removeSelection(long aSelectionIndex)
390 {
391 A11Y_TRYBLOCK_BEGIN
392
393 MOZ_ASSERT(!HyperTextProxyFor(this));
394
395 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
396 if (textAcc->IsDefunct())
397 return CO_E_OBJNOTCONNECTED;
398
399 return textAcc->RemoveFromSelection(aSelectionIndex) ?
400 S_OK : E_INVALIDARG;
401
402 A11Y_TRYBLOCK_END
403 }
404
405 STDMETHODIMP
setCaretOffset(long aOffset)406 ia2AccessibleText::setCaretOffset(long aOffset)
407 {
408 A11Y_TRYBLOCK_BEGIN
409
410 MOZ_ASSERT(!HyperTextProxyFor(this));
411
412 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
413 if (textAcc->IsDefunct())
414 return CO_E_OBJNOTCONNECTED;
415
416 if (!textAcc->IsValidOffset(aOffset))
417 return E_INVALIDARG;
418
419 textAcc->SetCaretOffset(aOffset);
420 return S_OK;
421
422 A11Y_TRYBLOCK_END
423 }
424
425 STDMETHODIMP
setSelection(long aSelectionIndex,long aStartOffset,long aEndOffset)426 ia2AccessibleText::setSelection(long aSelectionIndex, long aStartOffset,
427 long aEndOffset)
428 {
429 A11Y_TRYBLOCK_BEGIN
430
431 MOZ_ASSERT(!HyperTextProxyFor(this));
432
433 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
434 if (textAcc->IsDefunct())
435 return CO_E_OBJNOTCONNECTED;
436
437 return textAcc->SetSelectionBoundsAt(aSelectionIndex, aStartOffset, aEndOffset) ?
438 S_OK : E_INVALIDARG;
439
440 A11Y_TRYBLOCK_END
441 }
442
443 STDMETHODIMP
get_nCharacters(long * aNCharacters)444 ia2AccessibleText::get_nCharacters(long* aNCharacters)
445 {
446 A11Y_TRYBLOCK_BEGIN
447
448 if (!aNCharacters)
449 return E_INVALIDARG;
450 *aNCharacters = 0;
451
452 MOZ_ASSERT(!HyperTextProxyFor(this));
453
454 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
455 if (textAcc->IsDefunct())
456 return CO_E_OBJNOTCONNECTED;
457
458 *aNCharacters = textAcc->CharacterCount();
459 return S_OK;
460
461 A11Y_TRYBLOCK_END
462 }
463
464 STDMETHODIMP
scrollSubstringTo(long aStartIndex,long aEndIndex,enum IA2ScrollType aScrollType)465 ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex,
466 enum IA2ScrollType aScrollType)
467 {
468 A11Y_TRYBLOCK_BEGIN
469
470 MOZ_ASSERT(!HyperTextProxyFor(this));
471
472 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
473 if (textAcc->IsDefunct())
474 return CO_E_OBJNOTCONNECTED;
475
476 if (!textAcc->IsValidRange(aStartIndex, aEndIndex))
477 return E_INVALIDARG;
478
479 textAcc->ScrollSubstringTo(aStartIndex, aEndIndex, aScrollType);
480 return S_OK;
481
482 A11Y_TRYBLOCK_END
483 }
484
485 STDMETHODIMP
scrollSubstringToPoint(long aStartIndex,long aEndIndex,enum IA2CoordinateType aCoordType,long aX,long aY)486 ia2AccessibleText::scrollSubstringToPoint(long aStartIndex, long aEndIndex,
487 enum IA2CoordinateType aCoordType,
488 long aX, long aY)
489 {
490 A11Y_TRYBLOCK_BEGIN
491
492 uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
493 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
494 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
495
496 MOZ_ASSERT(!HyperTextProxyFor(this));
497
498 HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
499 if (textAcc->IsDefunct())
500 return CO_E_OBJNOTCONNECTED;
501
502 if (!textAcc->IsValidRange(aStartIndex, aEndIndex))
503 return E_INVALIDARG;
504
505 textAcc->ScrollSubstringToPoint(aStartIndex, aEndIndex,
506 geckoCoordType, aX, aY);
507 return S_OK;
508
509 A11Y_TRYBLOCK_END
510 }
511
512 STDMETHODIMP
get_newText(IA2TextSegment * aNewText)513 ia2AccessibleText::get_newText(IA2TextSegment *aNewText)
514 {
515 A11Y_TRYBLOCK_BEGIN
516
517 return GetModifiedText(true, aNewText);
518
519 A11Y_TRYBLOCK_END
520 }
521
522 STDMETHODIMP
get_oldText(IA2TextSegment * aOldText)523 ia2AccessibleText::get_oldText(IA2TextSegment *aOldText)
524 {
525 A11Y_TRYBLOCK_BEGIN
526
527 return GetModifiedText(false, aOldText);
528
529 A11Y_TRYBLOCK_END
530 }
531
532 // ia2AccessibleText
533
534 HRESULT
GetModifiedText(bool aGetInsertedText,IA2TextSegment * aText)535 ia2AccessibleText::GetModifiedText(bool aGetInsertedText,
536 IA2TextSegment *aText)
537 {
538 if (!aText)
539 return E_INVALIDARG;
540
541 if (!sLastTextChangeAcc)
542 return S_OK;
543
544 if (aGetInsertedText != sLastTextChangeWasInsert)
545 return S_OK;
546
547 if (sLastTextChangeAcc != this)
548 return S_OK;
549
550 aText->start = sLastTextChangeStart;
551 aText->end = sLastTextChangeEnd;
552
553 if (sLastTextChangeString->IsEmpty())
554 return S_FALSE;
555
556 aText->text = ::SysAllocStringLen(sLastTextChangeString->get(), sLastTextChangeString->Length());
557 return aText->text ? S_OK : E_OUTOFMEMORY;
558 }
559
560 AccessibleTextBoundary
GetGeckoTextBoundary(enum IA2TextBoundaryType aBoundaryType)561 ia2AccessibleText::GetGeckoTextBoundary(enum IA2TextBoundaryType aBoundaryType)
562 {
563 switch (aBoundaryType) {
564 case IA2_TEXT_BOUNDARY_CHAR:
565 return nsIAccessibleText::BOUNDARY_CHAR;
566 case IA2_TEXT_BOUNDARY_WORD:
567 return nsIAccessibleText::BOUNDARY_WORD_START;
568 case IA2_TEXT_BOUNDARY_LINE:
569 return nsIAccessibleText::BOUNDARY_LINE_START;
570 //case IA2_TEXT_BOUNDARY_SENTENCE:
571 //case IA2_TEXT_BOUNDARY_PARAGRAPH:
572 // XXX: not implemented
573 default:
574 return -1;
575 }
576 }
577
578 void
InitTextChangeData()579 ia2AccessibleText::InitTextChangeData()
580 {
581 ClearOnShutdown(&sLastTextChangeAcc);
582 ClearOnShutdown(&sLastTextChangeString);
583 }
584
585 void
UpdateTextChangeData(HyperTextAccessibleWrap * aAcc,bool aInsert,const nsString & aStr,int32_t aStart,uint32_t aLen)586 ia2AccessibleText::UpdateTextChangeData(HyperTextAccessibleWrap* aAcc,
587 bool aInsert, const nsString& aStr,
588 int32_t aStart, uint32_t aLen)
589 {
590 if (!sLastTextChangeString)
591 sLastTextChangeString = new nsString();
592
593 sLastTextChangeAcc = aAcc;
594 sLastTextChangeStart = aStart;
595 sLastTextChangeEnd = aStart + aLen;
596 sLastTextChangeWasInsert = aInsert;
597 *sLastTextChangeString = aStr;
598 }
599