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
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.
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"
41 #include "nsAccessibilityAtoms.h"
42 #include "nsAccessibilityService.h"
43 #include "nsAccessibleTreeWalker.h"
44 #include "nsTextUtils.h"
46 #include "nsPIAccessNode.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"
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
);
76 // nsHyperTextAccessible
79 NS_IMPL_ADDREF_INHERITED(nsHyperTextAccessible
, nsAccessibleWrap
)
80 NS_IMPL_RELEASE_INHERITED(nsHyperTextAccessible
, nsAccessibleWrap
)
82 nsresult
nsHyperTextAccessible::QueryInterface(REFNSIID aIID
, void** aInstancePtr
)
84 *aInstancePtr
= nsnull
;
86 nsCOMPtr
<nsIDOMXULDocument
> xulDoc(do_QueryInterface(mDOMNode
));
87 if (mDOMNode
&& !xulDoc
) {
88 // We need XUL doc check for now because for now nsDocAccessible must
89 // inherit from nsHyperTextAccessible in order for HTML document accessibles
90 // to get support for these interfaces.
91 // However at some point we may push <body> to implement the interfaces and
92 // return nsDocAccessible to inherit from nsAccessibleWrap.
94 if (aIID
.Equals(NS_GET_IID(nsHyperTextAccessible
))) {
95 *aInstancePtr
= static_cast<nsHyperTextAccessible
*>(this);
101 (mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_GRAPHIC
||
102 mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_IMAGE_MAP
||
103 mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_SLIDER
||
104 mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_PROGRESSBAR
||
105 mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_SEPARATOR
)) {
106 // ARIA roles that these interfaces are not appropriate for
107 return nsAccessible::QueryInterface(aIID
, aInstancePtr
);
110 if (aIID
.Equals(NS_GET_IID(nsIAccessibleText
))) {
111 *aInstancePtr
= static_cast<nsIAccessibleText
*>(this);
116 if (aIID
.Equals(NS_GET_IID(nsIAccessibleHyperText
))) {
117 *aInstancePtr
= static_cast<nsIAccessibleHyperText
*>(this);
122 if (aIID
.Equals(NS_GET_IID(nsIAccessibleEditableText
))) {
123 *aInstancePtr
= static_cast<nsIAccessibleEditableText
*>(this);
129 return nsAccessible::QueryInterface(aIID
, aInstancePtr
);
132 nsHyperTextAccessible::nsHyperTextAccessible(nsIDOMNode
* aNode
, nsIWeakReference
* aShell
):
133 nsAccessibleWrap(aNode
, aShell
)
137 NS_IMETHODIMP
nsHyperTextAccessible::GetRole(PRUint32
*aRole
)
139 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mDOMNode
);
141 return NS_ERROR_FAILURE
;
144 nsIAtom
*tag
= content
->Tag();
146 if (tag
== nsAccessibilityAtoms::form
) {
147 *aRole
= nsIAccessibleRole::ROLE_FORM
;
149 else if (tag
== nsAccessibilityAtoms::div
||
150 tag
== nsAccessibilityAtoms::blockquote
) {
151 *aRole
= nsIAccessibleRole::ROLE_SECTION
;
153 else if (tag
== nsAccessibilityAtoms::h1
||
154 tag
== nsAccessibilityAtoms::h2
||
155 tag
== nsAccessibilityAtoms::h3
||
156 tag
== nsAccessibilityAtoms::h4
||
157 tag
== nsAccessibilityAtoms::h5
||
158 tag
== nsAccessibilityAtoms::h6
) {
159 *aRole
= nsIAccessibleRole::ROLE_HEADING
;
162 nsIFrame
*frame
= GetFrame();
163 if (frame
&& frame
->GetType() == nsAccessibilityAtoms::blockFrame
) {
164 *aRole
= nsIAccessibleRole::ROLE_PARAGRAPH
;
167 *aRole
= nsIAccessibleRole::ROLE_TEXT_CONTAINER
; // In ATK this works
174 nsHyperTextAccessible::GetState(PRUint32
*aState
, PRUint32
*aExtraState
)
176 nsresult rv
= nsAccessibleWrap::GetState(aState
, aExtraState
);
177 NS_ENSURE_SUCCESS(rv
, rv
);
178 if (!mDOMNode
|| !aExtraState
)
181 nsCOMPtr
<nsIEditor
> editor
;
182 GetAssociatedEditor(getter_AddRefs(editor
));
185 editor
->GetFlags(&flags
);
186 if (0 == (flags
& nsIPlaintextEditor::eEditorReadonlyMask
)) {
187 *aExtraState
|= nsIAccessibleStates::EXT_STATE_EDITABLE
;
192 GetChildCount(&childCount
);
193 if (childCount
> 0) {
194 *aExtraState
|= nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT
;
200 void nsHyperTextAccessible::CacheChildren()
203 // This node has been shut down
204 mAccChildCount
= eChildCountUninitialized
;
208 // Special case for text entry fields, go directly to editor's root for children
209 if (mAccChildCount
== eChildCountUninitialized
) {
212 if (role
!= nsIAccessibleRole::ROLE_ENTRY
&& role
!= nsIAccessibleRole::ROLE_PASSWORD_TEXT
) {
213 nsAccessible::CacheChildren();
216 nsCOMPtr
<nsIEditor
> editor
;
217 GetAssociatedEditor(getter_AddRefs(editor
));
219 nsAccessible::CacheChildren();
222 mAccChildCount
= 0; // Avoid reentry
223 nsCOMPtr
<nsIDOMElement
> editorRoot
;
224 editor
->GetRootElement(getter_AddRefs(editorRoot
));
225 nsCOMPtr
<nsIDOMNode
> editorRootDOMNode
= do_QueryInterface(editorRoot
);
226 if (!editorRootDOMNode
) {
229 nsAccessibleTreeWalker
walker(mWeakShell
, editorRootDOMNode
, PR_TRUE
);
230 nsCOMPtr
<nsPIAccessible
> privatePrevAccessible
;
231 PRInt32 childCount
= 0;
232 walker
.GetFirstChild();
233 SetFirstChild(walker
.mState
.accessible
);
235 while (walker
.mState
.accessible
) {
237 privatePrevAccessible
= do_QueryInterface(walker
.mState
.accessible
);
238 privatePrevAccessible
->SetParent(this);
239 walker
.GetNextSibling();
240 privatePrevAccessible
->SetNextSibling(walker
.mState
.accessible
);
242 mAccChildCount
= childCount
;
246 // Substring must be entirely within the same text node
247 nsIntRect
nsHyperTextAccessible::GetBoundsForString(nsIFrame
*aFrame
, PRUint32 aStartRenderedOffset
,
248 PRUint32 aEndRenderedOffset
)
250 nsIntRect screenRect
;
251 NS_ENSURE_TRUE(aFrame
, screenRect
);
252 if (aFrame
->GetType() != nsAccessibilityAtoms::textFrame
) {
253 // XXX fallback for non-text frames, happens for bullets right now
254 // but in the future bullets will have proper text frames
255 return aFrame
->GetScreenRectExternal();
258 PRInt32 startContentOffset
, endContentOffset
;
259 nsresult rv
= RenderedToContentOffset(aFrame
, aStartRenderedOffset
, &startContentOffset
);
260 NS_ENSURE_SUCCESS(rv
, screenRect
);
261 rv
= RenderedToContentOffset(aFrame
, aEndRenderedOffset
, &endContentOffset
);
262 NS_ENSURE_SUCCESS(rv
, screenRect
);
265 PRInt32 startContentOffsetInFrame
;
266 // Get the right frame continuation -- not really a child, but a sibling of
267 // the primary frame passed in
268 rv
= aFrame
->GetChildFrameContainingOffset(startContentOffset
, PR_FALSE
,
269 &startContentOffsetInFrame
, &frame
);
270 NS_ENSURE_SUCCESS(rv
, screenRect
);
272 nsCOMPtr
<nsIPresShell
> shell
= GetPresShell();
273 NS_ENSURE_TRUE(shell
, screenRect
);
275 nsCOMPtr
<nsIRenderingContext
> rc
;
276 shell
->CreateRenderingContext(frame
, getter_AddRefs(rc
));
277 NS_ENSURE_TRUE(rc
, screenRect
);
279 const nsStyleFont
*font
= frame
->GetStyleFont();
280 const nsStyleVisibility
*visibility
= frame
->GetStyleVisibility();
282 rv
= rc
->SetFont(font
->mFont
, visibility
->mLangGroup
);
283 NS_ENSURE_SUCCESS(rv
, screenRect
);
285 nsPresContext
*context
= shell
->GetPresContext();
287 while (frame
&& startContentOffset
< endContentOffset
) {
288 // Start with this frame's screen rect, which we will
289 // shrink based on the substring we care about within it.
290 // We will then add that frame to the total screenRect we
292 nsIntRect frameScreenRect
= frame
->GetScreenRectExternal();
294 // Get the length of the substring in this frame that we want the bounds for
295 PRInt32 startFrameTextOffset
, endFrameTextOffset
;
296 frame
->GetOffsets(startFrameTextOffset
, endFrameTextOffset
);
297 PRInt32 frameTotalTextLength
= endFrameTextOffset
- startFrameTextOffset
;
298 PRInt32 seekLength
= endContentOffset
- startContentOffset
;
299 PRInt32 frameSubStringLength
= PR_MIN(frameTotalTextLength
- startContentOffsetInFrame
, seekLength
);
301 // Add the point where the string starts to the frameScreenRect
302 nsPoint frameTextStartPoint
;
303 rv
= frame
->GetPointFromOffset(startContentOffset
, &frameTextStartPoint
);
304 NS_ENSURE_SUCCESS(rv
, nsRect());
305 frameScreenRect
.x
+= context
->AppUnitsToDevPixels(frameTextStartPoint
.x
);
307 // Use the point for the end offset to calculate the width
308 nsPoint frameTextEndPoint
;
309 rv
= frame
->GetPointFromOffset(startContentOffset
+ frameSubStringLength
, &frameTextEndPoint
);
310 NS_ENSURE_SUCCESS(rv
, nsRect());
311 frameScreenRect
.width
= context
->AppUnitsToDevPixels(frameTextEndPoint
.x
- frameTextStartPoint
.x
);
313 screenRect
.UnionRect(frameScreenRect
, screenRect
);
315 // Get ready to loop back for next frame continuation
316 startContentOffset
+= frameSubStringLength
;
317 startContentOffsetInFrame
= 0;
318 frame
= frame
->GetNextContinuation();
325 * Gets the specified text.
328 nsHyperTextAccessible::GetPosAndText(PRInt32
& aStartOffset
, PRInt32
& aEndOffset
,
329 nsAString
*aText
, nsIFrame
**aEndFrame
,
330 nsIntRect
*aBoundsRect
,
331 nsIAccessible
**aStartAcc
,
332 nsIAccessible
**aEndAcc
)
334 if (aStartOffset
== nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT
) {
335 GetCharacterCount(&aStartOffset
);
337 if (aStartOffset
== nsIAccessibleText::TEXT_OFFSET_CARET
) {
338 GetCaretOffset(&aStartOffset
);
340 if (aEndOffset
== nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT
) {
341 GetCharacterCount(&aEndOffset
);
343 if (aEndOffset
== nsIAccessibleText::TEXT_OFFSET_CARET
) {
344 GetCaretOffset(&aEndOffset
);
347 PRInt32 startOffset
= aStartOffset
;
348 PRInt32 endOffset
= aEndOffset
;
349 // XXX this prevents text interface usage on <input type="password">
350 PRBool isPassword
= (Role(this) == nsIAccessibleRole::ROLE_PASSWORD_TEXT
);
352 // Clear out parameters and set up loop
357 const PRInt32 kMaxTextLength
= 32767;
358 endOffset
= kMaxTextLength
; // Max end offset
360 else if (startOffset
> endOffset
) {
364 nsIFrame
*startFrame
= nsnull
;
369 aBoundsRect
->Empty();
377 nsCOMPtr
<nsIAccessible
> accessible
, lastAccessible
;
379 gfxSkipChars skipChars
;
380 gfxSkipCharsIterator iter
;
382 // Loop through children and collect valid offsets, text and bounds
383 // depending on what we need for out parameters
384 while (NextChild(accessible
)) {
385 lastAccessible
= accessible
;
386 nsCOMPtr
<nsPIAccessNode
> accessNode(do_QueryInterface(accessible
));
387 nsIFrame
*frame
= accessNode
->GetFrame();
391 nsIFrame
*primaryFrame
= frame
;
392 if (IsText(accessible
)) {
393 // We only need info up to rendered offset -- that is what we're
394 // converting to content offset
395 PRInt32 substringEndOffset
= -1;
396 PRUint32 ourRenderedStart
= 0;
397 PRInt32 ourContentStart
= 0;
398 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
399 nsresult rv
= frame
->GetRenderedText(nsnull
, &skipChars
, &iter
);
400 if (NS_SUCCEEDED(rv
)) {
401 ourRenderedStart
= iter
.GetSkippedOffset();
402 ourContentStart
= iter
.GetOriginalOffset();
404 iter
.ConvertOriginalToSkipped(skipChars
.GetOriginalCharCount() +
405 ourContentStart
) - ourRenderedStart
;
408 if (substringEndOffset
< 0) {
409 // XXX for non-textframe text like list bullets,
410 // should go away after list bullet rewrite
411 substringEndOffset
= TextLength(accessible
);
413 if (startOffset
< substringEndOffset
) {
414 // Our start is within this substring
415 if (startOffset
> 0 || endOffset
< substringEndOffset
) {
416 // We don't want the whole string for this accessible
417 // Get out the continuing text frame with this offset
418 PRInt32 outStartLineUnused
;
419 PRInt32 contentOffset
;
420 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
421 contentOffset
= iter
.ConvertSkippedToOriginal(startOffset
) +
422 ourRenderedStart
- ourContentStart
;
425 contentOffset
= startOffset
;
427 frame
->GetChildFrameContainingOffset(contentOffset
, PR_TRUE
,
428 &outStartLineUnused
, &frame
);
430 *aEndFrame
= frame
; // We ended in the current frame
432 NS_ADDREF(*aEndAcc
= accessible
);
434 if (substringEndOffset
> endOffset
) {
435 // Need to stop before the end of the available text
436 substringEndOffset
= endOffset
;
438 aEndOffset
= endOffset
;
442 for (PRInt32 count
= startOffset
; count
< substringEndOffset
; count
++)
443 *aText
+= '*'; // Show *'s only for password text
446 nsCOMPtr
<nsPIAccessible
> pAcc(do_QueryInterface(accessible
));
447 pAcc
->AppendTextTo(*aText
, startOffset
,
448 substringEndOffset
- startOffset
);
451 if (aBoundsRect
) { // Caller wants the bounds of the text
452 aBoundsRect
->UnionRect(*aBoundsRect
,
453 GetBoundsForString(primaryFrame
, startOffset
,
454 substringEndOffset
));
458 aStartOffset
= startOffset
;
460 NS_ADDREF(*aStartAcc
= accessible
);
462 // We already started copying in this accessible's string,
463 // for the next accessible we'll start at offset 0
467 // We have not found the start position yet, get the new startOffset
468 // that is relative to next accessible
469 startOffset
-= substringEndOffset
;
471 // The endOffset needs to be relative to the new startOffset
472 endOffset
-= substringEndOffset
;
475 // Embedded object, append marker
476 // XXX Append \n for <br>'s
477 if (startOffset
>= 1) {
483 if (frame
->GetType() == nsAccessibilityAtoms::brFrame
) {
484 *aText
+= kForcedNewLineChar
;
485 } else if (MustPrune(this)) {
486 *aText
+= kImaginaryEmbeddedObjectChar
;
487 // Expose imaginary embedded object character if the accessible
490 *aText
+= kEmbeddedObjectChar
;
494 aBoundsRect
->UnionRect(*aBoundsRect
,
495 frame
->GetScreenRectExternal());
502 NS_ADDREF(*aStartAcc
= accessible
);
507 if (endOffset
<= 0 && startFrame
) {
508 break; // If we don't have startFrame yet, get that in next loop iteration
512 if (aStartAcc
&& !*aStartAcc
) {
513 NS_IF_ADDREF(*aStartAcc
= lastAccessible
);
515 if (aEndFrame
&& !*aEndFrame
) {
516 *aEndFrame
= startFrame
;
517 if (aStartAcc
&& aEndAcc
)
518 NS_IF_ADDREF(*aEndAcc
= *aStartAcc
);
524 NS_IMETHODIMP
nsHyperTextAccessible::GetText(PRInt32 aStartOffset
, PRInt32 aEndOffset
, nsAString
&aText
)
527 return NS_ERROR_FAILURE
;
529 return GetPosAndText(aStartOffset
, aEndOffset
, &aText
) ? NS_OK
: NS_ERROR_FAILURE
;
533 * Gets the character count.
535 NS_IMETHODIMP
nsHyperTextAccessible::GetCharacterCount(PRInt32
*aCharacterCount
)
537 *aCharacterCount
= 0;
539 return NS_ERROR_FAILURE
;
542 nsCOMPtr
<nsIAccessible
> accessible
;
544 while (NextChild(accessible
)) {
545 PRInt32 textLength
= TextLength(accessible
);
546 NS_ENSURE_TRUE(textLength
>= 0, nsnull
);
547 *aCharacterCount
+= textLength
;
553 * Gets the specified character.
555 NS_IMETHODIMP
nsHyperTextAccessible::GetCharacterAtOffset(PRInt32 aOffset
, PRUnichar
*aCharacter
)
558 return NS_ERROR_FAILURE
;
561 nsresult rv
= GetText(aOffset
, aOffset
+ 1, text
);
566 if (text
.IsEmpty()) {
567 return NS_ERROR_FAILURE
;
569 *aCharacter
= text
.First();
573 nsresult
nsHyperTextAccessible::DOMPointToHypertextOffset(nsIDOMNode
* aNode
, PRInt32 aNodeOffset
,
574 PRInt32
* aHyperTextOffset
,
575 nsIAccessible
**aFinalAccessible
,
578 // Turn a DOM Node and offset into an offset into this hypertext.
579 // On failure, return null. On success, return the DOM node which contains the offset.
580 NS_ENSURE_ARG_POINTER(aHyperTextOffset
);
581 *aHyperTextOffset
= 0;
584 return NS_ERROR_FAILURE
;
586 if (aFinalAccessible
) {
587 *aFinalAccessible
= nsnull
;
590 PRUint32 addTextOffset
= 0;
591 nsCOMPtr
<nsIDOMNode
> findNode
;
593 unsigned short nodeType
;
594 aNode
->GetNodeType(&nodeType
);
595 if (aNodeOffset
== -1) {
598 else if (nodeType
== nsIDOMNode::TEXT_NODE
) {
599 // For text nodes, aNodeOffset comes in as a character offset
600 // Text offset will be added at the end, if we find the offset in this hypertext
601 // We want the "skipped" offset into the text (rendered text without the extra whitespace)
602 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
603 NS_ASSERTION(content
, "No nsIContent for dom node");
604 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
605 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
606 nsIFrame
*frame
= presShell
->GetPrimaryFrameFor(content
);
607 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
608 nsresult rv
= ContentToRenderedOffset(frame
, aNodeOffset
, &addTextOffset
);
609 NS_ENSURE_SUCCESS(rv
, rv
);
610 // Get the child node and
614 // For non-text nodes, aNodeOffset comes in as a child node index
615 nsCOMPtr
<nsIContent
> parentContent(do_QueryInterface(aNode
));
616 // Should not happen, but better to protect against crash if doc node is somehow passed in
617 NS_ENSURE_TRUE(parentContent
, NS_ERROR_FAILURE
);
618 // findNode could be null if aNodeOffset == # of child nodes, which means one of two things:
619 // 1) we're at the end of the children, keep findNode = null, so that we get the last possible offset
620 // 2) there are no children and the passed-in node is mDOMNode, which means we're an aempty nsIAccessibleText
621 // 3) there are no children, and the passed-in node is not mDOMNode -- use parentContent for the node to find
623 findNode
= do_QueryInterface(parentContent
->GetChildAt(aNodeOffset
));
624 if (!findNode
&& !aNodeOffset
) {
625 if (SameCOMIdentity(parentContent
, mDOMNode
)) {
626 // There are no children, which means this is an empty nsIAccessibleText, in which
627 // case we can only be at hypertext offset 0
628 *aHyperTextOffset
= 0;
631 findNode
= do_QueryInterface(parentContent
); // Case #2: there are no children
635 // Get accessible for this findNode, or if that node isn't accessible, use the
636 // accessible for the next DOM node which has one (based on forward depth first search)
637 nsCOMPtr
<nsIAccessible
> descendantAccessible
;
639 nsCOMPtr
<nsIContent
> findContent
= do_QueryInterface(findNode
);
640 if (findContent
->IsNodeOfType(nsINode::eHTML
) &&
641 findContent
->NodeInfo()->Equals(nsAccessibilityAtoms::br
)) {
642 nsIContent
*parent
= findContent
->GetParent();
644 parent
->IsRootOfNativeAnonymousSubtree() &&
645 parent
->GetChildCount() == 1) {
646 // This <br> is the only node in a text control, therefore it is the hacky
647 // "bogus node" used when there is no text in a control
648 *aHyperTextOffset
= 0;
652 descendantAccessible
= GetFirstAvailableAccessible(findNode
);
654 // From the descendant, go up and get the immediate child of this hypertext
655 nsCOMPtr
<nsIAccessible
> childAccessible
;
656 while (descendantAccessible
) {
657 nsCOMPtr
<nsIAccessible
> parentAccessible
;
658 descendantAccessible
->GetParent(getter_AddRefs(parentAccessible
));
659 if (this == parentAccessible
) {
660 childAccessible
= descendantAccessible
;
663 // This offset no longer applies because the passed-in text object is not a child
664 // of the hypertext. This happens when there are nested hypertexts, e.g.
665 // <div>abc<h1>def</h1>ghi</div>
666 // If the passed-in DOM point was not on a direct child of the hypertext, we will
667 // return the offset for that entire hypertext
669 // Not inclusive, the indicated char comes at index before this offset
670 // If the end offset is after the first character of the passed in object, use 1 for
671 // addTextOffset, to put us after the embedded object char. We'll only treat the offset as
672 // before the embedded object char if we end at the very beginning of the child.
673 addTextOffset
= addTextOffset
> 0;
676 // Start offset, inclusive
677 // Make sure the offset lands on the embedded object character in order to indicate
678 // the true inner offset is inside the subtree for that link
679 addTextOffset
= (TextLength(descendantAccessible
) == static_cast<PRInt32
>(addTextOffset
)) ? 1 : 0;
681 descendantAccessible
= parentAccessible
;
684 // Loop through, adding offsets until we reach childAccessible
685 // If childAccessible is null we will end up adding up the entire length of
686 // the hypertext, which is good -- it just means our offset node
687 // came after the last accessible child's node
688 nsCOMPtr
<nsIAccessible
> accessible
;
689 while (NextChild(accessible
) && accessible
!= childAccessible
) {
690 PRInt32 textLength
= TextLength(accessible
);
691 NS_ENSURE_TRUE(textLength
>= 0, nsnull
);
692 *aHyperTextOffset
+= textLength
;
695 *aHyperTextOffset
+= addTextOffset
;
696 NS_ASSERTION(accessible
== childAccessible
, "These should be equal whenever we exit loop and accessible != nsnull");
697 if (aFinalAccessible
&& (NextChild(accessible
) || static_cast<PRInt32
>(addTextOffset
) < TextLength(childAccessible
))) {
698 // If not at end of last text node, we will return the accessible we were in
699 NS_ADDREF(*aFinalAccessible
= childAccessible
);
707 nsHyperTextAccessible::HypertextOffsetToDOMPoint(PRInt32 aHTOffset
,
711 nsCOMPtr
<nsIDOMNode
> endNode
;
714 return HypertextOffsetsToDOMRange(aHTOffset
, aHTOffset
, aNode
, aOffset
,
715 getter_AddRefs(endNode
), &endOffset
);
719 nsHyperTextAccessible::HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset
,
720 PRInt32 aEndHTOffset
,
721 nsIDOMNode
**aStartNode
,
722 PRInt32
*aStartOffset
,
723 nsIDOMNode
**aEndNode
,
726 NS_ENSURE_ARG_POINTER(aStartNode
);
727 *aStartNode
= nsnull
;
729 NS_ENSURE_ARG_POINTER(aStartOffset
);
732 NS_ENSURE_ARG_POINTER(aEndNode
);
735 NS_ENSURE_ARG_POINTER(aEndOffset
);
738 // If the given offsets are 0 and associated editor is empty then return
739 // collapsed range with editor root element as range container.
740 if (aStartHTOffset
== 0 && aEndHTOffset
== 0) {
741 nsCOMPtr
<nsIEditor
> editor
;
742 GetAssociatedEditor(getter_AddRefs(editor
));
744 PRBool isEmpty
= PR_FALSE
;
745 editor
->GetDocumentIsEmpty(&isEmpty
);
747 nsCOMPtr
<nsIDOMElement
> editorRootElm
;
748 editor
->GetRootElement(getter_AddRefs(editorRootElm
));
750 nsCOMPtr
<nsIDOMNode
> editorRoot(do_QueryInterface(editorRootElm
));
752 *aStartOffset
= *aEndOffset
= 0;
753 NS_ADDREF(*aStartNode
= editorRoot
);
754 NS_ADDREF(*aEndNode
= editorRoot
);
762 nsCOMPtr
<nsIAccessible
> startAcc
, endAcc
;
763 PRInt32 startOffset
= aStartHTOffset
, endOffset
= aEndHTOffset
;
764 nsIFrame
*startFrame
= nsnull
, *endFrame
= nsnull
;
766 startFrame
= GetPosAndText(startOffset
, endOffset
, nsnull
, &endFrame
, nsnull
,
767 getter_AddRefs(startAcc
), getter_AddRefs(endAcc
));
768 if (!startAcc
|| !endAcc
)
769 return NS_ERROR_FAILURE
;
771 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
772 nsresult rv
= GetDOMPointByFrameOffset(startFrame
, startOffset
, startAcc
,
773 getter_AddRefs(startNode
),
775 NS_ENSURE_SUCCESS(rv
, rv
);
777 if (aStartHTOffset
!= aEndHTOffset
) {
778 rv
= GetDOMPointByFrameOffset(endFrame
, endOffset
, endAcc
,
779 getter_AddRefs(endNode
), &endOffset
);
780 NS_ENSURE_SUCCESS(rv
, rv
);
783 endOffset
= startOffset
;
786 NS_ADDREF(*aStartNode
= startNode
);
787 *aStartOffset
= startOffset
;
789 NS_ADDREF(*aEndNode
= endNode
);
790 *aEndOffset
= endOffset
;
796 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell
*aPresShell
,
797 nsIFrame
*aFromFrame
,
799 nsIAccessible
*aFromAccessible
,
800 nsSelectionAmount aAmount
,
801 nsDirection aDirection
,
804 const PRBool kIsJumpLinesOk
= PR_TRUE
; // okay to jump lines
805 const PRBool kIsScrollViewAStop
= PR_FALSE
; // do not stop at scroll views
806 const PRBool kIsKeyboardSelect
= PR_TRUE
; // is keyboard selection
807 const PRBool kIsVisualBidi
= PR_FALSE
; // use visual order for bidi text
809 EWordMovementType wordMovementType
= aNeedsStart
? eStartWord
: eEndWord
;
810 if (aAmount
== eSelectLine
) {
811 aAmount
= (aDirection
== eDirNext
) ? eSelectEndLine
: eSelectBeginLine
;
814 // Ask layout for the new node and offset, after moving the appropriate amount
815 nsPeekOffsetStruct pos
;
818 PRInt32 contentOffset
= aFromOffset
;
819 if (IsText(aFromAccessible
)) {
820 nsCOMPtr
<nsPIAccessNode
> accessNode(do_QueryInterface(aFromAccessible
));
821 NS_ASSERTION(accessNode
, "nsIAccessible doesn't support nsPIAccessNode");
823 nsIFrame
*frame
= accessNode
->GetFrame();
824 NS_ENSURE_TRUE(frame
, -1);
825 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
826 rv
= RenderedToContentOffset(frame
, aFromOffset
, &contentOffset
);
827 NS_ENSURE_SUCCESS(rv
, -1);
831 pos
.SetData(aAmount
, aDirection
, contentOffset
,
832 0, kIsJumpLinesOk
, kIsScrollViewAStop
, kIsKeyboardSelect
, kIsVisualBidi
,
834 rv
= aFromFrame
->PeekOffset(&pos
);
836 if (aDirection
== eDirPrevious
) {
837 // Use passed-in frame as starting point in failure case for now,
838 // this is a hack to deal with starting on a list bullet frame,
839 // which fails in PeekOffset() because the line iterator doesn't see it.
840 // XXX Need to look at our overall handling of list bullets, which are an odd case
841 pos
.mResultContent
= aFromFrame
->GetContent();
842 PRInt32 endOffsetUnused
;
843 aFromFrame
->GetOffsets(pos
.mContentOffset
, endOffsetUnused
);
850 // Turn the resulting node and offset into a hyperTextOffset
851 PRInt32 hyperTextOffset
;
852 nsCOMPtr
<nsIDOMNode
> resultNode
= do_QueryInterface(pos
.mResultContent
);
853 NS_ENSURE_TRUE(resultNode
, -1);
855 nsCOMPtr
<nsIAccessible
> finalAccessible
;
856 rv
= DOMPointToHypertextOffset(resultNode
, pos
.mContentOffset
, &hyperTextOffset
,
857 getter_AddRefs(finalAccessible
),
858 aDirection
== eDirNext
);
859 // If finalAccessible == nsnull, then DOMPointToHypertextOffset() searched through the hypertext
860 // children without finding the node/offset position
861 NS_ENSURE_SUCCESS(rv
, -1);
863 if (!finalAccessible
&& aDirection
== eDirPrevious
) {
864 // If we reached the end during search, this means we didn't find the DOM point
865 // and we're actually at the start of the paragraph
868 else if (aAmount
== eSelectBeginLine
) {
869 // For line selection with needsStart, set start of line exactly to line break
870 if (pos
.mContentOffset
== 0 && mFirstChild
&&
871 Role(mFirstChild
) == nsIAccessibleRole::ROLE_STATICTEXT
&&
872 TextLength(mFirstChild
) == hyperTextOffset
) {
873 // XXX Bullet hack -- we should remove this once list bullets use anonymous content
876 if (!aNeedsStart
&& hyperTextOffset
> 0) {
880 else if (aAmount
== eSelectEndLine
&& finalAccessible
) {
881 // If not at very end of hypertext, we may need change the end of line offset by 1,
882 // to make sure we are in the right place relative to the line ending
883 if (Role(finalAccessible
) == nsIAccessibleRole::ROLE_WHITESPACE
) { // Landed on <br> hard line break
884 // if aNeedsStart, set end of line exactly 1 character past line break
885 // XXX It would be cleaner if we did not have to have the hard line break check,
886 // and just got the correct results from PeekOffset() for the <br> case -- the returned offset should
887 // come after the new line, as it does in other cases.
888 ++ hyperTextOffset
; // Get past hard line break
890 // We are now 1 character past the line break
896 return hyperTextOffset
;
900 Gets the specified text relative to aBoundaryType, which means:
901 BOUNDARY_CHAR The character before/at/after the offset is returned.
902 BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start.
903 BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end.
904 BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start.
905 BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start.
908 nsresult
nsHyperTextAccessible::GetTextHelper(EGetTextType aType
, nsAccessibleTextBoundary aBoundaryType
,
909 PRInt32 aOffset
, PRInt32
*aStartOffset
, PRInt32
*aEndOffset
,
914 NS_ENSURE_ARG_POINTER(aStartOffset
);
915 NS_ENSURE_ARG_POINTER(aEndOffset
);
916 *aStartOffset
= *aEndOffset
= 0;
918 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
920 return NS_ERROR_FAILURE
;
923 if (aOffset
== nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT
) {
924 GetCharacterCount(&aOffset
);
926 if (aOffset
== nsIAccessibleText::TEXT_OFFSET_CARET
) {
927 GetCaretOffset(&aOffset
);
928 if (aOffset
> 0 && (aBoundaryType
== BOUNDARY_LINE_START
||
929 aBoundaryType
== BOUNDARY_LINE_END
)) {
930 // It is the same character offset when the caret is visually at the very end of a line
931 // or the start of a new line. Getting text at the line should provide the line with the visual caret,
932 // otherwise screen readers will announce the wrong line as the user presses up or down arrow and land
933 // at the end of a line.
934 nsCOMPtr
<nsISelection
> domSel
;
935 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
936 nsnull
, getter_AddRefs(domSel
));
937 NS_ENSURE_SUCCESS(rv
, rv
);
939 nsCOMPtr
<nsISelectionPrivate
> privateSelection(do_QueryInterface(domSel
));
940 nsCOMPtr
<nsFrameSelection
> frameSelection
;
941 rv
= privateSelection
->GetFrameSelection(getter_AddRefs(frameSelection
));
942 NS_ENSURE_SUCCESS(rv
, rv
);
944 if (frameSelection
->GetHint() == nsFrameSelection::HINTLEFT
) {
945 -- aOffset
; // We are at the start of a line
949 else if (aOffset
< 0) {
950 return NS_ERROR_FAILURE
;
953 nsSelectionAmount amount
;
954 PRBool needsStart
= PR_FALSE
;
955 switch (aBoundaryType
) {
957 amount
= eSelectCharacter
;
959 aType
= eGetAfter
; // Avoid returning 2 characters
962 case BOUNDARY_WORD_START
:
963 needsStart
= PR_TRUE
;
964 amount
= eSelectWord
;
967 case BOUNDARY_WORD_END
:
968 amount
= eSelectWord
;
971 case BOUNDARY_LINE_START
:
972 // Newlines are considered at the end of a line. Since getting
973 // the BOUNDARY_LINE_START gets the text from the line-start to the next
974 // line-start, the newline is included at the end of the string.
975 needsStart
= PR_TRUE
;
976 amount
= eSelectLine
;
979 case BOUNDARY_LINE_END
:
980 // Newlines are considered at the end of a line. Since getting
981 // the BOUNDARY_END_START gets the text from the line-end to the next
982 //line-end, the newline is included at the beginning of the string.
983 amount
= eSelectLine
;
986 case BOUNDARY_ATTRIBUTE_RANGE
:
988 nsresult rv
= GetTextAttributes(PR_FALSE
, aOffset
,
989 aStartOffset
, aEndOffset
, nsnull
);
990 NS_ENSURE_SUCCESS(rv
, rv
);
992 return GetText(*aStartOffset
, *aEndOffset
, aText
);
995 default: // Note, sentence support is deprecated and falls through to here
996 return NS_ERROR_INVALID_ARG
;
999 PRInt32 startOffset
= aOffset
+ (aBoundaryType
== BOUNDARY_LINE_END
); // Avoid getting the previous line
1000 PRInt32 endOffset
= startOffset
;
1002 // Convert offsets to frame-relative
1003 nsCOMPtr
<nsIAccessible
> startAcc
;
1004 nsIFrame
*startFrame
= GetPosAndText(startOffset
, endOffset
, nsnull
, nsnull
,
1005 nsnull
, getter_AddRefs(startAcc
));
1009 GetCharacterCount(&textLength
);
1010 if (aBoundaryType
== BOUNDARY_LINE_START
&& aOffset
> 0 && aOffset
== textLength
) {
1011 // Asking for start of line, while on last character
1012 nsCOMPtr
<nsPIAccessNode
> startAccessNode
= do_QueryInterface(startAcc
);
1013 if (startAccessNode
) {
1014 startFrame
= startAccessNode
->GetFrame();
1018 return aOffset
> textLength
? NS_ERROR_FAILURE
: NS_OK
;
1021 // We're on the last continuation since we're on the last character
1022 startFrame
= startFrame
->GetLastContinuation();
1026 PRInt32 finalStartOffset
, finalEndOffset
;
1028 // If aType == eGetAt we'll change both the start and end offset from
1029 // the original offset
1030 if (aType
== eGetAfter
) {
1031 finalStartOffset
= aOffset
;
1034 finalStartOffset
= GetRelativeOffset(presShell
, startFrame
, startOffset
,
1035 startAcc
, amount
, eDirPrevious
,
1037 NS_ENSURE_TRUE(finalStartOffset
>= 0, NS_ERROR_FAILURE
);
1040 if (aType
== eGetBefore
) {
1041 endOffset
= aOffset
;
1044 // Start moving forward from the start so that we don't get
1045 // 2 words/lines if the offset occured on whitespace boundary
1046 // Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed
1047 // For BOUNDARY_LINE_END, make sure we start of this line
1048 startOffset
= endOffset
= finalStartOffset
+ (aBoundaryType
== BOUNDARY_LINE_END
);
1049 nsCOMPtr
<nsIAccessible
> endAcc
;
1050 nsIFrame
*endFrame
= GetPosAndText(startOffset
, endOffset
, nsnull
, nsnull
,
1051 nsnull
, getter_AddRefs(endAcc
));
1052 if (endAcc
&& Role(endAcc
) == nsIAccessibleRole::ROLE_STATICTEXT
) {
1053 // Static text like list bullets will ruin our forward calculation,
1054 // since the caret cannot be in the static text. Start just after the static text.
1055 startOffset
= endOffset
= finalStartOffset
+ (aBoundaryType
== BOUNDARY_LINE_END
) + TextLength(endAcc
);
1056 endFrame
= GetPosAndText(startOffset
, endOffset
, nsnull
, nsnull
,
1057 nsnull
, getter_AddRefs(endAcc
));
1060 return NS_ERROR_FAILURE
;
1062 finalEndOffset
= GetRelativeOffset(presShell
, endFrame
, endOffset
, endAcc
,
1063 amount
, eDirNext
, needsStart
);
1064 NS_ENSURE_TRUE(endOffset
>= 0, NS_ERROR_FAILURE
);
1065 if (finalEndOffset
== aOffset
) {
1066 if (aType
== eGetAt
&& amount
== eSelectWord
) {
1067 // Fix word error for the first character in word: PeekOffset() will return the previous word when
1068 // aOffset points to the first character of the word, but accessibility APIs want the current word
1069 // that the first character is in
1070 return GetTextHelper(eGetAfter
, aBoundaryType
, aOffset
, aStartOffset
, aEndOffset
, aText
);
1073 GetCharacterCount(&textLength
);
1074 if (finalEndOffset
< textLength
) {
1075 // This happens sometimes when current character at finalStartOffset
1076 // is an embedded object character representing another hypertext, that
1077 // the AT really needs to dig into separately
1083 *aStartOffset
= finalStartOffset
;
1084 *aEndOffset
= finalEndOffset
;
1086 NS_ASSERTION((finalStartOffset
< aOffset
&& finalEndOffset
>= aOffset
) || aType
!= eGetBefore
, "Incorrect results for GetTextHelper");
1087 NS_ASSERTION((finalStartOffset
<= aOffset
&& finalEndOffset
> aOffset
) || aType
== eGetBefore
, "Incorrect results for GetTextHelper");
1089 GetPosAndText(finalStartOffset
, finalEndOffset
, &aText
);
1094 * nsIAccessibleText impl.
1096 NS_IMETHODIMP
nsHyperTextAccessible::GetTextBeforeOffset(PRInt32 aOffset
, nsAccessibleTextBoundary aBoundaryType
,
1097 PRInt32
*aStartOffset
, PRInt32
*aEndOffset
, nsAString
& aText
)
1099 return GetTextHelper(eGetBefore
, aBoundaryType
, aOffset
, aStartOffset
, aEndOffset
, aText
);
1102 NS_IMETHODIMP
nsHyperTextAccessible::GetTextAtOffset(PRInt32 aOffset
, nsAccessibleTextBoundary aBoundaryType
,
1103 PRInt32
*aStartOffset
, PRInt32
*aEndOffset
, nsAString
& aText
)
1105 return GetTextHelper(eGetAt
, aBoundaryType
, aOffset
, aStartOffset
, aEndOffset
, aText
);
1108 NS_IMETHODIMP
nsHyperTextAccessible::GetTextAfterOffset(PRInt32 aOffset
, nsAccessibleTextBoundary aBoundaryType
,
1109 PRInt32
*aStartOffset
, PRInt32
*aEndOffset
, nsAString
& aText
)
1111 return GetTextHelper(eGetAfter
, aBoundaryType
, aOffset
, aStartOffset
, aEndOffset
, aText
);
1114 // nsIPersistentProperties
1115 // nsIAccessibleText::getTextAttributes(in boolean includeDefAttrs,
1117 // out long rangeStartOffset,
1118 // out long rangeEndOffset);
1120 nsHyperTextAccessible::GetTextAttributes(PRBool aIncludeDefAttrs
,
1122 PRInt32
*aStartOffset
,
1123 PRInt32
*aEndOffset
,
1124 nsIPersistentProperties
**aAttributes
)
1126 // 1. First we get spell check, then language, then the set of CSS-based
1128 // 2. As we get each new attribute, we pass the current start and end offsets
1129 // as in/out parameters. In other words, as attributes are collected,
1130 // the attribute range itself can only stay the same or get smaller.
1133 // Current: range 5-10
1134 // Adding: range 7-12
1135 // Result: range 7-10
1137 NS_ENSURE_ARG_POINTER(aStartOffset
);
1140 NS_ENSURE_ARG_POINTER(aEndOffset
);
1141 nsresult rv
= GetCharacterCount(aEndOffset
);
1142 NS_ENSURE_SUCCESS(rv
, rv
);
1145 *aAttributes
= nsnull
;
1147 nsCOMPtr
<nsIPersistentProperties
> attributes
=
1148 do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID
);
1149 NS_ENSURE_TRUE(attributes
, NS_ERROR_OUT_OF_MEMORY
);
1151 NS_ADDREF(*aAttributes
= attributes
);
1155 return NS_ERROR_FAILURE
;
1157 nsCOMPtr
<nsIDOMNode
> node
;
1158 PRInt32 nodeOffset
= 0;
1159 rv
= HypertextOffsetToDOMPoint(aOffset
, getter_AddRefs(node
), &nodeOffset
);
1160 NS_ENSURE_SUCCESS(rv
, rv
);
1162 // Set 'misspelled' text attribute.
1163 rv
= GetSpellTextAttribute(node
, nodeOffset
, aStartOffset
, aEndOffset
,
1164 aAttributes
? *aAttributes
: nsnull
);
1165 NS_ENSURE_SUCCESS(rv
, rv
);
1167 nsCOMPtr
<nsIContent
> content(do_QueryInterface(node
));
1168 if (content
&& content
->IsNodeOfType(nsINode::eELEMENT
))
1169 node
= do_QueryInterface(content
->GetChildAt(nodeOffset
));
1174 // Set 'lang' text attribute.
1175 rv
= GetLangTextAttributes(aIncludeDefAttrs
, node
,
1176 aStartOffset
, aEndOffset
,
1177 aAttributes
? *aAttributes
: nsnull
);
1178 NS_ENSURE_SUCCESS(rv
, rv
);
1180 // Set CSS based text attributes.
1181 rv
= GetCSSTextAttributes(aIncludeDefAttrs
, node
,
1182 aStartOffset
, aEndOffset
,
1183 aAttributes
? *aAttributes
: nsnull
);
1187 // nsIPersistentProperties
1188 // nsIAccessibleText::defaultTextAttributes
1190 nsHyperTextAccessible::GetDefaultTextAttributes(nsIPersistentProperties
**aAttributes
)
1192 NS_ENSURE_ARG_POINTER(aAttributes
);
1193 *aAttributes
= nsnull
;
1195 nsCOMPtr
<nsIPersistentProperties
> attributes
=
1196 do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID
);
1197 NS_ENSURE_TRUE(attributes
, NS_ERROR_OUT_OF_MEMORY
);
1199 NS_ADDREF(*aAttributes
= attributes
);
1202 return NS_ERROR_FAILURE
;
1204 nsCOMPtr
<nsIDOMElement
> element
= nsAccUtils::GetDOMElementFor(mDOMNode
);
1206 nsCSSTextAttr
textAttr(PR_TRUE
, element
, nsnull
);
1207 while (textAttr
.Iterate()) {
1209 nsAutoString value
, oldValue
;
1210 if (textAttr
.Get(name
, value
))
1211 attributes
->SetStringProperty(name
, value
, oldValue
);
1214 nsIFrame
*sourceFrame
= nsAccUtils::GetFrameFor(element
);
1216 nsBackgroundTextAttr
backgroundTextAttr(sourceFrame
, nsnull
);
1219 if (backgroundTextAttr
.Get(value
)) {
1220 nsAccUtils::SetAccAttr(attributes
,
1221 nsAccessibilityAtoms::backgroundColor
, value
);
1229 nsHyperTextAccessible::GetAttributesInternal(nsIPersistentProperties
*aAttributes
)
1232 return NS_ERROR_FAILURE
; // Node already shut down
1235 nsresult rv
= nsAccessibleWrap::GetAttributesInternal(aAttributes
);
1236 NS_ENSURE_SUCCESS(rv
, rv
);
1238 nsCOMPtr
<nsIContent
> content(do_QueryInterface(GetRoleContent(mDOMNode
)));
1239 NS_ENSURE_TRUE(content
, NS_ERROR_UNEXPECTED
);
1240 nsIAtom
*tag
= content
->Tag();
1242 PRInt32 headLevel
= 0;
1243 if (tag
== nsAccessibilityAtoms::h1
)
1245 else if (tag
== nsAccessibilityAtoms::h2
)
1247 else if (tag
== nsAccessibilityAtoms::h3
)
1249 else if (tag
== nsAccessibilityAtoms::h4
)
1251 else if (tag
== nsAccessibilityAtoms::h5
)
1253 else if (tag
== nsAccessibilityAtoms::h6
)
1257 nsAutoString strHeadLevel
;
1258 strHeadLevel
.AppendInt(headLevel
);
1259 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::level
,
1263 // Indicate when the current object uses block-level formatting
1264 // via formatting: block
1265 // XXX: 'formatting' attribute is deprecated and will be removed in Mozilla2,
1266 // use 'display' attribute instead.
1267 nsIFrame
*frame
= GetFrame();
1268 if (frame
&& frame
->GetType() == nsAccessibilityAtoms::blockFrame
) {
1269 nsAutoString oldValueUnused
;
1270 aAttributes
->SetStringProperty(NS_LITERAL_CSTRING("formatting"), NS_LITERAL_STRING("block"),
1274 if (gLastFocusedNode
== mDOMNode
) {
1275 PRInt32 lineNumber
= GetCaretLineNumber();
1276 if (lineNumber
>= 1) {
1277 nsAutoString strLineNumber
;
1278 strLineNumber
.AppendInt(lineNumber
);
1279 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::lineNumber
,
1288 * Given an offset, the x, y, width, and height values are filled appropriately.
1290 NS_IMETHODIMP
nsHyperTextAccessible::GetCharacterExtents(PRInt32 aOffset
, PRInt32
*aX
, PRInt32
*aY
,
1291 PRInt32
*aWidth
, PRInt32
*aHeight
,
1292 PRUint32 aCoordType
)
1294 return GetRangeExtents(aOffset
, aOffset
+ 1, aX
, aY
, aWidth
, aHeight
, aCoordType
);
1298 * Given a start & end offset, the x, y, width, and height values are filled appropriately.
1300 NS_IMETHODIMP
nsHyperTextAccessible::GetRangeExtents(PRInt32 aStartOffset
, PRInt32 aEndOffset
,
1301 PRInt32
*aX
, PRInt32
*aY
,
1302 PRInt32
*aWidth
, PRInt32
*aHeight
,
1303 PRUint32 aCoordType
)
1305 nsIntRect boundsRect
;
1306 nsIFrame
*endFrameUnused
;
1307 if (!GetPosAndText(aStartOffset
, aEndOffset
, nsnull
, &endFrameUnused
, &boundsRect
) ||
1308 boundsRect
.IsEmpty()) {
1309 return NS_ERROR_FAILURE
;
1314 *aWidth
= boundsRect
.width
;
1315 *aHeight
= boundsRect
.height
;
1317 return nsAccUtils::ConvertScreenCoordsTo(aX
, aY
, aCoordType
, this);
1321 * Gets the offset of the character located at coordinates x and y. x and y are interpreted as being relative to
1322 * the screen or this widget's window depending on coords.
1325 nsHyperTextAccessible::GetOffsetAtPoint(PRInt32 aX
, PRInt32 aY
,
1326 PRUint32 aCoordType
, PRInt32
*aOffset
)
1329 nsCOMPtr
<nsIPresShell
> shell
= GetPresShell();
1331 return NS_ERROR_FAILURE
;
1333 nsIFrame
*hyperFrame
= GetFrame();
1335 return NS_ERROR_FAILURE
;
1337 nsIntRect frameScreenRect
= hyperFrame
->GetScreenRectExternal();
1340 nsresult rv
= nsAccUtils::ConvertToScreenCoords(aX
, aY
, aCoordType
,
1342 NS_ENSURE_SUCCESS(rv
, rv
);
1344 // coords are currently screen coordinates, and we need to turn them into
1345 // frame coordinates relative to the current accessible
1346 if (!frameScreenRect
.Contains(coords
.x
, coords
.y
)) {
1347 return NS_OK
; // Not found, will return -1
1349 nsPoint
pointInHyperText(coords
.x
- frameScreenRect
.x
,
1350 coords
.y
- frameScreenRect
.y
);
1351 nsPresContext
*context
= GetPresContext();
1352 NS_ENSURE_TRUE(context
, NS_ERROR_FAILURE
);
1353 pointInHyperText
.x
= context
->DevPixelsToAppUnits(pointInHyperText
.x
);
1354 pointInHyperText
.y
= context
->DevPixelsToAppUnits(pointInHyperText
.y
);
1356 // Go through the frames to check if each one has the point.
1357 // When one does, add up the character offsets until we have a match
1359 // We have an point in an accessible child of this, now we need to add up the
1360 // offsets before it to what we already have
1361 nsCOMPtr
<nsIAccessible
> accessible
;
1364 while (NextChild(accessible
)) {
1365 nsCOMPtr
<nsPIAccessNode
> accessNode(do_QueryInterface(accessible
));
1366 nsIFrame
*primaryFrame
= accessNode
->GetFrame();
1367 NS_ENSURE_TRUE(primaryFrame
, NS_ERROR_FAILURE
);
1369 nsIFrame
*frame
= primaryFrame
;
1371 nsIContent
*content
= frame
->GetContent();
1372 NS_ENSURE_TRUE(content
, NS_ERROR_FAILURE
);
1373 nsPoint pointInFrame
= pointInHyperText
- frame
->GetOffsetToExternal(hyperFrame
);
1374 nsSize frameSize
= frame
->GetSize();
1375 if (pointInFrame
.x
< frameSize
.width
&& pointInFrame
.y
< frameSize
.height
) {
1377 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
1378 nsIFrame::ContentOffsets contentOffsets
= frame
->GetContentOffsetsFromPointExternal(pointInFrame
, PR_TRUE
);
1379 if (contentOffsets
.IsNull() || contentOffsets
.content
!= content
) {
1380 return NS_OK
; // Not found, will return -1
1382 PRUint32 addToOffset
;
1383 nsresult rv
= ContentToRenderedOffset(primaryFrame
,
1384 contentOffsets
.offset
,
1386 NS_ENSURE_SUCCESS(rv
, rv
);
1387 offset
+= addToOffset
;
1392 frame
= frame
->GetNextContinuation();
1394 PRInt32 textLength
= TextLength(accessible
);
1395 NS_ENSURE_TRUE(textLength
>= 0, NS_ERROR_FAILURE
);
1396 offset
+= textLength
;
1399 return NS_OK
; // Not found, will return -1
1402 // ------- nsIAccessibleHyperText ---------------
1404 nsHyperTextAccessible::GetLinkCount(PRInt32
*aLinkCount
)
1406 NS_ENSURE_ARG_POINTER(aLinkCount
);
1409 return NS_ERROR_FAILURE
;
1412 nsCOMPtr
<nsIAccessible
> accessible
;
1414 while (NextChild(accessible
)) {
1415 if (IsEmbeddedObject(accessible
)) {
1424 nsHyperTextAccessible::GetLink(PRInt32 aLinkIndex
, nsIAccessibleHyperLink
**aLink
)
1426 NS_ENSURE_ARG_POINTER(aLink
);
1430 return NS_ERROR_FAILURE
;
1432 PRInt32 linkIndex
= aLinkIndex
;
1433 nsCOMPtr
<nsIAccessible
> accessible
;
1434 while (NextChild(accessible
)) {
1435 if (IsEmbeddedObject(accessible
) && linkIndex
-- == 0)
1436 return CallQueryInterface(accessible
, aLink
);
1439 return NS_ERROR_INVALID_ARG
;
1443 nsHyperTextAccessible::GetLinkIndex(PRInt32 aCharIndex
, PRInt32
*aLinkIndex
)
1445 NS_ENSURE_ARG_POINTER(aLinkIndex
);
1446 *aLinkIndex
= -1; // API says this magic value means 'not found'
1448 PRInt32 characterCount
= 0;
1449 PRInt32 linkIndex
= 0;
1451 return NS_ERROR_FAILURE
;
1454 nsCOMPtr
<nsIAccessible
> accessible
;
1456 while (NextChild(accessible
) && characterCount
<= aCharIndex
) {
1457 PRUint32 role
= Role(accessible
);
1458 if (role
== nsIAccessibleRole::ROLE_TEXT_LEAF
||
1459 role
== nsIAccessibleRole::ROLE_STATICTEXT
) {
1460 PRInt32 textLength
= TextLength(accessible
);
1461 NS_ENSURE_TRUE(textLength
>= 0, NS_ERROR_FAILURE
);
1462 characterCount
+= textLength
;
1465 if (characterCount
++ == aCharIndex
) {
1466 *aLinkIndex
= linkIndex
;
1469 if (role
!= nsIAccessibleRole::ROLE_WHITESPACE
) {
1478 * nsIAccessibleEditableText impl.
1480 NS_IMETHODIMP
nsHyperTextAccessible::SetAttributes(PRInt32 aStartPos
, PRInt32 aEndPos
,
1481 nsISupports
*aAttributes
)
1483 return NS_ERROR_NOT_IMPLEMENTED
;
1486 NS_IMETHODIMP
nsHyperTextAccessible::SetTextContents(const nsAString
&aText
)
1489 GetCharacterCount(&numChars
);
1490 if (numChars
== 0 || NS_SUCCEEDED(DeleteText(0, numChars
))) {
1491 return InsertText(aText
, 0);
1493 return NS_ERROR_FAILURE
;
1496 NS_IMETHODIMP
nsHyperTextAccessible::InsertText(const nsAString
&aText
, PRInt32 aPosition
)
1498 if (NS_SUCCEEDED(SetCaretOffset(aPosition
))) {
1499 nsCOMPtr
<nsIEditor
> editor
;
1500 GetAssociatedEditor(getter_AddRefs(editor
));
1501 nsCOMPtr
<nsIPlaintextEditor
> peditor(do_QueryInterface(editor
));
1502 return peditor
? peditor
->InsertText(aText
) : NS_ERROR_FAILURE
;
1505 return NS_ERROR_FAILURE
;
1508 NS_IMETHODIMP
nsHyperTextAccessible::CopyText(PRInt32 aStartPos
, PRInt32 aEndPos
)
1510 nsCOMPtr
<nsIEditor
> editor
;
1511 GetAssociatedEditor(getter_AddRefs(editor
));
1512 if (editor
&& NS_SUCCEEDED(SetSelectionRange(aStartPos
, aEndPos
)))
1513 return editor
->Copy();
1515 return NS_ERROR_FAILURE
;
1518 NS_IMETHODIMP
nsHyperTextAccessible::CutText(PRInt32 aStartPos
, PRInt32 aEndPos
)
1520 nsCOMPtr
<nsIEditor
> editor
;
1521 GetAssociatedEditor(getter_AddRefs(editor
));
1522 if (editor
&& NS_SUCCEEDED(SetSelectionRange(aStartPos
, aEndPos
)))
1523 return editor
->Cut();
1525 return NS_ERROR_FAILURE
;
1528 NS_IMETHODIMP
nsHyperTextAccessible::DeleteText(PRInt32 aStartPos
, PRInt32 aEndPos
)
1530 nsCOMPtr
<nsIEditor
> editor
;
1531 GetAssociatedEditor(getter_AddRefs(editor
));
1532 if (editor
&& NS_SUCCEEDED(SetSelectionRange(aStartPos
, aEndPos
)))
1533 return editor
->DeleteSelection(nsIEditor::eNone
);
1535 return NS_ERROR_FAILURE
;
1538 NS_IMETHODIMP
nsHyperTextAccessible::PasteText(PRInt32 aPosition
)
1540 nsCOMPtr
<nsIEditor
> editor
;
1541 GetAssociatedEditor(getter_AddRefs(editor
));
1542 if (editor
&& NS_SUCCEEDED(SetCaretOffset(aPosition
)))
1543 return editor
->Paste(nsIClipboard::kGlobalClipboard
);
1545 return NS_ERROR_FAILURE
;
1549 nsHyperTextAccessible::GetAssociatedEditor(nsIEditor
**aEditor
)
1551 NS_ENSURE_ARG_POINTER(aEditor
);
1554 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mDOMNode
);
1555 NS_ENSURE_TRUE(content
, NS_ERROR_FAILURE
);
1557 if (!content
->HasFlag(NODE_IS_EDITABLE
)) {
1558 // If we're inside an editable container, then return that container's editor
1559 nsCOMPtr
<nsIAccessible
> ancestor
, current
= this;
1560 while (NS_SUCCEEDED(current
->GetParent(getter_AddRefs(ancestor
))) && ancestor
) {
1561 nsRefPtr
<nsHyperTextAccessible
> ancestorTextAccessible
;
1562 ancestor
->QueryInterface(NS_GET_IID(nsHyperTextAccessible
),
1563 getter_AddRefs(ancestorTextAccessible
));
1564 if (ancestorTextAccessible
) {
1565 // Recursion will stop at container doc because it has its own impl
1566 // of GetAssociatedEditor()
1567 return ancestorTextAccessible
->GetAssociatedEditor(aEditor
);
1574 nsCOMPtr
<nsIDocShellTreeItem
> docShellTreeItem
=
1575 nsAccUtils::GetDocShellTreeItemFor(mDOMNode
);
1576 nsCOMPtr
<nsIEditingSession
> editingSession(do_GetInterface(docShellTreeItem
));
1577 if (!editingSession
)
1578 return NS_OK
; // No editing session interface
1580 nsCOMPtr
<nsIPresShell
> shell
= GetPresShell();
1581 NS_ENSURE_TRUE(shell
, NS_ERROR_FAILURE
);
1583 nsCOMPtr
<nsIDocument
> doc
= shell
->GetDocument();
1584 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
1586 nsCOMPtr
<nsIEditor
> editor
;
1587 return editingSession
->GetEditorForWindow(doc
->GetWindow(), aEditor
);
1591 * =================== Caret & Selection ======================
1594 nsresult
nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos
, PRInt32 aEndPos
)
1596 // Set the selection
1597 nsresult rv
= 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
));
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
);
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
, PR_FALSE
);
1627 NS_IMETHODIMP
nsHyperTextAccessible::SetCaretOffset(PRInt32 aCaretOffset
)
1629 return SetSelectionRange(aCaretOffset
, aCaretOffset
);
1633 * Gets the offset position of the caret (cursor).
1635 NS_IMETHODIMP
nsHyperTextAccessible::GetCaretOffset(PRInt32
*aCaretOffset
)
1639 nsCOMPtr
<nsISelection
> domSel
;
1640 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1641 nsnull
, getter_AddRefs(domSel
));
1642 NS_ENSURE_SUCCESS(rv
, rv
);
1644 nsCOMPtr
<nsIDOMNode
> caretNode
;
1645 rv
= domSel
->GetFocusNode(getter_AddRefs(caretNode
));
1646 NS_ENSURE_SUCCESS(rv
, rv
);
1648 PRInt32 caretOffset
;
1649 domSel
->GetFocusOffset(&caretOffset
);
1651 return DOMPointToHypertextOffset(caretNode
, caretOffset
, aCaretOffset
);
1654 PRInt32
nsHyperTextAccessible::GetCaretLineNumber()
1656 // Provide the line number for the caret, relative to the
1657 // currently focused node. Use a 1-based index
1658 nsCOMPtr
<nsISelection
> domSel
;
1659 GetSelections(nsISelectionController::SELECTION_NORMAL
, nsnull
,
1660 getter_AddRefs(domSel
));
1661 nsCOMPtr
<nsISelectionPrivate
> privateSelection(do_QueryInterface(domSel
));
1662 NS_ENSURE_TRUE(privateSelection
, -1);
1663 nsCOMPtr
<nsFrameSelection
> frameSelection
;
1664 privateSelection
->GetFrameSelection(getter_AddRefs(frameSelection
));
1665 NS_ENSURE_TRUE(frameSelection
, -1);
1667 nsCOMPtr
<nsIDOMNode
> caretNode
;
1668 domSel
->GetFocusNode(getter_AddRefs(caretNode
));
1669 nsCOMPtr
<nsIContent
> caretContent
= do_QueryInterface(caretNode
);
1670 if (!caretContent
|| !nsAccUtils::IsAncestorOf(mDOMNode
, caretNode
)) {
1674 PRInt32 caretOffset
, returnOffsetUnused
;
1675 domSel
->GetFocusOffset(&caretOffset
);
1676 nsFrameSelection::HINT hint
= frameSelection
->GetHint();
1677 nsIFrame
*caretFrame
= frameSelection
->GetFrameForNodeOffset(caretContent
, caretOffset
,
1678 hint
, &returnOffsetUnused
);
1679 NS_ENSURE_TRUE(caretFrame
, -1);
1681 PRInt32 lineNumber
= 1;
1682 nsCOMPtr
<nsILineIterator
> lineIterForCaret
;
1683 nsCOMPtr
<nsIContent
> hyperTextContent
= do_QueryInterface(mDOMNode
);
1684 while (caretFrame
) {
1685 if (hyperTextContent
== caretFrame
->GetContent()) {
1686 return lineNumber
; // Must be in a single line hyper text, there is no line iterator
1688 nsIFrame
*parentFrame
= caretFrame
->GetParent();
1692 // Add lines for the sibling frames before the caret
1693 nsIFrame
*sibling
= parentFrame
->GetFirstChild(nsnull
);
1694 while (sibling
&& sibling
!= caretFrame
) {
1695 nsCOMPtr
<nsILineIterator
> lineIterForSibling
= do_QueryInterface(sibling
);
1696 if (lineIterForSibling
) {
1698 // For the frames before that grab all the lines
1699 lineIterForSibling
->GetNumLines(&addLines
);
1700 lineNumber
+= addLines
;
1702 sibling
= sibling
->GetNextSibling();
1705 // Get the line number relative to the container with lines
1706 if (!lineIterForCaret
) { // Add the caret line just once
1707 lineIterForCaret
= do_QueryInterface(parentFrame
);
1708 if (lineIterForCaret
) {
1709 // Ancestor of caret
1711 lineIterForCaret
->FindLineContaining(caretFrame
, &addLines
);
1712 lineNumber
+= addLines
;
1716 caretFrame
= parentFrame
;
1719 NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
1724 nsHyperTextAccessible::GetSelections(PRInt16 aType
,
1725 nsISelectionController
**aSelCon
,
1726 nsISelection
**aDomSel
,
1727 nsCOMArray
<nsIDOMRange
>* aRanges
)
1730 return NS_ERROR_FAILURE
;
1742 nsCOMPtr
<nsISelection
> domSel
;
1743 nsCOMPtr
<nsISelectionController
> selCon
;
1745 nsCOMPtr
<nsIEditor
> editor
;
1746 GetAssociatedEditor(getter_AddRefs(editor
));
1747 nsCOMPtr
<nsIPlaintextEditor
> peditor(do_QueryInterface(editor
));
1749 // Case 1: plain text editor
1750 // This is for form controls which have their own
1751 // selection controller separate from the document, for example
1752 // HTML:input, HTML:textarea, XUL:textbox, etc.
1753 editor
->GetSelectionController(getter_AddRefs(selCon
));
1756 // Case 2: rich content subtree (can be rich editor)
1757 // This uses the selection controller from the entire document
1758 nsIFrame
*frame
= GetFrame();
1759 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
1761 // Get the selection and selection controller
1762 frame
->GetSelectionController(GetPresContext(),
1763 getter_AddRefs(selCon
));
1765 NS_ENSURE_TRUE(selCon
, NS_ERROR_FAILURE
);
1767 selCon
->GetSelection(aType
, getter_AddRefs(domSel
));
1768 NS_ENSURE_TRUE(domSel
, NS_ERROR_FAILURE
);
1771 NS_ADDREF(*aSelCon
= selCon
);
1774 NS_ADDREF(*aDomSel
= domSel
);
1778 nsCOMPtr
<nsISelection2
> selection2(do_QueryInterface(domSel
));
1779 NS_ENSURE_TRUE(selection2
, NS_ERROR_FAILURE
);
1781 nsCOMPtr
<nsIDOMNode
> startNode(mDOMNode
);
1783 nsCOMPtr
<nsIDOMElement
> editorRoot
;
1784 editor
->GetRootElement(getter_AddRefs(editorRoot
));
1785 startNode
= do_QueryInterface(editorRoot
);
1787 NS_ENSURE_STATE(startNode
);
1789 nsCOMPtr
<nsIDOMNodeList
> childNodes
;
1790 nsresult rv
= startNode
->GetChildNodes(getter_AddRefs(childNodes
));
1791 NS_ENSURE_SUCCESS(rv
, rv
);
1792 PRUint32 numChildren
;
1793 rv
= childNodes
->GetLength(&numChildren
);
1794 NS_ENSURE_SUCCESS(rv
, rv
);
1795 rv
= selection2
->GetRangesForIntervalCOMArray(startNode
, 0,
1796 startNode
, numChildren
,
1798 NS_ENSURE_SUCCESS(rv
, rv
);
1799 // Remove collapsed ranges
1800 PRInt32 numRanges
= aRanges
->Count();
1801 for (PRInt32 count
= 0; count
< numRanges
; count
++) {
1803 (*aRanges
)[count
]->GetCollapsed(&isCollapsed
);
1805 aRanges
->RemoveObjectAt(count
);
1816 * Gets the number of selected regions.
1818 NS_IMETHODIMP
nsHyperTextAccessible::GetSelectionCount(PRInt32
*aSelectionCount
)
1820 nsCOMPtr
<nsISelection
> domSel
;
1821 nsCOMArray
<nsIDOMRange
> ranges
;
1822 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1823 nsnull
, nsnull
, &ranges
);
1824 NS_ENSURE_SUCCESS(rv
, rv
);
1826 *aSelectionCount
= ranges
.Count();
1832 * Gets the start and end offset of the specified selection.
1834 NS_IMETHODIMP
nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum
, PRInt32
*aStartOffset
, PRInt32
*aEndOffset
)
1836 *aStartOffset
= *aEndOffset
= 0;
1838 nsCOMPtr
<nsISelection
> domSel
;
1839 nsCOMArray
<nsIDOMRange
> ranges
;
1840 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1841 nsnull
, getter_AddRefs(domSel
), &ranges
);
1842 NS_ENSURE_SUCCESS(rv
, rv
);
1844 PRInt32 rangeCount
= ranges
.Count();
1845 if (aSelectionNum
< 0 || aSelectionNum
>= rangeCount
)
1846 return NS_ERROR_INVALID_ARG
;
1848 nsCOMPtr
<nsIDOMRange
> range
= ranges
[aSelectionNum
];
1851 nsCOMPtr
<nsIDOMNode
> startNode
;
1852 range
->GetStartContainer(getter_AddRefs(startNode
));
1853 PRInt32 startOffset
;
1854 range
->GetStartOffset(&startOffset
);
1857 nsCOMPtr
<nsIDOMNode
> endNode
;
1858 range
->GetEndContainer(getter_AddRefs(endNode
));
1860 range
->GetEndOffset(&endOffset
);
1862 PRInt16 rangeCompareResult
;
1863 rv
= range
->CompareBoundaryPoints(nsIDOMRange::START_TO_END
, range
, &rangeCompareResult
);
1864 NS_ENSURE_SUCCESS(rv
, rv
);
1866 if (rangeCompareResult
< 0) {
1867 // Make sure start is before end, by swapping offsets
1868 // This occurs when the user selects backwards in the text
1869 startNode
.swap(endNode
);
1870 PRInt32 tempOffset
= startOffset
;
1871 startOffset
= endOffset
;
1872 endOffset
= tempOffset
;
1875 nsCOMPtr
<nsIAccessible
> startAccessible
;
1876 rv
= DOMPointToHypertextOffset(startNode
, startOffset
, aStartOffset
, getter_AddRefs(startAccessible
));
1877 NS_ENSURE_SUCCESS(rv
, rv
);
1878 if (!startAccessible
) {
1879 *aStartOffset
= 0; // Could not find start point within this hypertext, so starts before
1882 return DOMPointToHypertextOffset(endNode
, endOffset
, aEndOffset
, nsnull
, PR_TRUE
);
1886 * Changes the start and end offset of the specified selection.
1889 nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum
,
1890 PRInt32 aStartOffset
,
1893 nsCOMPtr
<nsISelection
> domSel
;
1894 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1895 nsnull
, getter_AddRefs(domSel
));
1896 NS_ENSURE_SUCCESS(rv
, rv
);
1898 // Caret is a collapsed selection
1899 PRBool isOnlyCaret
= (aStartOffset
== aEndOffset
);
1902 domSel
->GetRangeCount(&rangeCount
);
1903 nsCOMPtr
<nsIDOMRange
> range
;
1904 if (aSelectionNum
== rangeCount
) { // Add a range
1905 range
= do_CreateInstance(kRangeCID
);
1906 NS_ENSURE_TRUE(range
, NS_ERROR_OUT_OF_MEMORY
);
1908 else if (aSelectionNum
< 0 || aSelectionNum
> rangeCount
) {
1909 return NS_ERROR_INVALID_ARG
;
1912 domSel
->GetRangeAt(aSelectionNum
, getter_AddRefs(range
));
1913 NS_ENSURE_TRUE(range
, NS_ERROR_FAILURE
);
1916 PRInt32 startOffset
, endOffset
;
1917 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
1919 rv
= HypertextOffsetsToDOMRange(aStartOffset
, aEndOffset
,
1920 getter_AddRefs(startNode
), &startOffset
,
1921 getter_AddRefs(endNode
), &endOffset
);
1922 NS_ENSURE_SUCCESS(rv
, rv
);
1924 rv
= range
->SetStart(startNode
, startOffset
);
1925 NS_ENSURE_SUCCESS(rv
, rv
);
1927 rv
= isOnlyCaret
? range
->Collapse(PR_TRUE
) :
1928 range
->SetEnd(endNode
, endOffset
);
1929 NS_ENSURE_SUCCESS(rv
, rv
);
1931 if (aSelectionNum
== rangeCount
) { // Add successfully created new range
1932 return domSel
->AddRange(range
);
1938 * Adds a selection bounded by the specified offsets.
1940 NS_IMETHODIMP
nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset
, PRInt32 aEndOffset
)
1942 nsCOMPtr
<nsISelection
> domSel
;
1943 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1944 nsnull
, getter_AddRefs(domSel
));
1945 NS_ENSURE_SUCCESS(rv
, rv
);
1948 domSel
->GetRangeCount(&rangeCount
);
1950 return SetSelectionBounds(rangeCount
, aStartOffset
, aEndOffset
);
1954 * Removes the specified selection.
1956 NS_IMETHODIMP
nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum
)
1958 nsCOMPtr
<nsISelection
> domSel
;
1959 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1960 nsnull
, getter_AddRefs(domSel
));
1961 NS_ENSURE_SUCCESS(rv
, rv
);
1964 domSel
->GetRangeCount(&rangeCount
);
1965 if (aSelectionNum
< 0 || aSelectionNum
>= rangeCount
)
1966 return NS_ERROR_INVALID_ARG
;
1968 nsCOMPtr
<nsIDOMRange
> range
;
1969 domSel
->GetRangeAt(aSelectionNum
, getter_AddRefs(range
));
1970 return domSel
->RemoveRange(range
);
1973 // void nsIAccessibleText::
1974 // scrollSubstringTo(in long startIndex, in long endIndex,
1975 // in unsigned long scrollType);
1977 nsHyperTextAccessible::ScrollSubstringTo(PRInt32 aStartIndex
, PRInt32 aEndIndex
,
1978 PRUint32 aScrollType
)
1980 PRInt32 startOffset
, endOffset
;
1981 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
1983 nsresult rv
= HypertextOffsetsToDOMRange(aStartIndex
, aEndIndex
,
1984 getter_AddRefs(startNode
),
1986 getter_AddRefs(endNode
),
1988 NS_ENSURE_SUCCESS(rv
, rv
);
1990 return nsAccUtils::ScrollSubstringTo(GetFrame(), startNode
, startOffset
,
1991 endNode
, endOffset
, aScrollType
);
1994 // void nsIAccessibleText::
1995 // scrollSubstringToPoint(in long startIndex, in long endIndex,
1996 // in unsigned long coordinateType,
1997 // in long x, in long y);
1999 nsHyperTextAccessible::ScrollSubstringToPoint(PRInt32 aStartIndex
,
2001 PRUint32 aCoordinateType
,
2002 PRInt32 aX
, PRInt32 aY
)
2004 nsIFrame
*frame
= GetFrame();
2006 return NS_ERROR_FAILURE
;
2009 nsresult rv
= nsAccUtils::ConvertToScreenCoords(aX
, aY
, aCoordinateType
,
2011 NS_ENSURE_SUCCESS(rv
, rv
);
2013 PRInt32 startOffset
, endOffset
;
2014 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
2016 rv
= HypertextOffsetsToDOMRange(aStartIndex
, aEndIndex
,
2017 getter_AddRefs(startNode
), &startOffset
,
2018 getter_AddRefs(endNode
), &endOffset
);
2019 NS_ENSURE_SUCCESS(rv
, rv
);
2021 nsPresContext
*presContext
= frame
->PresContext();
2023 PRBool initialScrolled
= PR_FALSE
;
2024 nsIFrame
*parentFrame
= frame
;
2025 while ((parentFrame
= parentFrame
->GetParent())) {
2026 nsIScrollableFrame
*scrollableFrame
= nsnull
;
2027 CallQueryInterface(parentFrame
, &scrollableFrame
);
2028 if (scrollableFrame
) {
2029 if (!initialScrolled
) {
2030 // Scroll substring to the given point. Turn the point into percents
2031 // relative scrollable area to use nsAccUtils::ScrollSubstringTo.
2032 nsIntRect frameRect
= parentFrame
->GetScreenRectExternal();
2033 PRInt32 devOffsetX
= coords
.x
- frameRect
.x
;
2034 PRInt32 devOffsetY
= coords
.y
- frameRect
.y
;
2036 nsPoint
offsetPoint(presContext
->DevPixelsToAppUnits(devOffsetX
),
2037 presContext
->DevPixelsToAppUnits(devOffsetY
));
2039 nsSize
size(parentFrame
->GetSize());
2040 PRInt16 hPercent
= offsetPoint
.x
* 100 / size
.width
;
2041 PRInt16 vPercent
= offsetPoint
.y
* 100 / size
.height
;
2043 rv
= nsAccUtils::ScrollSubstringTo(GetFrame(), startNode
, startOffset
,
2045 vPercent
, hPercent
);
2046 NS_ENSURE_SUCCESS(rv
, rv
);
2048 initialScrolled
= PR_TRUE
;
2050 // Substring was scrolled to the given point already inside its closest
2051 // scrollable area. If there are nested scrollable areas then make
2052 // sure we scroll lower areas to the given point inside currently
2053 // traversed scrollable area.
2054 nsAccUtils::ScrollFrameToPoint(parentFrame
, frame
, coords
);
2057 frame
= parentFrame
;
2063 nsresult
nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame
*aFrame
, PRInt32 aContentOffset
,
2064 PRUint32
*aRenderedOffset
)
2067 // Current frame not rendered -- this can happen if text is set on
2068 // something with display: none
2069 *aRenderedOffset
= 0;
2072 NS_ASSERTION(aFrame
->GetType() == nsAccessibilityAtoms::textFrame
,
2073 "Need text frame for offset conversion");
2074 NS_ASSERTION(aFrame
->GetPrevContinuation() == nsnull
,
2075 "Call on primary frame only");
2077 gfxSkipChars skipChars
;
2078 gfxSkipCharsIterator iter
;
2079 // Only get info up to original ofset, we know that will be larger than skipped offset
2080 nsresult rv
= aFrame
->GetRenderedText(nsnull
, &skipChars
, &iter
, 0, aContentOffset
);
2081 NS_ENSURE_SUCCESS(rv
, rv
);
2083 PRUint32 ourRenderedStart
= iter
.GetSkippedOffset();
2084 PRInt32 ourContentStart
= iter
.GetOriginalOffset();
2086 *aRenderedOffset
= iter
.ConvertOriginalToSkipped(aContentOffset
+ ourContentStart
) -
2092 nsresult
nsHyperTextAccessible::RenderedToContentOffset(nsIFrame
*aFrame
, PRUint32 aRenderedOffset
,
2093 PRInt32
*aContentOffset
)
2095 *aContentOffset
= 0;
2096 NS_ENSURE_TRUE(aFrame
, NS_ERROR_FAILURE
);
2098 NS_ASSERTION(aFrame
->GetType() == nsAccessibilityAtoms::textFrame
,
2099 "Need text frame for offset conversion");
2100 NS_ASSERTION(aFrame
->GetPrevContinuation() == nsnull
,
2101 "Call on primary frame only");
2103 gfxSkipChars skipChars
;
2104 gfxSkipCharsIterator iter
;
2105 // We only need info up to skipped offset -- that is what we're converting to original offset
2106 nsresult rv
= aFrame
->GetRenderedText(nsnull
, &skipChars
, &iter
, 0, aRenderedOffset
);
2107 NS_ENSURE_SUCCESS(rv
, rv
);
2109 PRUint32 ourRenderedStart
= iter
.GetSkippedOffset();
2110 PRInt32 ourContentStart
= iter
.GetOriginalOffset();
2112 *aContentOffset
= iter
.ConvertSkippedToOriginal(aRenderedOffset
+ ourRenderedStart
) - ourContentStart
;
2118 nsHyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame
*aFrame
,
2120 nsIAccessible
*aAccessible
,
2122 PRInt32
*aNodeOffset
)
2124 NS_ENSURE_ARG(aAccessible
);
2126 nsCOMPtr
<nsIDOMNode
> node
;
2129 // If the given frame is null then set offset after the DOM node of the
2130 // given accessible.
2131 nsCOMPtr
<nsIAccessNode
> accessNode(do_QueryInterface(aAccessible
));
2133 nsCOMPtr
<nsIDOMNode
> DOMNode
;
2134 accessNode
->GetDOMNode(getter_AddRefs(DOMNode
));
2135 nsCOMPtr
<nsIContent
> content(do_QueryInterface(DOMNode
));
2136 NS_ENSURE_STATE(content
);
2138 nsCOMPtr
<nsIContent
> parent(content
->GetParent());
2139 NS_ENSURE_STATE(parent
);
2141 *aNodeOffset
= parent
->IndexOf(content
) + 1;
2142 node
= do_QueryInterface(parent
);
2144 } else if (aFrame
->GetType() == nsAccessibilityAtoms::textFrame
) {
2145 nsCOMPtr
<nsIContent
> content(aFrame
->GetContent());
2146 NS_ENSURE_STATE(content
);
2148 nsCOMPtr
<nsIPresShell
> shell(GetPresShell());
2149 NS_ENSURE_STATE(shell
);
2151 nsIFrame
*primaryFrame
= shell
->GetPrimaryFrameFor(content
);
2152 nsresult rv
= RenderedToContentOffset(primaryFrame
, aOffset
, aNodeOffset
);
2153 NS_ENSURE_SUCCESS(rv
, rv
);
2155 node
= do_QueryInterface(content
);
2158 nsCOMPtr
<nsIContent
> content(aFrame
->GetContent());
2159 NS_ENSURE_STATE(content
);
2161 nsCOMPtr
<nsIContent
> parent(content
->GetParent());
2162 NS_ENSURE_STATE(parent
);
2164 *aNodeOffset
= parent
->IndexOf(content
);
2165 node
= do_QueryInterface(parent
);
2168 NS_IF_ADDREF(*aNode
= node
);
2172 // nsHyperTextAccessible
2174 nsHyperTextAccessible::DOMRangeBoundToHypertextOffset(nsIDOMRange
*aRange
,
2175 PRBool aIsStartBound
,
2176 PRBool aIsStartHTOffset
,
2179 nsCOMPtr
<nsIDOMNode
> node
;
2180 PRInt32 nodeOffset
= 0;
2183 if (aIsStartBound
) {
2184 rv
= aRange
->GetStartContainer(getter_AddRefs(node
));
2185 NS_ENSURE_SUCCESS(rv
, rv
);
2187 rv
= aRange
->GetStartOffset(&nodeOffset
);
2188 NS_ENSURE_SUCCESS(rv
, rv
);
2190 rv
= aRange
->GetEndContainer(getter_AddRefs(node
));
2191 NS_ENSURE_SUCCESS(rv
, rv
);
2193 rv
= aRange
->GetEndOffset(&nodeOffset
);
2194 NS_ENSURE_SUCCESS(rv
, rv
);
2197 nsCOMPtr
<nsIAccessible
> startAcc
;
2198 rv
= DOMPointToHypertextOffset(node
, nodeOffset
, aHTOffset
,
2199 getter_AddRefs(startAcc
));
2200 NS_ENSURE_SUCCESS(rv
, rv
);
2202 if (aIsStartHTOffset
&& !startAcc
)
2208 // nsHyperTextAccessible
2210 nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode
*aNode
,
2211 PRInt32 aNodeOffset
,
2212 PRInt32
*aHTStartOffset
,
2213 PRInt32
*aHTEndOffset
,
2214 nsIPersistentProperties
*aAttributes
)
2216 nsCOMArray
<nsIDOMRange
> ranges
;
2217 nsresult rv
= GetSelections(nsISelectionController::SELECTION_SPELLCHECK
,
2218 nsnull
, nsnull
, &ranges
);
2219 NS_ENSURE_SUCCESS(rv
, rv
);
2221 PRInt32 rangeCount
= ranges
.Count();
2225 for (PRInt32 index
= 0; index
< rangeCount
; index
++) {
2226 nsCOMPtr
<nsIDOMRange
> range
= ranges
[index
];
2227 nsCOMPtr
<nsIDOMNSRange
> nsrange(do_QueryInterface(range
));
2228 NS_ENSURE_STATE(nsrange
);
2231 rv
= nsrange
->ComparePoint(aNode
, aNodeOffset
, &result
);
2232 NS_ENSURE_SUCCESS(rv
, rv
);
2234 if (result
== 1) { // range is before point
2235 PRInt32 startHTOffset
= 0;
2236 rv
= DOMRangeBoundToHypertextOffset(range
, PR_FALSE
, PR_TRUE
,
2238 NS_ENSURE_SUCCESS(rv
, rv
);
2240 if (startHTOffset
> *aHTStartOffset
)
2241 *aHTStartOffset
= startHTOffset
;
2243 } else if (result
== -1) { // range is after point
2244 PRInt32 endHTOffset
= 0;
2245 rv
= DOMRangeBoundToHypertextOffset(range
, PR_TRUE
, PR_FALSE
,
2247 NS_ENSURE_SUCCESS(rv
, rv
);
2249 if (endHTOffset
< *aHTEndOffset
)
2250 *aHTEndOffset
= endHTOffset
;
2252 } else { // point is in range
2253 PRInt32 startHTOffset
= 0;
2254 rv
= DOMRangeBoundToHypertextOffset(range
, PR_TRUE
, PR_TRUE
,
2256 NS_ENSURE_SUCCESS(rv
, rv
);
2258 PRInt32 endHTOffset
= 0;
2259 rv
= DOMRangeBoundToHypertextOffset(range
, PR_FALSE
, PR_FALSE
,
2261 NS_ENSURE_SUCCESS(rv
, rv
);
2263 if (startHTOffset
> *aHTStartOffset
)
2264 *aHTStartOffset
= startHTOffset
;
2265 if (endHTOffset
< *aHTEndOffset
)
2266 *aHTEndOffset
= endHTOffset
;
2269 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::invalid
,
2270 NS_LITERAL_STRING("spelling"));
2280 // nsHyperTextAccessible
2282 nsHyperTextAccessible::GetLangTextAttributes(PRBool aIncludeDefAttrs
,
2283 nsIDOMNode
*aSourceNode
,
2284 PRInt32
*aStartHTOffset
,
2285 PRInt32
*aEndHTOffset
,
2286 nsIPersistentProperties
*aAttributes
)
2288 nsCOMPtr
<nsIDOMElement
> sourceElm(nsAccUtils::GetDOMElementFor(aSourceNode
));
2290 nsCOMPtr
<nsIContent
> content(do_QueryInterface(sourceElm
));
2291 nsCOMPtr
<nsIContent
> rootContent(do_QueryInterface(mDOMNode
));
2294 nsAccUtils::GetLanguageFor(content
, rootContent
, lang
);
2296 nsAutoString rootLang
;
2297 nsresult rv
= GetLanguage(rootLang
);
2298 NS_ENSURE_SUCCESS(rv
, rv
);
2301 // Expose 'language' text attribute if the DOM 'lang' attribute is
2302 // presented and it's different from the 'lang' attribute on the root
2303 // element or we should include default values of text attribute.
2304 const nsAString
& resultLang
= lang
.IsEmpty() ? rootLang
: lang
;
2305 if (!resultLang
.IsEmpty() && (aIncludeDefAttrs
|| lang
!= rootLang
))
2306 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::language
,
2310 nsLangTextAttr
textAttr(lang
, rootContent
);
2311 return GetRangeForTextAttr(aSourceNode
, &textAttr
,
2312 aStartHTOffset
, aEndHTOffset
);
2315 // nsHyperTextAccessible
2317 nsHyperTextAccessible::GetCSSTextAttributes(PRBool aIncludeDefAttrs
,
2318 nsIDOMNode
*aSourceNode
,
2319 PRInt32
*aStartHTOffset
,
2320 PRInt32
*aEndHTOffset
,
2321 nsIPersistentProperties
*aAttributes
)
2323 nsCOMPtr
<nsIDOMElement
> sourceElm(nsAccUtils::GetDOMElementFor(aSourceNode
));
2324 nsCOMPtr
<nsIDOMElement
> rootElm(nsAccUtils::GetDOMElementFor(mDOMNode
));
2326 nsCSSTextAttr
textAttr(aIncludeDefAttrs
, sourceElm
, rootElm
);
2327 while (textAttr
.Iterate()) {
2329 nsAutoString value
, oldValue
;
2330 if (aAttributes
&& textAttr
.Get(name
, value
))
2331 aAttributes
->SetStringProperty(name
, value
, oldValue
);
2333 nsresult rv
= GetRangeForTextAttr(aSourceNode
, &textAttr
,
2334 aStartHTOffset
, aEndHTOffset
);
2335 NS_ENSURE_SUCCESS(rv
, rv
);
2338 nsIFrame
*sourceFrame
= nsAccUtils::GetFrameFor(sourceElm
);
2340 nsIFrame
*rootFrame
= nsnull
;
2342 if (!aIncludeDefAttrs
)
2343 rootFrame
= nsAccUtils::GetFrameFor(rootElm
);
2345 nsBackgroundTextAttr
backgroundTextAttr(sourceFrame
, rootFrame
);
2347 if (backgroundTextAttr
.Get(value
)) {
2348 nsAccUtils::SetAccAttr(aAttributes
,
2349 nsAccessibilityAtoms::backgroundColor
, value
);
2352 nsresult rv
= GetRangeForTextAttr(aSourceNode
, &backgroundTextAttr
,
2353 aStartHTOffset
, aEndHTOffset
);
2360 // nsHyperTextAccessible
2362 nsHyperTextAccessible::GetRangeForTextAttr(nsIDOMNode
*aNode
,
2363 nsTextAttr
*aComparer
,
2364 PRInt32
*aStartHTOffset
,
2365 PRInt32
*aEndHTOffset
)
2367 nsCOMPtr
<nsIDOMElement
> rootElm(nsAccUtils::GetDOMElementFor(mDOMNode
));
2368 NS_ENSURE_STATE(rootElm
);
2370 nsCOMPtr
<nsIDOMNode
> tmpNode(aNode
);
2371 nsCOMPtr
<nsIDOMNode
> currNode(aNode
);
2373 // Navigate backwards and forwards from current node to the root node to
2374 // calculate range bounds for the text attribute. Navigation sequence is the
2376 // 1. Navigate through the siblings.
2377 // 2. If the traversed sibling has children then navigate from its leaf child
2378 // to it through whole tree of the traversed sibling.
2379 // 3. Get the parent and cycle algorithm until the root node.
2381 // Navigate backwards (find the start offset).
2382 while (currNode
&& currNode
!= rootElm
) {
2383 nsCOMPtr
<nsIDOMElement
> currElm(nsAccUtils::GetDOMElementFor(currNode
));
2384 NS_ENSURE_STATE(currElm
);
2386 if (currNode
!= aNode
&& !aComparer
->Equal(currElm
)) {
2387 PRInt32 startHTOffset
= 0;
2388 nsCOMPtr
<nsIAccessible
> startAcc
;
2389 nsresult rv
= DOMPointToHypertextOffset(tmpNode
, -1, &startHTOffset
,
2390 getter_AddRefs(startAcc
));
2391 NS_ENSURE_SUCCESS(rv
, rv
);
2396 if (startHTOffset
> *aStartHTOffset
)
2397 *aStartHTOffset
= startHTOffset
;
2402 currNode
->GetPreviousSibling(getter_AddRefs(tmpNode
));
2404 // Navigate through the subtree of traversed children to calculate
2405 // left bound of the range.
2406 FindStartOffsetInSubtree(tmpNode
, currNode
, aComparer
, aStartHTOffset
);
2409 currNode
->GetParentNode(getter_AddRefs(tmpNode
));
2410 currNode
.swap(tmpNode
);
2413 // Navigate forwards (find the end offset).
2414 PRBool moveIntoSubtree
= PR_TRUE
;
2416 while (currNode
&& currNode
!= rootElm
) {
2417 nsCOMPtr
<nsIDOMElement
> currElm(nsAccUtils::GetDOMElementFor(currNode
));
2418 NS_ENSURE_STATE(currElm
);
2420 // Stop new end offset searching if the given text attribute changes its
2422 if (!aComparer
->Equal(currElm
)) {
2423 PRInt32 endHTOffset
= 0;
2424 nsresult rv
= DOMPointToHypertextOffset(currNode
, -1, &endHTOffset
);
2425 NS_ENSURE_SUCCESS(rv
, rv
);
2427 if (endHTOffset
< *aEndHTOffset
)
2428 *aEndHTOffset
= endHTOffset
;
2433 if (moveIntoSubtree
) {
2434 // Navigate through subtree of traversed node. We use 'moveIntoSubtree'
2435 // flag to avoid traversing the same subtree twice.
2436 currNode
->GetFirstChild(getter_AddRefs(tmpNode
));
2438 FindEndOffsetInSubtree(tmpNode
, aComparer
, aEndHTOffset
);
2441 currNode
->GetNextSibling(getter_AddRefs(tmpNode
));
2442 moveIntoSubtree
= PR_TRUE
;
2444 currNode
->GetParentNode(getter_AddRefs(tmpNode
));
2445 moveIntoSubtree
= PR_FALSE
;
2448 currNode
.swap(tmpNode
);
2456 nsHyperTextAccessible::FindEndOffsetInSubtree(nsIDOMNode
*aCurrNode
,
2457 nsTextAttr
*aComparer
,
2463 nsCOMPtr
<nsIDOMElement
> currElm(nsAccUtils::GetDOMElementFor(aCurrNode
));
2464 NS_ENSURE_STATE(currElm
);
2466 // If the given text attribute (pointed by nsTextAttr object) changes its
2467 // value on the traversed element then fit the end of range.
2468 if (!aComparer
->Equal(currElm
)) {
2469 PRInt32 endHTOffset
= 0;
2470 nsresult rv
= DOMPointToHypertextOffset(aCurrNode
, -1, &endHTOffset
);
2471 NS_ENSURE_SUCCESS(rv
, rv
);
2473 if (endHTOffset
< *aHTOffset
)
2474 *aHTOffset
= endHTOffset
;
2479 // Deeply traverse into the tree to fit the end of range.
2480 nsCOMPtr
<nsIDOMNode
> nextNode
;
2481 aCurrNode
->GetFirstChild(getter_AddRefs(nextNode
));
2483 PRBool res
= FindEndOffsetInSubtree(nextNode
, aComparer
, aHTOffset
);
2488 aCurrNode
->GetNextSibling(getter_AddRefs(nextNode
));
2490 if (FindEndOffsetInSubtree(nextNode
, aComparer
, aHTOffset
))
2498 nsHyperTextAccessible::FindStartOffsetInSubtree(nsIDOMNode
*aCurrNode
,
2499 nsIDOMNode
*aPrevNode
,
2500 nsTextAttr
*aComparer
,
2506 // Find the closest element back to the traversed element.
2507 nsCOMPtr
<nsIDOMNode
> nextNode
;
2508 aCurrNode
->GetLastChild(getter_AddRefs(nextNode
));
2510 if (FindStartOffsetInSubtree(nextNode
, aPrevNode
, aComparer
, aHTOffset
))
2514 nsCOMPtr
<nsIDOMElement
> currElm(nsAccUtils::GetDOMElementFor(aCurrNode
));
2515 NS_ENSURE_STATE(currElm
);
2517 // If the given text attribute (pointed by nsTextAttr object) changes its
2518 // value on the traversed element then fit the start of range.
2519 if (!aComparer
->Equal(currElm
)) {
2520 PRInt32 startHTOffset
= 0;
2521 nsCOMPtr
<nsIAccessible
> startAcc
;
2522 nsresult rv
= DOMPointToHypertextOffset(aPrevNode
, -1, &startHTOffset
,
2523 getter_AddRefs(startAcc
));
2524 NS_ENSURE_SUCCESS(rv
, rv
);
2529 if (startHTOffset
> *aHTOffset
)
2530 *aHTOffset
= startHTOffset
;
2535 // Moving backwards to find the start of range.
2536 aCurrNode
->GetPreviousSibling(getter_AddRefs(nextNode
));
2538 if (FindStartOffsetInSubtree(nextNode
, aCurrNode
, aComparer
, aHTOffset
))