CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / accessible / src / html / nsHyperTextAccessible.cpp
blobe65d4b91f1f866ab8d7ddc8c00817f0f6b2b758f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developers of the Original Code are
18 * Sun Microsystems and IBM Corporation
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Ginn Chen (ginn.chen@sun.com)
24 * Aaron Leventhal (aleventh@us.ibm.com)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsHyperTextAccessible.h"
42 #include "nsAccessibilityAtoms.h"
43 #include "nsAccessibilityService.h"
44 #include "nsAccUtils.h"
45 #include "nsTextAttrs.h"
47 #include "nsIClipboard.h"
48 #include "nsContentCID.h"
49 #include "nsIDOMAbstractView.h"
50 #include "nsIDOMCharacterData.h"
51 #include "nsIDOMDocument.h"
52 #include "nsPIDOMWindow.h"
53 #include "nsIDOMDocumentView.h"
54 #include "nsIDOMRange.h"
55 #include "nsIDOMNSRange.h"
56 #include "nsIDOMWindowInternal.h"
57 #include "nsIDOMXULDocument.h"
58 #include "nsIEditingSession.h"
59 #include "nsIEditor.h"
60 #include "nsIFontMetrics.h"
61 #include "nsIFrame.h"
62 #include "nsFrameSelection.h"
63 #include "nsILineIterator.h"
64 #include "nsIInterfaceRequestorUtils.h"
65 #include "nsIPlaintextEditor.h"
66 #include "nsIScrollableFrame.h"
67 #include "nsISelection2.h"
68 #include "nsISelectionPrivate.h"
69 #include "nsIServiceManager.h"
70 #include "nsTextFragment.h"
71 #include "gfxSkipChars.h"
73 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
75 ////////////////////////////////////////////////////////////////////////////////
76 // nsHyperTextAccessible
77 ////////////////////////////////////////////////////////////////////////////////
79 nsHyperTextAccessible::
80 nsHyperTextAccessible(nsIContent *aNode, nsIWeakReference *aShell) :
81 nsAccessibleWrap(aNode, aShell)
83 mFlags |= eHyperTextAccessible;
86 NS_IMPL_ADDREF_INHERITED(nsHyperTextAccessible, nsAccessibleWrap)
87 NS_IMPL_RELEASE_INHERITED(nsHyperTextAccessible, nsAccessibleWrap)
89 nsresult nsHyperTextAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
91 *aInstancePtr = nsnull;
93 if (aIID.Equals(NS_GET_IID(nsHyperTextAccessible))) {
94 *aInstancePtr = static_cast<nsHyperTextAccessible*>(this);
95 NS_ADDREF_THIS();
96 return NS_OK;
99 if (mRoleMapEntry &&
100 (mRoleMapEntry->role == nsIAccessibleRole::ROLE_GRAPHIC ||
101 mRoleMapEntry->role == nsIAccessibleRole::ROLE_IMAGE_MAP ||
102 mRoleMapEntry->role == nsIAccessibleRole::ROLE_SLIDER ||
103 mRoleMapEntry->role == nsIAccessibleRole::ROLE_PROGRESSBAR ||
104 mRoleMapEntry->role == nsIAccessibleRole::ROLE_SEPARATOR)) {
105 // ARIA roles that these interfaces are not appropriate for
106 return nsAccessible::QueryInterface(aIID, aInstancePtr);
109 if (aIID.Equals(NS_GET_IID(nsIAccessibleText))) {
110 *aInstancePtr = static_cast<nsIAccessibleText*>(this);
111 NS_ADDREF_THIS();
112 return NS_OK;
115 if (aIID.Equals(NS_GET_IID(nsIAccessibleHyperText))) {
116 *aInstancePtr = static_cast<nsIAccessibleHyperText*>(this);
117 NS_ADDREF_THIS();
118 return NS_OK;
121 if (aIID.Equals(NS_GET_IID(nsIAccessibleEditableText))) {
122 *aInstancePtr = static_cast<nsIAccessibleEditableText*>(this);
123 NS_ADDREF_THIS();
124 return NS_OK;
127 return nsAccessible::QueryInterface(aIID, aInstancePtr);
130 PRUint32
131 nsHyperTextAccessible::NativeRole()
133 nsIAtom *tag = mContent->Tag();
135 if (tag == nsAccessibilityAtoms::form)
136 return nsIAccessibleRole::ROLE_FORM;
138 if (tag == nsAccessibilityAtoms::blockquote ||
139 tag == nsAccessibilityAtoms::div ||
140 tag == nsAccessibilityAtoms::nav)
141 return nsIAccessibleRole::ROLE_SECTION;
143 if (tag == nsAccessibilityAtoms::h1 ||
144 tag == nsAccessibilityAtoms::h2 ||
145 tag == nsAccessibilityAtoms::h3 ||
146 tag == nsAccessibilityAtoms::h4 ||
147 tag == nsAccessibilityAtoms::h5 ||
148 tag == nsAccessibilityAtoms::h6)
149 return nsIAccessibleRole::ROLE_HEADING;
151 if (tag == nsAccessibilityAtoms::article)
152 return nsIAccessibleRole::ROLE_DOCUMENT;
154 // Deal with html landmark elements
155 if (tag == nsAccessibilityAtoms::header)
156 return nsIAccessibleRole::ROLE_HEADER;
158 if (tag == nsAccessibilityAtoms::footer)
159 return nsIAccessibleRole::ROLE_FOOTER;
161 if (tag == nsAccessibilityAtoms::aside)
162 return nsIAccessibleRole::ROLE_NOTE;
164 // Treat block frames as paragraphs
165 nsIFrame *frame = GetFrame();
166 if (frame && frame->GetType() == nsAccessibilityAtoms::blockFrame &&
167 frame->GetContent()->Tag() != nsAccessibilityAtoms::input) {
168 // An html:input @type="file" is the only input that is exposed as a
169 // blockframe. It must be exposed as ROLE_TEXT_CONTAINER for JAWS.
170 return nsIAccessibleRole::ROLE_PARAGRAPH;
173 return nsIAccessibleRole::ROLE_TEXT_CONTAINER; // In ATK this works
176 nsresult
177 nsHyperTextAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
179 nsresult rv = nsAccessibleWrap::GetStateInternal(aState, aExtraState);
180 NS_ENSURE_A11Y_SUCCESS(rv, rv);
182 if (!aExtraState)
183 return NS_OK;
185 nsCOMPtr<nsIEditor> editor;
186 GetAssociatedEditor(getter_AddRefs(editor));
187 if (editor) {
188 PRUint32 flags;
189 editor->GetFlags(&flags);
190 if (0 == (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
191 *aExtraState |= nsIAccessibleStates::EXT_STATE_EDITABLE;
193 } else if (mContent->Tag() == nsAccessibilityAtoms::article) {
194 // We want <article> to behave like a document in terms of readonly state.
195 *aState |= nsIAccessibleStates::STATE_READONLY;
198 PRInt32 childCount;
199 GetChildCount(&childCount);
200 if (childCount > 0) {
201 *aExtraState |= nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT;
204 return NS_OK;
207 // Substring must be entirely within the same text node
208 nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRUint32 aStartRenderedOffset,
209 PRUint32 aEndRenderedOffset)
211 nsIntRect screenRect;
212 NS_ENSURE_TRUE(aFrame, screenRect);
213 if (aFrame->GetType() != nsAccessibilityAtoms::textFrame) {
214 // XXX fallback for non-text frames, happens for bullets right now
215 // but in the future bullets will have proper text frames
216 return aFrame->GetScreenRectExternal();
219 PRInt32 startContentOffset, endContentOffset;
220 nsresult rv = RenderedToContentOffset(aFrame, aStartRenderedOffset, &startContentOffset);
221 NS_ENSURE_SUCCESS(rv, screenRect);
222 rv = RenderedToContentOffset(aFrame, aEndRenderedOffset, &endContentOffset);
223 NS_ENSURE_SUCCESS(rv, screenRect);
225 nsIFrame *frame;
226 PRInt32 startContentOffsetInFrame;
227 // Get the right frame continuation -- not really a child, but a sibling of
228 // the primary frame passed in
229 rv = aFrame->GetChildFrameContainingOffset(startContentOffset, PR_FALSE,
230 &startContentOffsetInFrame, &frame);
231 NS_ENSURE_SUCCESS(rv, screenRect);
233 nsCOMPtr<nsIPresShell> shell = GetPresShell();
234 NS_ENSURE_TRUE(shell, screenRect);
236 nsPresContext *context = shell->GetPresContext();
238 while (frame && startContentOffset < endContentOffset) {
239 // Start with this frame's screen rect, which we will
240 // shrink based on the substring we care about within it.
241 // We will then add that frame to the total screenRect we
242 // are returning.
243 nsIntRect frameScreenRect = frame->GetScreenRectExternal();
245 // Get the length of the substring in this frame that we want the bounds for
246 PRInt32 startFrameTextOffset, endFrameTextOffset;
247 frame->GetOffsets(startFrameTextOffset, endFrameTextOffset);
248 PRInt32 frameTotalTextLength = endFrameTextOffset - startFrameTextOffset;
249 PRInt32 seekLength = endContentOffset - startContentOffset;
250 PRInt32 frameSubStringLength = NS_MIN(frameTotalTextLength - startContentOffsetInFrame, seekLength);
252 // Add the point where the string starts to the frameScreenRect
253 nsPoint frameTextStartPoint;
254 rv = frame->GetPointFromOffset(startContentOffset, &frameTextStartPoint);
255 NS_ENSURE_SUCCESS(rv, nsIntRect());
256 frameScreenRect.x += context->AppUnitsToDevPixels(frameTextStartPoint.x);
258 // Use the point for the end offset to calculate the width
259 nsPoint frameTextEndPoint;
260 rv = frame->GetPointFromOffset(startContentOffset + frameSubStringLength, &frameTextEndPoint);
261 NS_ENSURE_SUCCESS(rv, nsIntRect());
262 frameScreenRect.width = context->AppUnitsToDevPixels(frameTextEndPoint.x - frameTextStartPoint.x);
264 screenRect.UnionRect(frameScreenRect, screenRect);
266 // Get ready to loop back for next frame continuation
267 startContentOffset += frameSubStringLength;
268 startContentOffsetInFrame = 0;
269 frame = frame->GetNextContinuation();
272 return screenRect;
276 * Gets the specified text.
278 nsIFrame*
279 nsHyperTextAccessible::GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset,
280 nsAString *aText, nsIFrame **aEndFrame,
281 nsIntRect *aBoundsRect,
282 nsAccessible **aStartAcc,
283 nsAccessible **aEndAcc)
285 if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
286 aStartOffset = CharacterCount();
288 if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
289 GetCaretOffset(&aStartOffset);
291 if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
292 aEndOffset = CharacterCount();
294 if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
295 GetCaretOffset(&aEndOffset);
298 PRInt32 startOffset = aStartOffset;
299 PRInt32 endOffset = aEndOffset;
300 // XXX this prevents text interface usage on <input type="password">
301 PRBool isPassword = (Role() == nsIAccessibleRole::ROLE_PASSWORD_TEXT);
303 // Clear out parameters and set up loop
304 if (aText) {
305 aText->Truncate();
307 if (endOffset < 0) {
308 const PRInt32 kMaxTextLength = 32767;
309 endOffset = kMaxTextLength; // Max end offset
311 else if (startOffset > endOffset) {
312 return nsnull;
315 nsIFrame *startFrame = nsnull;
316 if (aEndFrame) {
317 *aEndFrame = nsnull;
319 if (aBoundsRect) {
320 aBoundsRect->Empty();
322 if (aStartAcc)
323 *aStartAcc = nsnull;
324 if (aEndAcc)
325 *aEndAcc = nsnull;
327 nsIntRect unionRect;
328 nsAccessible *lastAccessible = nsnull;
330 gfxSkipChars skipChars;
331 gfxSkipCharsIterator iter;
333 // Loop through children and collect valid offsets, text and bounds
334 // depending on what we need for out parameters.
335 PRInt32 childCount = GetChildCount();
336 for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
337 nsAccessible *childAcc = mChildren[childIdx];
338 lastAccessible = childAcc;
340 nsIFrame *frame = childAcc->GetFrame();
341 if (!frame) {
342 continue;
344 nsIFrame *primaryFrame = frame;
345 if (nsAccUtils::IsText(childAcc)) {
346 // We only need info up to rendered offset -- that is what we're
347 // converting to content offset
348 PRInt32 substringEndOffset = -1;
349 PRUint32 ourRenderedStart = 0;
350 PRInt32 ourContentStart = 0;
351 if (frame->GetType() == nsAccessibilityAtoms::textFrame) {
352 nsresult rv = frame->GetRenderedText(nsnull, &skipChars, &iter);
353 if (NS_SUCCEEDED(rv)) {
354 ourRenderedStart = iter.GetSkippedOffset();
355 ourContentStart = iter.GetOriginalOffset();
356 substringEndOffset =
357 iter.ConvertOriginalToSkipped(skipChars.GetOriginalCharCount() +
358 ourContentStart) - ourRenderedStart;
361 if (substringEndOffset < 0) {
362 // XXX for non-textframe text like list bullets,
363 // should go away after list bullet rewrite
364 substringEndOffset = nsAccUtils::TextLength(childAcc);
366 if (startOffset < substringEndOffset) {
367 // Our start is within this substring
368 if (startOffset > 0 || endOffset < substringEndOffset) {
369 // We don't want the whole string for this accessible
370 // Get out the continuing text frame with this offset
371 PRInt32 outStartLineUnused;
372 PRInt32 contentOffset;
373 if (frame->GetType() == nsAccessibilityAtoms::textFrame) {
374 contentOffset = iter.ConvertSkippedToOriginal(startOffset) +
375 ourRenderedStart - ourContentStart;
377 else {
378 contentOffset = startOffset;
380 frame->GetChildFrameContainingOffset(contentOffset, PR_TRUE,
381 &outStartLineUnused, &frame);
382 if (aEndFrame) {
383 *aEndFrame = frame; // We ended in the current frame
384 if (aEndAcc)
385 NS_ADDREF(*aEndAcc = childAcc);
387 if (substringEndOffset > endOffset) {
388 // Need to stop before the end of the available text
389 substringEndOffset = endOffset;
391 aEndOffset = endOffset;
393 if (aText) {
394 if (isPassword) {
395 for (PRInt32 count = startOffset; count < substringEndOffset; count ++)
396 *aText += '*'; // Show *'s only for password text
398 else {
399 childAcc->AppendTextTo(*aText, startOffset,
400 substringEndOffset - startOffset);
403 if (aBoundsRect) { // Caller wants the bounds of the text
404 aBoundsRect->UnionRect(*aBoundsRect,
405 GetBoundsForString(primaryFrame, startOffset,
406 substringEndOffset));
408 if (!startFrame) {
409 startFrame = frame;
410 aStartOffset = startOffset;
411 if (aStartAcc)
412 NS_ADDREF(*aStartAcc = childAcc);
414 // We already started copying in this accessible's string,
415 // for the next accessible we'll start at offset 0
416 startOffset = 0;
418 else {
419 // We have not found the start position yet, get the new startOffset
420 // that is relative to next accessible
421 startOffset -= substringEndOffset;
423 // The endOffset needs to be relative to the new startOffset
424 endOffset -= substringEndOffset;
426 else {
427 // Embedded object, append marker
428 // XXX Append \n for <br>'s
429 if (startOffset >= 1) {
430 -- startOffset;
432 else {
433 if (endOffset > 0) {
434 if (aText) {
435 // XXX: should use nsIAccessible::AppendTextTo.
436 if (frame->GetType() == nsAccessibilityAtoms::brFrame) {
437 *aText += kForcedNewLineChar;
438 } else if (nsAccUtils::MustPrune(this)) {
439 *aText += kImaginaryEmbeddedObjectChar;
440 // Expose imaginary embedded object character if the accessible
441 // hans't children.
442 } else {
443 *aText += kEmbeddedObjectChar;
446 if (aBoundsRect) {
447 aBoundsRect->UnionRect(*aBoundsRect,
448 frame->GetScreenRectExternal());
451 if (!startFrame) {
452 startFrame = frame;
453 aStartOffset = 0;
454 if (aStartAcc)
455 NS_ADDREF(*aStartAcc = childAcc);
458 -- endOffset;
460 if (endOffset <= 0 && startFrame) {
461 break; // If we don't have startFrame yet, get that in next loop iteration
465 if (aStartAcc && !*aStartAcc) {
466 NS_IF_ADDREF(*aStartAcc = lastAccessible);
468 if (aEndFrame && !*aEndFrame) {
469 *aEndFrame = startFrame;
470 if (aStartAcc && aEndAcc)
471 NS_IF_ADDREF(*aEndAcc = *aStartAcc);
474 return startFrame;
477 NS_IMETHODIMP
478 nsHyperTextAccessible::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset,
479 nsAString &aText)
481 aText.Truncate();
483 if (IsDefunct())
484 return NS_ERROR_FAILURE;
486 PRInt32 startOffset = ConvertMagicOffset(aStartOffset);
487 PRInt32 startChildIdx = GetChildIndexAtOffset(startOffset);
488 if (startChildIdx == -1)
489 return NS_ERROR_INVALID_ARG;
491 PRInt32 endOffset = ConvertMagicOffset(aEndOffset);
492 PRInt32 endChildIdx = GetChildIndexAtOffset(endOffset);
493 if (endChildIdx == -1)
494 return NS_ERROR_INVALID_ARG;
496 if (startChildIdx == endChildIdx) {
497 PRInt32 childOffset = GetChildOffset(startChildIdx);
498 NS_ENSURE_STATE(childOffset != -1);
500 nsAccessible* child = GetChildAt(startChildIdx);
501 child->AppendTextTo(aText, startOffset - childOffset,
502 endOffset - startOffset);
504 return NS_OK;
507 PRInt32 startChildOffset = GetChildOffset(startChildIdx);
508 NS_ENSURE_STATE(startChildOffset != -1);
510 nsAccessible* startChild = GetChildAt(startChildIdx);
511 startChild->AppendTextTo(aText, startOffset - startChildOffset);
513 for (PRInt32 childIdx = startChildIdx + 1; childIdx < endChildIdx; childIdx++) {
514 nsAccessible* child = GetChildAt(childIdx);
515 child->AppendTextTo(aText);
518 PRInt32 endChildOffset = GetChildOffset(endChildIdx);
519 NS_ENSURE_STATE(endChildOffset != -1);
521 nsAccessible* endChild = GetChildAt(endChildIdx);
522 endChild->AppendTextTo(aText, 0, endOffset - endChildOffset);
524 return NS_OK;
528 * Gets the character count.
530 NS_IMETHODIMP nsHyperTextAccessible::GetCharacterCount(PRInt32 *aCharacterCount)
532 NS_ENSURE_ARG_POINTER(aCharacterCount);
533 *aCharacterCount = 0;
535 if (IsDefunct())
536 return NS_ERROR_FAILURE;
538 *aCharacterCount = CharacterCount();
539 return NS_OK;
543 * Gets the specified character.
545 NS_IMETHODIMP nsHyperTextAccessible::GetCharacterAtOffset(PRInt32 aOffset, PRUnichar *aCharacter)
547 NS_ENSURE_ARG_POINTER(aCharacter);
548 *aCharacter = nsnull;
550 if (IsDefunct())
551 return NS_ERROR_FAILURE;
553 nsAutoString character;
554 if (GetCharAt(aOffset, eGetAt, character)) {
555 *aCharacter = character.First();
556 return NS_OK;
559 return NS_ERROR_INVALID_ARG;
562 nsAccessible*
563 nsHyperTextAccessible::DOMPointToHypertextOffset(nsINode *aNode,
564 PRInt32 aNodeOffset,
565 PRInt32 *aHyperTextOffset,
566 PRBool aIsEndOffset)
568 if (!aHyperTextOffset)
569 return nsnull;
570 *aHyperTextOffset = 0;
572 if (!aNode)
573 return nsnull;
575 PRUint32 addTextOffset = 0;
576 nsINode* findNode = nsnull;
578 if (aNodeOffset == -1) {
579 findNode = aNode;
581 } else if (aNode->IsNodeOfType(nsINode::eTEXT)) {
582 // For text nodes, aNodeOffset comes in as a character offset
583 // Text offset will be added at the end, if we find the offset in this hypertext
584 // We want the "skipped" offset into the text (rendered text without the extra whitespace)
585 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
586 NS_ASSERTION(content, "No nsIContent for dom node");
587 nsIFrame *frame = content->GetPrimaryFrame();
588 NS_ENSURE_TRUE(frame, nsnull);
589 nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &addTextOffset);
590 NS_ENSURE_SUCCESS(rv, nsnull);
591 // Get the child node and
592 findNode = aNode;
594 } else {
595 // findNode could be null if aNodeOffset == # of child nodes, which means
596 // one of two things:
597 // 1) we're at the end of the children, keep findNode = null, so that we get
598 // the last possible offset
599 // 2) there are no children and the passed-in node is mContent, which means
600 // we're an aempty nsIAccessibleText
601 // 3) there are no children, and the passed-in node is not mContent -- use
602 // parentContent for the node to find
604 findNode = aNode->GetChildAt(aNodeOffset);
605 if (!findNode && !aNodeOffset) {
606 if (aNode == GetNode()) {
607 // There are no children, which means this is an empty nsIAccessibleText, in which
608 // case we can only be at hypertext offset 0
609 *aHyperTextOffset = 0;
610 return nsnull;
612 findNode = aNode; // Case #2: there are no children
616 // Get accessible for this findNode, or if that node isn't accessible, use the
617 // accessible for the next DOM node which has one (based on forward depth first search)
618 nsAccessible *descendantAcc = nsnull;
619 if (findNode) {
620 nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
621 if (findContent && findContent->IsHTML() &&
622 findContent->NodeInfo()->Equals(nsAccessibilityAtoms::br) &&
623 findContent->AttrValueIs(kNameSpaceID_None,
624 nsAccessibilityAtoms::mozeditorbogusnode,
625 nsAccessibilityAtoms::_true,
626 eIgnoreCase)) {
627 // This <br> is the hacky "bogus node" used when there is no text in a control
628 *aHyperTextOffset = 0;
629 return nsnull;
631 descendantAcc = GetFirstAvailableAccessible(findNode);
634 // From the descendant, go up and get the immediate child of this hypertext
635 nsAccessible *childAccAtOffset = nsnull;
636 while (descendantAcc) {
637 nsAccessible *parentAcc = descendantAcc->GetParent();
638 if (parentAcc == this) {
639 childAccAtOffset = descendantAcc;
640 break;
643 // This offset no longer applies because the passed-in text object is not a child
644 // of the hypertext. This happens when there are nested hypertexts, e.g.
645 // <div>abc<h1>def</h1>ghi</div>
646 // If the passed-in DOM point was not on a direct child of the hypertext, we will
647 // return the offset for that entire hypertext
648 if (aIsEndOffset) {
649 // Not inclusive, the indicated char comes at index before this offset
650 // If the end offset is after the first character of the passed in object, use 1 for
651 // addTextOffset, to put us after the embedded object char. We'll only treat the offset as
652 // before the embedded object char if we end at the very beginning of the child.
653 addTextOffset = addTextOffset > 0;
655 else {
656 // Start offset, inclusive
657 // Make sure the offset lands on the embedded object character in order to indicate
658 // the true inner offset is inside the subtree for that link
659 addTextOffset =
660 (nsAccUtils::TextLength(descendantAcc) == addTextOffset) ? 1 : 0;
663 descendantAcc = parentAcc;
666 // Loop through, adding offsets until we reach childAccessible
667 // If childAccessible is null we will end up adding up the entire length of
668 // the hypertext, which is good -- it just means our offset node
669 // came after the last accessible child's node
670 PRInt32 childCount = GetChildCount();
672 PRInt32 childIdx = 0;
673 nsAccessible *childAcc = nsnull;
674 for (; childIdx < childCount; childIdx++) {
675 childAcc = mChildren[childIdx];
676 if (childAcc == childAccAtOffset)
677 break;
679 *aHyperTextOffset += nsAccUtils::TextLength(childAcc);
682 if (childIdx < childCount) {
683 *aHyperTextOffset += addTextOffset;
684 NS_ASSERTION(childAcc == childAccAtOffset,
685 "These should be equal whenever we exit loop and childAcc != nsnull");
687 if (childIdx < childCount - 1 ||
688 addTextOffset < nsAccUtils::TextLength(childAccAtOffset)) {
689 // If not at end of last text node, we will return the accessible we were in
690 return childAccAtOffset;
694 return nsnull;
697 nsresult
698 nsHyperTextAccessible::HypertextOffsetToDOMPoint(PRInt32 aHTOffset,
699 nsIDOMNode **aNode,
700 PRInt32 *aOffset)
702 nsCOMPtr<nsIDOMNode> endNode;
703 PRInt32 endOffset;
705 return HypertextOffsetsToDOMRange(aHTOffset, aHTOffset, aNode, aOffset,
706 getter_AddRefs(endNode), &endOffset);
709 nsresult
710 nsHyperTextAccessible::HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset,
711 PRInt32 aEndHTOffset,
712 nsIDOMNode **aStartNode,
713 PRInt32 *aStartOffset,
714 nsIDOMNode **aEndNode,
715 PRInt32 *aEndOffset)
717 NS_ENSURE_ARG_POINTER(aStartNode);
718 *aStartNode = nsnull;
720 NS_ENSURE_ARG_POINTER(aStartOffset);
721 *aStartOffset = -1;
723 NS_ENSURE_ARG_POINTER(aEndNode);
724 *aEndNode = nsnull;
726 NS_ENSURE_ARG_POINTER(aEndOffset);
727 *aEndOffset = -1;
729 // If the given offsets are 0 and associated editor is empty then return
730 // collapsed range with editor root element as range container.
731 if (aStartHTOffset == 0 && aEndHTOffset == 0) {
732 nsCOMPtr<nsIEditor> editor;
733 GetAssociatedEditor(getter_AddRefs(editor));
734 if (editor) {
735 PRBool isEmpty = PR_FALSE;
736 editor->GetDocumentIsEmpty(&isEmpty);
737 if (isEmpty) {
738 nsCOMPtr<nsIDOMElement> editorRootElm;
739 editor->GetRootElement(getter_AddRefs(editorRootElm));
741 nsCOMPtr<nsIDOMNode> editorRoot(do_QueryInterface(editorRootElm));
742 if (editorRoot) {
743 *aStartOffset = *aEndOffset = 0;
744 NS_ADDREF(*aStartNode = editorRoot);
745 NS_ADDREF(*aEndNode = editorRoot);
747 return NS_OK;
753 nsRefPtr<nsAccessible> startAcc, endAcc;
754 PRInt32 startOffset = aStartHTOffset, endOffset = aEndHTOffset;
755 nsIFrame *startFrame = nsnull, *endFrame = nsnull;
757 startFrame = GetPosAndText(startOffset, endOffset, nsnull, &endFrame, nsnull,
758 getter_AddRefs(startAcc), getter_AddRefs(endAcc));
759 if (!startAcc || !endAcc)
760 return NS_ERROR_FAILURE;
762 nsCOMPtr<nsIDOMNode> startNode, endNode;
763 nsresult rv = GetDOMPointByFrameOffset(startFrame, startOffset, startAcc,
764 getter_AddRefs(startNode),
765 &startOffset);
766 NS_ENSURE_SUCCESS(rv, rv);
768 if (aStartHTOffset != aEndHTOffset) {
769 rv = GetDOMPointByFrameOffset(endFrame, endOffset, endAcc,
770 getter_AddRefs(endNode), &endOffset);
771 NS_ENSURE_SUCCESS(rv, rv);
772 } else {
773 endNode = startNode;
774 endOffset = startOffset;
777 NS_ADDREF(*aStartNode = startNode);
778 *aStartOffset = startOffset;
780 NS_ADDREF(*aEndNode = endNode);
781 *aEndOffset = endOffset;
783 return NS_OK;
786 PRInt32
787 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell *aPresShell,
788 nsIFrame *aFromFrame,
789 PRInt32 aFromOffset,
790 nsAccessible *aFromAccessible,
791 nsSelectionAmount aAmount,
792 nsDirection aDirection,
793 PRBool aNeedsStart)
795 const PRBool kIsJumpLinesOk = PR_TRUE; // okay to jump lines
796 const PRBool kIsScrollViewAStop = PR_FALSE; // do not stop at scroll views
797 const PRBool kIsKeyboardSelect = PR_TRUE; // is keyboard selection
798 const PRBool kIsVisualBidi = PR_FALSE; // use visual order for bidi text
800 EWordMovementType wordMovementType = aNeedsStart ? eStartWord : eEndWord;
801 if (aAmount == eSelectLine) {
802 aAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
805 // Ask layout for the new node and offset, after moving the appropriate amount
806 nsPeekOffsetStruct pos;
808 nsresult rv;
809 PRInt32 contentOffset = aFromOffset;
810 if (nsAccUtils::IsText(aFromAccessible)) {
811 nsIFrame *frame = aFromAccessible->GetFrame();
812 NS_ENSURE_TRUE(frame, -1);
814 if (frame->GetType() == nsAccessibilityAtoms::textFrame) {
815 rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset);
816 NS_ENSURE_SUCCESS(rv, -1);
820 pos.SetData(aAmount, aDirection, contentOffset,
821 0, kIsJumpLinesOk, kIsScrollViewAStop, kIsKeyboardSelect, kIsVisualBidi,
822 wordMovementType);
823 rv = aFromFrame->PeekOffset(&pos);
824 if (NS_FAILED(rv)) {
825 if (aDirection == eDirPrevious) {
826 // Use passed-in frame as starting point in failure case for now,
827 // this is a hack to deal with starting on a list bullet frame,
828 // which fails in PeekOffset() because the line iterator doesn't see it.
829 // XXX Need to look at our overall handling of list bullets, which are an odd case
830 pos.mResultContent = aFromFrame->GetContent();
831 PRInt32 endOffsetUnused;
832 aFromFrame->GetOffsets(pos.mContentOffset, endOffsetUnused);
834 else {
835 return -1;
839 // Turn the resulting node and offset into a hyperTextOffset
840 PRInt32 hyperTextOffset;
841 if (!pos.mResultContent)
842 return -1;
844 // If finalAccessible is nsnull, then DOMPointToHypertextOffset() searched
845 // through the hypertext children without finding the node/offset position.
846 nsAccessible *finalAccessible =
847 DOMPointToHypertextOffset(pos.mResultContent, pos.mContentOffset,
848 &hyperTextOffset, aDirection == eDirNext);
850 if (!finalAccessible && aDirection == eDirPrevious) {
851 // If we reached the end during search, this means we didn't find the DOM point
852 // and we're actually at the start of the paragraph
853 hyperTextOffset = 0;
855 else if (aAmount == eSelectBeginLine) {
856 nsAccessible *firstChild = mChildren.SafeElementAt(0, nsnull);
857 // For line selection with needsStart, set start of line exactly to line break
858 if (pos.mContentOffset == 0 && firstChild &&
859 firstChild->Role() == nsIAccessibleRole::ROLE_STATICTEXT &&
860 static_cast<PRInt32>(nsAccUtils::TextLength(firstChild)) == hyperTextOffset) {
861 // XXX Bullet hack -- we should remove this once list bullets use anonymous content
862 hyperTextOffset = 0;
864 if (!aNeedsStart && hyperTextOffset > 0) {
865 -- hyperTextOffset;
868 else if (aAmount == eSelectEndLine && finalAccessible) {
869 // If not at very end of hypertext, we may need change the end of line offset by 1,
870 // to make sure we are in the right place relative to the line ending
871 if (finalAccessible->Role() == nsIAccessibleRole::ROLE_WHITESPACE) { // Landed on <br> hard line break
872 // if aNeedsStart, set end of line exactly 1 character past line break
873 // XXX It would be cleaner if we did not have to have the hard line break check,
874 // and just got the correct results from PeekOffset() for the <br> case -- the returned offset should
875 // come after the new line, as it does in other cases.
876 ++ hyperTextOffset; // Get past hard line break
878 // We are now 1 character past the line break
879 if (!aNeedsStart) {
880 -- hyperTextOffset;
884 return hyperTextOffset;
888 Gets the specified text relative to aBoundaryType, which means:
889 BOUNDARY_CHAR The character before/at/after the offset is returned.
890 BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start.
891 BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end.
892 BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start.
893 BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start.
896 nsresult nsHyperTextAccessible::GetTextHelper(EGetTextType aType, nsAccessibleTextBoundary aBoundaryType,
897 PRInt32 aOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset,
898 nsAString &aText)
900 aText.Truncate();
902 NS_ENSURE_ARG_POINTER(aStartOffset);
903 NS_ENSURE_ARG_POINTER(aEndOffset);
904 *aStartOffset = *aEndOffset = 0;
906 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
907 if (!presShell) {
908 return NS_ERROR_FAILURE;
911 if (aOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
912 aOffset = CharacterCount();
914 if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
915 GetCaretOffset(&aOffset);
916 if (aOffset > 0 && (aBoundaryType == BOUNDARY_LINE_START ||
917 aBoundaryType == BOUNDARY_LINE_END)) {
918 // It is the same character offset when the caret is visually at the very end of a line
919 // or the start of a new line. Getting text at the line should provide the line with the visual caret,
920 // otherwise screen readers will announce the wrong line as the user presses up or down arrow and land
921 // at the end of a line.
922 nsCOMPtr<nsISelection> domSel;
923 nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
924 nsnull, getter_AddRefs(domSel));
925 NS_ENSURE_SUCCESS(rv, rv);
927 nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSel));
928 nsCOMPtr<nsFrameSelection> frameSelection;
929 rv = privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
930 NS_ENSURE_SUCCESS(rv, rv);
932 if (frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
933 -- aOffset; // We are at the start of a line
937 else if (aOffset < 0) {
938 return NS_ERROR_FAILURE;
941 nsSelectionAmount amount;
942 PRBool needsStart = PR_FALSE;
943 switch (aBoundaryType) {
944 case BOUNDARY_CHAR:
945 amount = eSelectCluster;
946 if (aType == eGetAt)
947 aType = eGetAfter; // Avoid returning 2 characters
948 break;
950 case BOUNDARY_WORD_START:
951 needsStart = PR_TRUE;
952 amount = eSelectWord;
953 break;
955 case BOUNDARY_WORD_END:
956 amount = eSelectWord;
957 break;
959 case BOUNDARY_LINE_START:
960 // Newlines are considered at the end of a line. Since getting
961 // the BOUNDARY_LINE_START gets the text from the line-start to the next
962 // line-start, the newline is included at the end of the string.
963 needsStart = PR_TRUE;
964 amount = eSelectLine;
965 break;
967 case BOUNDARY_LINE_END:
968 // Newlines are considered at the end of a line. Since getting
969 // the BOUNDARY_END_START gets the text from the line-end to the next
970 //line-end, the newline is included at the beginning of the string.
971 amount = eSelectLine;
972 break;
974 case BOUNDARY_ATTRIBUTE_RANGE:
976 nsresult rv = GetTextAttributes(PR_FALSE, aOffset,
977 aStartOffset, aEndOffset, nsnull);
978 NS_ENSURE_SUCCESS(rv, rv);
980 return GetText(*aStartOffset, *aEndOffset, aText);
983 default: // Note, sentence support is deprecated and falls through to here
984 return NS_ERROR_INVALID_ARG;
987 PRInt32 startOffset = aOffset + (aBoundaryType == BOUNDARY_LINE_END); // Avoid getting the previous line
988 PRInt32 endOffset = startOffset;
990 // Convert offsets to frame-relative
991 nsRefPtr<nsAccessible> startAcc;
992 nsIFrame *startFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
993 nsnull, getter_AddRefs(startAcc));
995 if (!startFrame) {
996 PRInt32 textLength = CharacterCount();
997 if (aBoundaryType == BOUNDARY_LINE_START && aOffset > 0 && aOffset == textLength) {
998 // Asking for start of line, while on last character
999 if (startAcc)
1000 startFrame = startAcc->GetFrame();
1002 if (!startFrame) {
1003 return aOffset > textLength ? NS_ERROR_FAILURE : NS_OK;
1005 else {
1006 // We're on the last continuation since we're on the last character
1007 startFrame = startFrame->GetLastContinuation();
1011 PRInt32 finalStartOffset, finalEndOffset;
1013 // If aType == eGetAt we'll change both the start and end offset from
1014 // the original offset
1015 if (aType == eGetAfter) {
1016 finalStartOffset = aOffset;
1018 else {
1019 finalStartOffset = GetRelativeOffset(presShell, startFrame, startOffset,
1020 startAcc, amount, eDirPrevious,
1021 needsStart);
1022 NS_ENSURE_TRUE(finalStartOffset >= 0, NS_ERROR_FAILURE);
1025 if (aType == eGetBefore) {
1026 endOffset = aOffset;
1028 else {
1029 // Start moving forward from the start so that we don't get
1030 // 2 words/lines if the offset occurred on whitespace boundary
1031 // Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed
1032 // For BOUNDARY_LINE_END, make sure we start of this line
1033 startOffset = endOffset = finalStartOffset + (aBoundaryType == BOUNDARY_LINE_END);
1034 nsRefPtr<nsAccessible> endAcc;
1035 nsIFrame *endFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
1036 nsnull, getter_AddRefs(endAcc));
1037 if (endAcc && endAcc->Role() == nsIAccessibleRole::ROLE_STATICTEXT) {
1038 // Static text like list bullets will ruin our forward calculation,
1039 // since the caret cannot be in the static text. Start just after the static text.
1040 startOffset = endOffset = finalStartOffset +
1041 (aBoundaryType == BOUNDARY_LINE_END) +
1042 nsAccUtils::TextLength(endAcc);
1044 endFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
1045 nsnull, getter_AddRefs(endAcc));
1047 if (!endFrame) {
1048 return NS_ERROR_FAILURE;
1050 finalEndOffset = GetRelativeOffset(presShell, endFrame, endOffset, endAcc,
1051 amount, eDirNext, needsStart);
1052 NS_ENSURE_TRUE(endOffset >= 0, NS_ERROR_FAILURE);
1053 if (finalEndOffset == aOffset) {
1054 if (aType == eGetAt && amount == eSelectWord) {
1055 // Fix word error for the first character in word: PeekOffset() will return the previous word when
1056 // aOffset points to the first character of the word, but accessibility APIs want the current word
1057 // that the first character is in
1058 return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1060 PRInt32 textLength = CharacterCount();
1061 if (finalEndOffset < textLength) {
1062 // This happens sometimes when current character at finalStartOffset
1063 // is an embedded object character representing another hypertext, that
1064 // the AT really needs to dig into separately
1065 ++ finalEndOffset;
1070 *aStartOffset = finalStartOffset;
1071 *aEndOffset = finalEndOffset;
1073 NS_ASSERTION((finalStartOffset < aOffset && finalEndOffset >= aOffset) || aType != eGetBefore, "Incorrect results for GetTextHelper");
1074 NS_ASSERTION((finalStartOffset <= aOffset && finalEndOffset > aOffset) || aType == eGetBefore, "Incorrect results for GetTextHelper");
1076 GetPosAndText(finalStartOffset, finalEndOffset, &aText);
1077 return NS_OK;
1081 * nsIAccessibleText impl.
1083 NS_IMETHODIMP nsHyperTextAccessible::GetTextBeforeOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
1084 PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
1086 if (aBoundaryType == BOUNDARY_CHAR) {
1087 GetCharAt(aOffset, eGetBefore, aText, aStartOffset, aEndOffset);
1088 return NS_OK;
1091 return GetTextHelper(eGetBefore, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1094 NS_IMETHODIMP nsHyperTextAccessible::GetTextAtOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
1095 PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
1097 if (aBoundaryType == BOUNDARY_CHAR) {
1098 GetCharAt(aOffset, eGetAt, aText, aStartOffset, aEndOffset);
1099 return NS_OK;
1102 return GetTextHelper(eGetAt, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1105 NS_IMETHODIMP nsHyperTextAccessible::GetTextAfterOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
1106 PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
1108 if (aBoundaryType == BOUNDARY_CHAR) {
1109 GetCharAt(aOffset, eGetAfter, aText, aStartOffset, aEndOffset);
1110 return NS_OK;
1113 return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1116 // nsIPersistentProperties
1117 // nsIAccessibleText::getTextAttributes(in boolean includeDefAttrs,
1118 // in long offset,
1119 // out long rangeStartOffset,
1120 // out long rangeEndOffset);
1121 NS_IMETHODIMP
1122 nsHyperTextAccessible::GetTextAttributes(PRBool aIncludeDefAttrs,
1123 PRInt32 aOffset,
1124 PRInt32 *aStartOffset,
1125 PRInt32 *aEndOffset,
1126 nsIPersistentProperties **aAttributes)
1128 // 1. Get each attribute and its ranges one after another.
1129 // 2. As we get each new attribute, we pass the current start and end offsets
1130 // as in/out parameters. In other words, as attributes are collected,
1131 // the attribute range itself can only stay the same or get smaller.
1133 NS_ENSURE_ARG_POINTER(aStartOffset);
1134 *aStartOffset = 0;
1136 NS_ENSURE_ARG_POINTER(aEndOffset);
1137 *aEndOffset = 0;
1139 if (IsDefunct())
1140 return NS_ERROR_FAILURE;
1142 if (aAttributes) {
1143 *aAttributes = nsnull;
1145 nsCOMPtr<nsIPersistentProperties> attributes =
1146 do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
1147 NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
1149 NS_ADDREF(*aAttributes = attributes);
1152 nsAccessible* accAtOffset = GetChildAtOffset(aOffset);
1153 if (!accAtOffset) {
1154 // Offset 0 is correct offset when accessible has empty text. Include
1155 // default attributes if they were requested, otherwise return empty set.
1156 if (aOffset == 0) {
1157 if (aIncludeDefAttrs) {
1158 nsTextAttrsMgr textAttrsMgr(this, PR_TRUE, nsnull, -1);
1159 return textAttrsMgr.GetAttributes(*aAttributes);
1161 return NS_OK;
1163 return NS_ERROR_INVALID_ARG;
1166 PRInt32 accAtOffsetIdx = accAtOffset->GetIndexInParent();
1167 PRInt32 startOffset = GetChildOffset(accAtOffsetIdx);
1168 PRInt32 endOffset = GetChildOffset(accAtOffsetIdx + 1);
1169 PRInt32 offsetInAcc = aOffset - startOffset;
1171 nsTextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, accAtOffset,
1172 accAtOffsetIdx);
1173 nsresult rv = textAttrsMgr.GetAttributes(*aAttributes, &startOffset,
1174 &endOffset);
1175 NS_ENSURE_SUCCESS(rv, rv);
1177 // Compute spelling attributes on text accessible only.
1178 nsIFrame *offsetFrame = accAtOffset->GetFrame();
1179 if (offsetFrame && offsetFrame->GetType() == nsAccessibilityAtoms::textFrame) {
1180 nsCOMPtr<nsIDOMNode> node = accAtOffset->GetDOMNode();
1182 PRInt32 nodeOffset = 0;
1183 nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc,
1184 &nodeOffset);
1185 NS_ENSURE_SUCCESS(rv, rv);
1187 // Set 'misspelled' text attribute.
1188 rv = GetSpellTextAttribute(node, nodeOffset, &startOffset, &endOffset,
1189 aAttributes ? *aAttributes : nsnull);
1190 NS_ENSURE_SUCCESS(rv, rv);
1193 *aStartOffset = startOffset;
1194 *aEndOffset = endOffset;
1195 return NS_OK;
1198 // nsIPersistentProperties
1199 // nsIAccessibleText::defaultTextAttributes
1200 NS_IMETHODIMP
1201 nsHyperTextAccessible::GetDefaultTextAttributes(nsIPersistentProperties **aAttributes)
1203 NS_ENSURE_ARG_POINTER(aAttributes);
1204 *aAttributes = nsnull;
1206 if (IsDefunct())
1207 return NS_ERROR_FAILURE;
1209 nsCOMPtr<nsIPersistentProperties> attributes =
1210 do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
1211 NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
1213 NS_ADDREF(*aAttributes = attributes);
1215 nsTextAttrsMgr textAttrsMgr(this, PR_TRUE);
1216 return textAttrsMgr.GetAttributes(*aAttributes);
1219 PRInt32
1220 nsHyperTextAccessible::GetLevelInternal()
1222 nsIAtom *tag = mContent->Tag();
1223 if (tag == nsAccessibilityAtoms::h1)
1224 return 1;
1225 if (tag == nsAccessibilityAtoms::h2)
1226 return 2;
1227 if (tag == nsAccessibilityAtoms::h3)
1228 return 3;
1229 if (tag == nsAccessibilityAtoms::h4)
1230 return 4;
1231 if (tag == nsAccessibilityAtoms::h5)
1232 return 5;
1233 if (tag == nsAccessibilityAtoms::h6)
1234 return 6;
1236 return nsAccessibleWrap::GetLevelInternal();
1239 nsresult
1240 nsHyperTextAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
1242 nsresult rv = nsAccessibleWrap::GetAttributesInternal(aAttributes);
1243 NS_ENSURE_SUCCESS(rv, rv);
1245 // Indicate when the current object uses block-level formatting
1246 // via formatting: block
1247 // XXX: 'formatting' attribute is deprecated and will be removed in Mozilla2,
1248 // use 'display' attribute instead.
1249 nsIFrame *frame = GetFrame();
1250 if (frame && frame->GetType() == nsAccessibilityAtoms::blockFrame) {
1251 nsAutoString oldValueUnused;
1252 aAttributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"), NS_LITERAL_STRING("block"),
1253 oldValueUnused);
1256 if (gLastFocusedNode == GetNode()) {
1257 PRInt32 lineNumber = GetCaretLineNumber();
1258 if (lineNumber >= 1) {
1259 nsAutoString strLineNumber;
1260 strLineNumber.AppendInt(lineNumber);
1261 nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::lineNumber,
1262 strLineNumber);
1266 // For the html landmark elements we expose them like we do aria landmarks to
1267 // make AT navigation schemes "just work".
1268 if (mContent->Tag() == nsAccessibilityAtoms::nav)
1269 nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
1270 NS_LITERAL_STRING("navigation"));
1271 else if (mContent->Tag() == nsAccessibilityAtoms::header)
1272 nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
1273 NS_LITERAL_STRING("banner"));
1274 else if (mContent->Tag() == nsAccessibilityAtoms::footer)
1275 nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
1276 NS_LITERAL_STRING("contentinfo"));
1277 else if (mContent->Tag() == nsAccessibilityAtoms::aside)
1278 nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
1279 NS_LITERAL_STRING("note"));
1281 return NS_OK;
1285 * Given an offset, the x, y, width, and height values are filled appropriately.
1287 NS_IMETHODIMP nsHyperTextAccessible::GetCharacterExtents(PRInt32 aOffset, PRInt32 *aX, PRInt32 *aY,
1288 PRInt32 *aWidth, PRInt32 *aHeight,
1289 PRUint32 aCoordType)
1291 return GetRangeExtents(aOffset, aOffset + 1, aX, aY, aWidth, aHeight, aCoordType);
1295 * Given a start & end offset, the x, y, width, and height values are filled appropriately.
1297 NS_IMETHODIMP nsHyperTextAccessible::GetRangeExtents(PRInt32 aStartOffset, PRInt32 aEndOffset,
1298 PRInt32 *aX, PRInt32 *aY,
1299 PRInt32 *aWidth, PRInt32 *aHeight,
1300 PRUint32 aCoordType)
1302 nsIntRect boundsRect;
1303 nsIFrame *endFrameUnused;
1304 if (!GetPosAndText(aStartOffset, aEndOffset, nsnull, &endFrameUnused, &boundsRect) ||
1305 boundsRect.IsEmpty()) {
1306 return NS_ERROR_FAILURE;
1309 *aX = boundsRect.x;
1310 *aY = boundsRect.y;
1311 *aWidth = boundsRect.width;
1312 *aHeight = boundsRect.height;
1314 return nsAccUtils::ConvertScreenCoordsTo(aX, aY, aCoordType, this);
1318 * Gets the offset of the character located at coordinates x and y. x and y are interpreted as being relative to
1319 * the screen or this widget's window depending on coords.
1321 NS_IMETHODIMP
1322 nsHyperTextAccessible::GetOffsetAtPoint(PRInt32 aX, PRInt32 aY,
1323 PRUint32 aCoordType, PRInt32 *aOffset)
1325 *aOffset = -1;
1326 nsCOMPtr<nsIPresShell> shell = GetPresShell();
1327 if (!shell) {
1328 return NS_ERROR_FAILURE;
1330 nsIFrame *hyperFrame = GetFrame();
1331 if (!hyperFrame) {
1332 return NS_ERROR_FAILURE;
1334 nsIntRect frameScreenRect = hyperFrame->GetScreenRectExternal();
1336 nsIntPoint coords;
1337 nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType,
1338 this, &coords);
1339 NS_ENSURE_SUCCESS(rv, rv);
1341 // coords are currently screen coordinates, and we need to turn them into
1342 // frame coordinates relative to the current accessible
1343 if (!frameScreenRect.Contains(coords.x, coords.y)) {
1344 return NS_OK; // Not found, will return -1
1346 nsIntPoint pxInHyperText(coords.x - frameScreenRect.x,
1347 coords.y - frameScreenRect.y);
1348 nsPresContext *context = GetPresContext();
1349 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1350 nsPoint pointInHyperText(context->DevPixelsToAppUnits(pxInHyperText.x),
1351 context->DevPixelsToAppUnits(pxInHyperText.y));
1353 // Go through the frames to check if each one has the point.
1354 // When one does, add up the character offsets until we have a match
1356 // We have an point in an accessible child of this, now we need to add up the
1357 // offsets before it to what we already have
1358 PRInt32 offset = 0;
1359 PRInt32 childCount = GetChildCount();
1360 for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
1361 nsAccessible *childAcc = mChildren[childIdx];
1363 nsIFrame *primaryFrame = childAcc->GetFrame();
1364 NS_ENSURE_TRUE(primaryFrame, NS_ERROR_FAILURE);
1366 nsIFrame *frame = primaryFrame;
1367 while (frame) {
1368 nsIContent *content = frame->GetContent();
1369 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
1370 nsPoint pointInFrame = pointInHyperText - frame->GetOffsetToExternal(hyperFrame);
1371 nsSize frameSize = frame->GetSize();
1372 if (pointInFrame.x < frameSize.width && pointInFrame.y < frameSize.height) {
1373 // Finished
1374 if (frame->GetType() == nsAccessibilityAtoms::textFrame) {
1375 nsIFrame::ContentOffsets contentOffsets = frame->GetContentOffsetsFromPointExternal(pointInFrame, PR_TRUE);
1376 if (contentOffsets.IsNull() || contentOffsets.content != content) {
1377 return NS_OK; // Not found, will return -1
1379 PRUint32 addToOffset;
1380 nsresult rv = ContentToRenderedOffset(primaryFrame,
1381 contentOffsets.offset,
1382 &addToOffset);
1383 NS_ENSURE_SUCCESS(rv, rv);
1384 offset += addToOffset;
1386 *aOffset = offset;
1387 return NS_OK;
1389 frame = frame->GetNextContinuation();
1392 offset += nsAccUtils::TextLength(childAcc);
1395 return NS_OK; // Not found, will return -1
1399 ////////////////////////////////////////////////////////////////////////////////
1400 // nsIAccessibleHyperText
1402 NS_IMETHODIMP
1403 nsHyperTextAccessible::GetLinkCount(PRInt32 *aLinkCount)
1405 NS_ENSURE_ARG_POINTER(aLinkCount);
1406 *aLinkCount = 0;
1408 if (IsDefunct())
1409 return NS_ERROR_FAILURE;
1411 *aLinkCount = GetLinkCount();
1412 return NS_OK;
1415 NS_IMETHODIMP
1416 nsHyperTextAccessible::GetLinkAt(PRInt32 aIndex, nsIAccessibleHyperLink** aLink)
1418 NS_ENSURE_ARG_POINTER(aLink);
1419 *aLink = nsnull;
1421 if (IsDefunct())
1422 return NS_ERROR_FAILURE;
1424 nsAccessible* link = GetLinkAt(aIndex);
1425 if (link)
1426 CallQueryInterface(link, aLink);
1428 return NS_OK;
1431 NS_IMETHODIMP
1432 nsHyperTextAccessible::GetLinkIndex(nsIAccessibleHyperLink* aLink,
1433 PRInt32* aIndex)
1435 NS_ENSURE_ARG_POINTER(aLink);
1437 if (IsDefunct())
1438 return NS_ERROR_FAILURE;
1440 nsRefPtr<nsAccessible> link(do_QueryObject(aLink));
1441 *aIndex = GetLinkIndex(link);
1442 return NS_OK;
1445 NS_IMETHODIMP
1446 nsHyperTextAccessible::GetLinkIndexAtOffset(PRInt32 aOffset,
1447 PRInt32* aLinkIndex)
1449 NS_ENSURE_ARG_POINTER(aLinkIndex);
1450 *aLinkIndex = -1; // API says this magic value means 'not found'
1452 if (IsDefunct())
1453 return NS_ERROR_FAILURE;
1455 *aLinkIndex = GetLinkIndexAtOffset(aOffset);
1456 return NS_OK;
1460 * nsIAccessibleEditableText impl.
1462 NS_IMETHODIMP nsHyperTextAccessible::SetAttributes(PRInt32 aStartPos, PRInt32 aEndPos,
1463 nsISupports *aAttributes)
1465 return NS_ERROR_NOT_IMPLEMENTED;
1468 NS_IMETHODIMP nsHyperTextAccessible::SetTextContents(const nsAString &aText)
1470 PRInt32 numChars = CharacterCount();
1471 if (numChars == 0 || NS_SUCCEEDED(DeleteText(0, numChars))) {
1472 return InsertText(aText, 0);
1474 return NS_ERROR_FAILURE;
1477 NS_IMETHODIMP
1478 nsHyperTextAccessible::InsertText(const nsAString &aText, PRInt32 aPosition)
1480 nsCOMPtr<nsIEditor> editor;
1481 GetAssociatedEditor(getter_AddRefs(editor));
1483 nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(editor));
1484 NS_ENSURE_STATE(peditor);
1486 nsresult rv = SetSelectionRange(aPosition, aPosition);
1487 NS_ENSURE_SUCCESS(rv, rv);
1489 return peditor->InsertText(aText);
1492 NS_IMETHODIMP
1493 nsHyperTextAccessible::CopyText(PRInt32 aStartPos, PRInt32 aEndPos)
1495 nsCOMPtr<nsIEditor> editor;
1496 GetAssociatedEditor(getter_AddRefs(editor));
1497 NS_ENSURE_STATE(editor);
1499 nsresult rv = SetSelectionRange(aStartPos, aEndPos);
1500 NS_ENSURE_SUCCESS(rv, rv);
1502 return editor->Copy();
1505 NS_IMETHODIMP
1506 nsHyperTextAccessible::CutText(PRInt32 aStartPos, PRInt32 aEndPos)
1508 nsCOMPtr<nsIEditor> editor;
1509 GetAssociatedEditor(getter_AddRefs(editor));
1510 NS_ENSURE_STATE(editor);
1512 nsresult rv = SetSelectionRange(aStartPos, aEndPos);
1513 NS_ENSURE_SUCCESS(rv, rv);
1515 return editor->Cut();
1518 NS_IMETHODIMP
1519 nsHyperTextAccessible::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos)
1521 nsCOMPtr<nsIEditor> editor;
1522 GetAssociatedEditor(getter_AddRefs(editor));
1523 NS_ENSURE_STATE(editor);
1525 nsresult rv = SetSelectionRange(aStartPos, aEndPos);
1526 NS_ENSURE_SUCCESS(rv, rv);
1528 return editor->DeleteSelection(nsIEditor::eNone);
1531 NS_IMETHODIMP
1532 nsHyperTextAccessible::PasteText(PRInt32 aPosition)
1534 nsCOMPtr<nsIEditor> editor;
1535 GetAssociatedEditor(getter_AddRefs(editor));
1536 NS_ENSURE_STATE(editor);
1538 nsresult rv = SetSelectionRange(aPosition, aPosition);
1539 NS_ENSURE_SUCCESS(rv, rv);
1541 return editor->Paste(nsIClipboard::kGlobalClipboard);
1544 NS_IMETHODIMP
1545 nsHyperTextAccessible::GetAssociatedEditor(nsIEditor **aEditor)
1547 NS_ENSURE_ARG_POINTER(aEditor);
1548 *aEditor = nsnull;
1550 if (IsDefunct())
1551 return NS_ERROR_FAILURE;
1553 if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
1554 // If we're inside an editable container, then return that container's editor
1555 nsCOMPtr<nsIAccessible> ancestor, current = this;
1556 while (NS_SUCCEEDED(current->GetParent(getter_AddRefs(ancestor))) && ancestor) {
1557 nsRefPtr<nsHyperTextAccessible> ancestorTextAccessible;
1558 ancestor->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
1559 getter_AddRefs(ancestorTextAccessible));
1560 if (ancestorTextAccessible) {
1561 // Recursion will stop at container doc because it has its own impl
1562 // of GetAssociatedEditor()
1563 return ancestorTextAccessible->GetAssociatedEditor(aEditor);
1565 current = ancestor;
1567 return NS_OK;
1570 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
1571 nsCoreUtils::GetDocShellTreeItemFor(mContent);
1572 nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(docShellTreeItem));
1573 if (!editingSession)
1574 return NS_OK; // No editing session interface
1576 nsCOMPtr<nsIPresShell> shell = GetPresShell();
1577 NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
1579 nsCOMPtr<nsIDocument> doc = shell->GetDocument();
1580 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1582 nsCOMPtr<nsIEditor> editor;
1583 return editingSession->GetEditorForWindow(doc->GetWindow(), aEditor);
1587 * =================== Caret & Selection ======================
1590 nsresult
1591 nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
1593 nsresult rv = TakeFocus();
1594 NS_ENSURE_SUCCESS(rv, rv);
1596 // Set the selection
1597 SetSelectionBounds(0, aStartPos, aEndPos);
1598 NS_ENSURE_SUCCESS(rv, rv);
1600 // If range 0 was successfully set, clear any additional selection
1601 // ranges remaining from previous selection
1602 nsCOMPtr<nsISelection> domSel;
1603 nsCOMPtr<nsISelectionController> selCon;
1604 GetSelections(nsISelectionController::SELECTION_NORMAL,
1605 getter_AddRefs(selCon), getter_AddRefs(domSel));
1606 if (domSel) {
1607 PRInt32 numRanges;
1608 domSel->GetRangeCount(&numRanges);
1610 for (PRInt32 count = 0; count < numRanges - 1; count ++) {
1611 nsCOMPtr<nsIDOMRange> range;
1612 domSel->GetRangeAt(1, getter_AddRefs(range));
1613 domSel->RemoveRange(range);
1617 if (selCon) {
1618 // XXX I'm not sure this can do synchronous scrolling. If the last param is
1619 // set to true, this calling might flush the pending reflow. See bug 418470.
1620 selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
1621 nsISelectionController::SELECTION_FOCUS_REGION, 0);
1624 return NS_OK;
1627 NS_IMETHODIMP
1628 nsHyperTextAccessible::SetCaretOffset(PRInt32 aCaretOffset)
1630 return SetSelectionRange(aCaretOffset, aCaretOffset);
1634 * Gets the offset position of the caret (cursor).
1636 NS_IMETHODIMP
1637 nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
1639 *aCaretOffset = -1;
1641 // No caret if the focused node is not inside this DOM node and this DOM node
1642 // is not inside of focused node.
1644 nsINode* thisNode = GetNode();
1645 PRBool isInsideOfFocusedNode =
1646 nsCoreUtils::IsAncestorOf(gLastFocusedNode, thisNode);
1648 if (!isInsideOfFocusedNode && thisNode != gLastFocusedNode &&
1649 !nsCoreUtils::IsAncestorOf(thisNode, gLastFocusedNode))
1650 return NS_OK;
1652 // Turn the focus node and offset of the selection into caret hypretext
1653 // offset.
1654 nsCOMPtr<nsISelection> domSel;
1655 nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
1656 nsnull, getter_AddRefs(domSel));
1657 NS_ENSURE_SUCCESS(rv, rv);
1659 nsCOMPtr<nsIDOMNode> focusDOMNode;
1660 rv = domSel->GetFocusNode(getter_AddRefs(focusDOMNode));
1661 NS_ENSURE_SUCCESS(rv, rv);
1663 PRInt32 focusOffset;
1664 rv = domSel->GetFocusOffset(&focusOffset);
1665 NS_ENSURE_SUCCESS(rv, rv);
1667 // No caret if this DOM node is inside of focused node but the selection's
1668 // focus point is not inside of this DOM node.
1669 nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
1670 if (isInsideOfFocusedNode) {
1671 nsINode *resultNode =
1672 nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
1674 if (resultNode != thisNode &&
1675 !nsCoreUtils::IsAncestorOf(thisNode, resultNode))
1676 return NS_OK;
1679 DOMPointToHypertextOffset(focusNode, focusOffset, aCaretOffset);
1680 return NS_OK;
1683 PRInt32 nsHyperTextAccessible::GetCaretLineNumber()
1685 // Provide the line number for the caret, relative to the
1686 // currently focused node. Use a 1-based index
1687 nsCOMPtr<nsISelection> domSel;
1688 GetSelections(nsISelectionController::SELECTION_NORMAL, nsnull,
1689 getter_AddRefs(domSel));
1690 nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSel));
1691 NS_ENSURE_TRUE(privateSelection, -1);
1692 nsCOMPtr<nsFrameSelection> frameSelection;
1693 privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
1694 NS_ENSURE_TRUE(frameSelection, -1);
1696 nsCOMPtr<nsIDOMNode> caretNode;
1697 domSel->GetFocusNode(getter_AddRefs(caretNode));
1698 nsCOMPtr<nsIContent> caretContent = do_QueryInterface(caretNode);
1699 if (!caretContent || !nsCoreUtils::IsAncestorOf(GetNode(), caretContent))
1700 return -1;
1702 PRInt32 caretOffset, returnOffsetUnused;
1703 domSel->GetFocusOffset(&caretOffset);
1704 nsFrameSelection::HINT hint = frameSelection->GetHint();
1705 nsIFrame *caretFrame = frameSelection->GetFrameForNodeOffset(caretContent, caretOffset,
1706 hint, &returnOffsetUnused);
1707 NS_ENSURE_TRUE(caretFrame, -1);
1709 PRInt32 lineNumber = 1;
1710 nsAutoLineIterator lineIterForCaret;
1711 nsIContent *hyperTextContent = IsContent() ? mContent.get() : nsnull;
1712 while (caretFrame) {
1713 if (hyperTextContent == caretFrame->GetContent()) {
1714 return lineNumber; // Must be in a single line hyper text, there is no line iterator
1716 nsIFrame *parentFrame = caretFrame->GetParent();
1717 if (!parentFrame)
1718 break;
1720 // Add lines for the sibling frames before the caret
1721 nsIFrame *sibling = parentFrame->GetFirstChild(nsnull);
1722 while (sibling && sibling != caretFrame) {
1723 nsAutoLineIterator lineIterForSibling = sibling->GetLineIterator();
1724 if (lineIterForSibling) {
1725 // For the frames before that grab all the lines
1726 PRInt32 addLines = lineIterForSibling->GetNumLines();
1727 lineNumber += addLines;
1729 sibling = sibling->GetNextSibling();
1732 // Get the line number relative to the container with lines
1733 if (!lineIterForCaret) { // Add the caret line just once
1734 lineIterForCaret = parentFrame->GetLineIterator();
1735 if (lineIterForCaret) {
1736 // Ancestor of caret
1737 PRInt32 addLines = lineIterForCaret->FindLineContaining(caretFrame);
1738 lineNumber += addLines;
1742 caretFrame = parentFrame;
1745 NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
1746 return lineNumber;
1749 nsresult
1750 nsHyperTextAccessible::GetSelections(PRInt16 aType,
1751 nsISelectionController **aSelCon,
1752 nsISelection **aDomSel,
1753 nsCOMArray<nsIDOMRange>* aRanges)
1755 if (IsDefunct())
1756 return NS_ERROR_FAILURE;
1758 if (aSelCon) {
1759 *aSelCon = nsnull;
1761 if (aDomSel) {
1762 *aDomSel = nsnull;
1764 if (aRanges) {
1765 aRanges->Clear();
1768 nsCOMPtr<nsISelection> domSel;
1769 nsCOMPtr<nsISelectionController> selCon;
1771 nsCOMPtr<nsIEditor> editor;
1772 GetAssociatedEditor(getter_AddRefs(editor));
1773 nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(editor));
1774 if (peditor) {
1775 // Case 1: plain text editor
1776 // This is for form controls which have their own
1777 // selection controller separate from the document, for example
1778 // HTML:input, HTML:textarea, XUL:textbox, etc.
1779 editor->GetSelectionController(getter_AddRefs(selCon));
1781 else {
1782 // Case 2: rich content subtree (can be rich editor)
1783 // This uses the selection controller from the entire document
1784 nsIFrame *frame = GetFrame();
1785 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1787 // Get the selection and selection controller
1788 frame->GetSelectionController(GetPresContext(),
1789 getter_AddRefs(selCon));
1791 NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
1793 selCon->GetSelection(aType, getter_AddRefs(domSel));
1794 NS_ENSURE_TRUE(domSel, NS_ERROR_FAILURE);
1796 if (aSelCon) {
1797 NS_ADDREF(*aSelCon = selCon);
1799 if (aDomSel) {
1800 NS_ADDREF(*aDomSel = domSel);
1803 if (aRanges) {
1804 nsCOMPtr<nsISelection2> selection2(do_QueryInterface(domSel));
1805 NS_ENSURE_TRUE(selection2, NS_ERROR_FAILURE);
1807 nsCOMPtr<nsINode> startNode = GetNode();
1808 if (peditor) {
1809 nsCOMPtr<nsIDOMElement> editorRoot;
1810 editor->GetRootElement(getter_AddRefs(editorRoot));
1811 startNode = do_QueryInterface(editorRoot);
1813 NS_ENSURE_STATE(startNode);
1815 PRUint32 childCount = startNode->GetChildCount();
1816 nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(startNode));
1817 nsresult rv = selection2->
1818 GetRangesForIntervalCOMArray(startDOMNode, 0, startDOMNode, childCount,
1819 PR_TRUE, aRanges);
1820 NS_ENSURE_SUCCESS(rv, rv);
1821 // Remove collapsed ranges
1822 PRInt32 numRanges = aRanges->Count();
1823 for (PRInt32 count = 0; count < numRanges; count ++) {
1824 PRBool isCollapsed;
1825 (*aRanges)[count]->GetCollapsed(&isCollapsed);
1826 if (isCollapsed) {
1827 aRanges->RemoveObjectAt(count);
1828 -- numRanges;
1829 -- count;
1834 return NS_OK;
1838 * Gets the number of selected regions.
1840 NS_IMETHODIMP nsHyperTextAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
1842 nsCOMPtr<nsISelection> domSel;
1843 nsCOMArray<nsIDOMRange> ranges;
1844 nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
1845 nsnull, nsnull, &ranges);
1846 NS_ENSURE_SUCCESS(rv, rv);
1848 *aSelectionCount = ranges.Count();
1850 return NS_OK;
1854 * Gets the start and end offset of the specified selection.
1856 NS_IMETHODIMP nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum, PRInt32 *aStartOffset, PRInt32 *aEndOffset)
1858 *aStartOffset = *aEndOffset = 0;
1860 nsCOMPtr<nsISelection> domSel;
1861 nsCOMArray<nsIDOMRange> ranges;
1862 nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
1863 nsnull, getter_AddRefs(domSel), &ranges);
1864 NS_ENSURE_SUCCESS(rv, rv);
1866 PRInt32 rangeCount = ranges.Count();
1867 if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
1868 return NS_ERROR_INVALID_ARG;
1870 nsCOMPtr<nsIDOMRange> range = ranges[aSelectionNum];
1872 // Get start point
1873 nsCOMPtr<nsIDOMNode> startDOMNode;
1874 range->GetStartContainer(getter_AddRefs(startDOMNode));
1875 nsCOMPtr<nsINode> startNode(do_QueryInterface(startDOMNode));
1876 PRInt32 startOffset;
1877 range->GetStartOffset(&startOffset);
1879 // Get end point
1880 nsCOMPtr<nsIDOMNode> endDOMNode;
1881 range->GetEndContainer(getter_AddRefs(endDOMNode));
1882 nsCOMPtr<nsINode> endNode(do_QueryInterface(endDOMNode));
1883 PRInt32 endOffset;
1884 range->GetEndOffset(&endOffset);
1886 PRInt16 rangeCompareResult;
1887 rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range, &rangeCompareResult);
1888 NS_ENSURE_SUCCESS(rv, rv);
1890 if (rangeCompareResult < 0) {
1891 // Make sure start is before end, by swapping offsets
1892 // This occurs when the user selects backwards in the text
1893 startNode.swap(endNode);
1894 PRInt32 tempOffset = startOffset;
1895 startOffset = endOffset;
1896 endOffset = tempOffset;
1899 nsAccessible *startAccessible =
1900 DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
1901 if (!startAccessible) {
1902 *aStartOffset = 0; // Could not find start point within this hypertext, so starts before
1905 DOMPointToHypertextOffset(endNode, endOffset, aEndOffset, PR_TRUE);
1906 return NS_OK;
1910 * Changes the start and end offset of the specified selection.
1912 NS_IMETHODIMP
1913 nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
1914 PRInt32 aStartOffset,
1915 PRInt32 aEndOffset)
1917 nsCOMPtr<nsISelection> domSel;
1918 nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
1919 nsnull, getter_AddRefs(domSel));
1920 NS_ENSURE_SUCCESS(rv, rv);
1922 // Caret is a collapsed selection
1923 PRBool isOnlyCaret = (aStartOffset == aEndOffset);
1925 PRInt32 rangeCount;
1926 domSel->GetRangeCount(&rangeCount);
1927 nsCOMPtr<nsIDOMRange> range;
1928 if (aSelectionNum == rangeCount) { // Add a range
1929 range = do_CreateInstance(kRangeCID);
1930 NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
1932 else if (aSelectionNum < 0 || aSelectionNum > rangeCount) {
1933 return NS_ERROR_INVALID_ARG;
1935 else {
1936 domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
1937 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
1940 PRInt32 startOffset, endOffset;
1941 nsCOMPtr<nsIDOMNode> startNode, endNode;
1943 rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset,
1944 getter_AddRefs(startNode), &startOffset,
1945 getter_AddRefs(endNode), &endOffset);
1946 NS_ENSURE_SUCCESS(rv, rv);
1948 rv = range->SetStart(startNode, startOffset);
1949 NS_ENSURE_SUCCESS(rv, rv);
1951 rv = isOnlyCaret ? range->Collapse(PR_TRUE) :
1952 range->SetEnd(endNode, endOffset);
1953 NS_ENSURE_SUCCESS(rv, rv);
1955 if (aSelectionNum == rangeCount) { // Add successfully created new range
1956 return domSel->AddRange(range);
1958 return NS_OK;
1962 * Adds a selection bounded by the specified offsets.
1964 NS_IMETHODIMP nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset)
1966 nsCOMPtr<nsISelection> domSel;
1967 nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
1968 nsnull, getter_AddRefs(domSel));
1969 NS_ENSURE_SUCCESS(rv, rv);
1971 PRInt32 rangeCount;
1972 domSel->GetRangeCount(&rangeCount);
1974 return SetSelectionBounds(rangeCount, aStartOffset, aEndOffset);
1978 * Removes the specified selection.
1980 NS_IMETHODIMP nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum)
1982 nsCOMPtr<nsISelection> domSel;
1983 nsresult rv = GetSelections(nsISelectionController::SELECTION_NORMAL,
1984 nsnull, getter_AddRefs(domSel));
1985 NS_ENSURE_SUCCESS(rv, rv);
1987 PRInt32 rangeCount;
1988 domSel->GetRangeCount(&rangeCount);
1989 if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
1990 return NS_ERROR_INVALID_ARG;
1992 nsCOMPtr<nsIDOMRange> range;
1993 domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
1994 return domSel->RemoveRange(range);
1997 // void nsIAccessibleText::
1998 // scrollSubstringTo(in long startIndex, in long endIndex,
1999 // in unsigned long scrollType);
2000 NS_IMETHODIMP
2001 nsHyperTextAccessible::ScrollSubstringTo(PRInt32 aStartIndex, PRInt32 aEndIndex,
2002 PRUint32 aScrollType)
2004 PRInt32 startOffset, endOffset;
2005 nsCOMPtr<nsIDOMNode> startNode, endNode;
2007 nsresult rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex,
2008 getter_AddRefs(startNode),
2009 &startOffset,
2010 getter_AddRefs(endNode),
2011 &endOffset);
2012 NS_ENSURE_SUCCESS(rv, rv);
2014 return nsCoreUtils::ScrollSubstringTo(GetFrame(), startNode, startOffset,
2015 endNode, endOffset, aScrollType);
2018 // void nsIAccessibleText::
2019 // scrollSubstringToPoint(in long startIndex, in long endIndex,
2020 // in unsigned long coordinateType,
2021 // in long x, in long y);
2022 NS_IMETHODIMP
2023 nsHyperTextAccessible::ScrollSubstringToPoint(PRInt32 aStartIndex,
2024 PRInt32 aEndIndex,
2025 PRUint32 aCoordinateType,
2026 PRInt32 aX, PRInt32 aY)
2028 nsIFrame *frame = GetFrame();
2029 if (!frame)
2030 return NS_ERROR_FAILURE;
2032 nsIntPoint coords;
2033 nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
2034 this, &coords);
2035 NS_ENSURE_SUCCESS(rv, rv);
2037 PRInt32 startOffset, endOffset;
2038 nsCOMPtr<nsIDOMNode> startNode, endNode;
2040 rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex,
2041 getter_AddRefs(startNode), &startOffset,
2042 getter_AddRefs(endNode), &endOffset);
2043 NS_ENSURE_SUCCESS(rv, rv);
2045 nsPresContext *presContext = frame->PresContext();
2047 PRBool initialScrolled = PR_FALSE;
2048 nsIFrame *parentFrame = frame;
2049 while ((parentFrame = parentFrame->GetParent())) {
2050 nsIScrollableFrame *scrollableFrame = do_QueryFrame(parentFrame);
2051 if (scrollableFrame) {
2052 if (!initialScrolled) {
2053 // Scroll substring to the given point. Turn the point into percents
2054 // relative scrollable area to use nsCoreUtils::ScrollSubstringTo.
2055 nsIntRect frameRect = parentFrame->GetScreenRectExternal();
2056 PRInt32 devOffsetX = coords.x - frameRect.x;
2057 PRInt32 devOffsetY = coords.y - frameRect.y;
2059 nsPoint offsetPoint(presContext->DevPixelsToAppUnits(devOffsetX),
2060 presContext->DevPixelsToAppUnits(devOffsetY));
2062 nsSize size(parentFrame->GetSize());
2064 // avoid divide by zero
2065 size.width = size.width ? size.width : 1;
2066 size.height = size.height ? size.height : 1;
2068 PRInt16 hPercent = offsetPoint.x * 100 / size.width;
2069 PRInt16 vPercent = offsetPoint.y * 100 / size.height;
2071 rv = nsCoreUtils::ScrollSubstringTo(GetFrame(), startNode, startOffset,
2072 endNode, endOffset,
2073 vPercent, hPercent);
2074 NS_ENSURE_SUCCESS(rv, rv);
2076 initialScrolled = PR_TRUE;
2077 } else {
2078 // Substring was scrolled to the given point already inside its closest
2079 // scrollable area. If there are nested scrollable areas then make
2080 // sure we scroll lower areas to the given point inside currently
2081 // traversed scrollable area.
2082 nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
2085 frame = parentFrame;
2088 return NS_OK;
2091 ////////////////////////////////////////////////////////////////////////////////
2092 // nsAccessible public
2094 void
2095 nsHyperTextAccessible::InvalidateChildren()
2097 mOffsets.Clear();
2099 nsAccessibleWrap::InvalidateChildren();
2102 PRBool
2103 nsHyperTextAccessible::RemoveChild(nsAccessible* aAccessible)
2105 PRInt32 childIndex = aAccessible->GetIndexInParent();
2106 PRInt32 count = mOffsets.Length() - childIndex;
2107 if (count > 0)
2108 mOffsets.RemoveElementsAt(childIndex, count);
2110 return nsAccessible::RemoveChild(aAccessible);
2113 ////////////////////////////////////////////////////////////////////////////////
2114 // nsHyperTextAccessible public static
2116 nsresult nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
2117 PRUint32 *aRenderedOffset)
2119 if (!aFrame) {
2120 // Current frame not rendered -- this can happen if text is set on
2121 // something with display: none
2122 *aRenderedOffset = 0;
2123 return NS_OK;
2125 NS_ASSERTION(aFrame->GetType() == nsAccessibilityAtoms::textFrame,
2126 "Need text frame for offset conversion");
2127 NS_ASSERTION(aFrame->GetPrevContinuation() == nsnull,
2128 "Call on primary frame only");
2130 gfxSkipChars skipChars;
2131 gfxSkipCharsIterator iter;
2132 // Only get info up to original offset, we know that will be larger than skipped offset
2133 nsresult rv = aFrame->GetRenderedText(nsnull, &skipChars, &iter, 0, aContentOffset);
2134 NS_ENSURE_SUCCESS(rv, rv);
2136 PRUint32 ourRenderedStart = iter.GetSkippedOffset();
2137 PRInt32 ourContentStart = iter.GetOriginalOffset();
2139 *aRenderedOffset = iter.ConvertOriginalToSkipped(aContentOffset + ourContentStart) -
2140 ourRenderedStart;
2142 return NS_OK;
2145 nsresult nsHyperTextAccessible::RenderedToContentOffset(nsIFrame *aFrame, PRUint32 aRenderedOffset,
2146 PRInt32 *aContentOffset)
2148 *aContentOffset = 0;
2149 NS_ENSURE_TRUE(aFrame, NS_ERROR_FAILURE);
2151 NS_ASSERTION(aFrame->GetType() == nsAccessibilityAtoms::textFrame,
2152 "Need text frame for offset conversion");
2153 NS_ASSERTION(aFrame->GetPrevContinuation() == nsnull,
2154 "Call on primary frame only");
2156 gfxSkipChars skipChars;
2157 gfxSkipCharsIterator iter;
2158 // We only need info up to skipped offset -- that is what we're converting to original offset
2159 nsresult rv = aFrame->GetRenderedText(nsnull, &skipChars, &iter, 0, aRenderedOffset);
2160 NS_ENSURE_SUCCESS(rv, rv);
2162 PRUint32 ourRenderedStart = iter.GetSkippedOffset();
2163 PRInt32 ourContentStart = iter.GetOriginalOffset();
2165 *aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart;
2167 return NS_OK;
2170 ////////////////////////////////////////////////////////////////////////////////
2171 // nsHyperTextAccessible public
2173 bool
2174 nsHyperTextAccessible::GetCharAt(PRInt32 aOffset, EGetTextType aShift,
2175 nsAString& aChar, PRInt32* aStartOffset,
2176 PRInt32* aEndOffset)
2178 aChar.Truncate();
2180 PRInt32 offset = ConvertMagicOffset(aOffset) + static_cast<PRInt32>(aShift);
2181 PRInt32 childIdx = GetChildIndexAtOffset(offset);
2182 if (childIdx == -1)
2183 return false;
2185 nsAccessible* child = GetChildAt(childIdx);
2186 child->AppendTextTo(aChar, offset - GetChildOffset(childIdx), 1);
2188 if (aStartOffset)
2189 *aStartOffset = offset;
2190 if (aEndOffset)
2191 *aEndOffset = aChar.IsEmpty() ? offset : offset + 1;
2193 return true;
2196 PRInt32
2197 nsHyperTextAccessible::GetChildOffset(PRUint32 aChildIndex,
2198 PRBool aInvalidateAfter)
2200 if (aChildIndex == 0) {
2201 if (aInvalidateAfter)
2202 mOffsets.Clear();
2204 return aChildIndex;
2207 PRInt32 count = mOffsets.Length() - aChildIndex;
2208 if (count > 0) {
2209 if (aInvalidateAfter)
2210 mOffsets.RemoveElementsAt(aChildIndex, count);
2212 return mOffsets[aChildIndex - 1];
2215 PRUint32 lastOffset = mOffsets.IsEmpty() ?
2216 0 : mOffsets[mOffsets.Length() - 1];
2218 EnsureChildren();
2219 while (mOffsets.Length() < aChildIndex) {
2220 nsAccessible* child = mChildren[mOffsets.Length()];
2221 lastOffset += nsAccUtils::TextLength(child);
2222 mOffsets.AppendElement(lastOffset);
2225 return mOffsets[aChildIndex - 1];
2228 PRInt32
2229 nsHyperTextAccessible::GetChildIndexAtOffset(PRUint32 aOffset)
2231 PRUint32 lastOffset = 0;
2232 PRUint32 offsetCount = mOffsets.Length();
2233 if (offsetCount > 0) {
2234 lastOffset = mOffsets[offsetCount - 1];
2235 if (aOffset < lastOffset) {
2236 PRUint32 low = 0, high = offsetCount;
2237 while (high > low) {
2238 PRUint32 mid = (high + low) >> 1;
2239 if (mOffsets[mid] == aOffset)
2240 return mid < offsetCount - 1 ? mid + 1 : mid;
2242 if (mOffsets[mid] < aOffset)
2243 low = mid + 1;
2244 else
2245 high = mid;
2247 if (high == offsetCount)
2248 return -1;
2250 return low;
2254 PRUint32 childCount = GetChildCount();
2255 while (mOffsets.Length() < childCount) {
2256 nsAccessible* child = GetChildAt(mOffsets.Length());
2257 lastOffset += nsAccUtils::TextLength(child);
2258 mOffsets.AppendElement(lastOffset);
2259 if (aOffset < lastOffset)
2260 return mOffsets.Length() - 1;
2263 if (aOffset == lastOffset)
2264 return mOffsets.Length() - 1;
2266 return -1;
2269 ////////////////////////////////////////////////////////////////////////////////
2270 // nsHyperTextAccessible protected
2272 nsresult
2273 nsHyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame *aFrame,
2274 PRInt32 aOffset,
2275 nsIAccessible *aAccessible,
2276 nsIDOMNode **aNode,
2277 PRInt32 *aNodeOffset)
2279 NS_ENSURE_ARG(aAccessible);
2281 nsCOMPtr<nsIDOMNode> node;
2283 if (!aFrame) {
2284 // If the given frame is null then set offset after the DOM node of the
2285 // given accessible.
2286 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
2288 nsCOMPtr<nsIDOMNode> DOMNode;
2289 accessNode->GetDOMNode(getter_AddRefs(DOMNode));
2290 nsCOMPtr<nsIContent> content(do_QueryInterface(DOMNode));
2291 NS_ENSURE_STATE(content);
2293 nsCOMPtr<nsIContent> parent(content->GetParent());
2294 NS_ENSURE_STATE(parent);
2296 *aNodeOffset = parent->IndexOf(content) + 1;
2297 node = do_QueryInterface(parent);
2299 } else if (aFrame->GetType() == nsAccessibilityAtoms::textFrame) {
2300 nsCOMPtr<nsIContent> content(aFrame->GetContent());
2301 NS_ENSURE_STATE(content);
2303 nsIFrame *primaryFrame = content->GetPrimaryFrame();
2304 nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, aNodeOffset);
2305 NS_ENSURE_SUCCESS(rv, rv);
2307 node = do_QueryInterface(content);
2309 } else {
2310 nsCOMPtr<nsIContent> content(aFrame->GetContent());
2311 NS_ENSURE_STATE(content);
2313 nsCOMPtr<nsIContent> parent(content->GetParent());
2314 NS_ENSURE_STATE(parent);
2316 *aNodeOffset = parent->IndexOf(content);
2317 node = do_QueryInterface(parent);
2320 NS_IF_ADDREF(*aNode = node);
2321 return NS_OK;
2324 // nsHyperTextAccessible
2325 nsresult
2326 nsHyperTextAccessible::DOMRangeBoundToHypertextOffset(nsIDOMRange *aRange,
2327 PRBool aIsStartBound,
2328 PRBool aIsStartHTOffset,
2329 PRInt32 *aHTOffset)
2331 nsCOMPtr<nsIDOMNode> DOMNode;
2332 PRInt32 nodeOffset = 0;
2334 nsresult rv;
2335 if (aIsStartBound) {
2336 rv = aRange->GetStartContainer(getter_AddRefs(DOMNode));
2337 NS_ENSURE_SUCCESS(rv, rv);
2339 rv = aRange->GetStartOffset(&nodeOffset);
2340 NS_ENSURE_SUCCESS(rv, rv);
2341 } else {
2342 rv = aRange->GetEndContainer(getter_AddRefs(DOMNode));
2343 NS_ENSURE_SUCCESS(rv, rv);
2345 rv = aRange->GetEndOffset(&nodeOffset);
2346 NS_ENSURE_SUCCESS(rv, rv);
2349 nsCOMPtr<nsINode> node(do_QueryInterface(DOMNode));
2350 nsAccessible *startAcc =
2351 DOMPointToHypertextOffset(node, nodeOffset, aHTOffset);
2353 if (aIsStartHTOffset && !startAcc)
2354 *aHTOffset = 0;
2356 return NS_OK;
2359 // nsHyperTextAccessible
2360 nsresult
2361 nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
2362 PRInt32 aNodeOffset,
2363 PRInt32 *aHTStartOffset,
2364 PRInt32 *aHTEndOffset,
2365 nsIPersistentProperties *aAttributes)
2367 nsCOMArray<nsIDOMRange> ranges;
2368 nsresult rv = GetSelections(nsISelectionController::SELECTION_SPELLCHECK,
2369 nsnull, nsnull, &ranges);
2370 NS_ENSURE_SUCCESS(rv, rv);
2372 PRInt32 rangeCount = ranges.Count();
2373 if (!rangeCount)
2374 return NS_OK;
2376 for (PRInt32 index = 0; index < rangeCount; index++) {
2377 nsCOMPtr<nsIDOMRange> range = ranges[index];
2378 nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
2379 NS_ENSURE_STATE(nsrange);
2381 PRInt16 result;
2382 rv = nsrange->ComparePoint(aNode, aNodeOffset, &result);
2383 NS_ENSURE_SUCCESS(rv, rv);
2384 // ComparePoint checks boundary points, but we need to check that
2385 // text at aNodeOffset is inside the range.
2386 // See also bug 460690.
2387 if (result == 0) {
2388 nsCOMPtr<nsIDOMNode> end;
2389 rv = range->GetEndContainer(getter_AddRefs(end));
2390 NS_ENSURE_SUCCESS(rv, rv);
2391 PRInt32 endOffset;
2392 rv = range->GetEndOffset(&endOffset);
2393 NS_ENSURE_SUCCESS(rv, rv);
2394 if (aNode == end && aNodeOffset == endOffset) {
2395 result = 1;
2399 if (result == 1) { // range is before point
2400 PRInt32 startHTOffset = 0;
2401 rv = DOMRangeBoundToHypertextOffset(range, PR_FALSE, PR_TRUE,
2402 &startHTOffset);
2403 NS_ENSURE_SUCCESS(rv, rv);
2405 if (startHTOffset > *aHTStartOffset)
2406 *aHTStartOffset = startHTOffset;
2408 } else if (result == -1) { // range is after point
2409 PRInt32 endHTOffset = 0;
2410 rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_FALSE,
2411 &endHTOffset);
2412 NS_ENSURE_SUCCESS(rv, rv);
2414 if (endHTOffset < *aHTEndOffset)
2415 *aHTEndOffset = endHTOffset;
2417 } else { // point is in range
2418 PRInt32 startHTOffset = 0;
2419 rv = DOMRangeBoundToHypertextOffset(range, PR_TRUE, PR_TRUE,
2420 &startHTOffset);
2421 NS_ENSURE_SUCCESS(rv, rv);
2423 PRInt32 endHTOffset = 0;
2424 rv = DOMRangeBoundToHypertextOffset(range, PR_FALSE, PR_FALSE,
2425 &endHTOffset);
2426 NS_ENSURE_SUCCESS(rv, rv);
2428 if (startHTOffset > *aHTStartOffset)
2429 *aHTStartOffset = startHTOffset;
2430 if (endHTOffset < *aHTEndOffset)
2431 *aHTEndOffset = endHTOffset;
2433 if (aAttributes) {
2434 nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::invalid,
2435 NS_LITERAL_STRING("spelling"));
2438 return NS_OK;
2442 return NS_OK;