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 "nsIClipboard.h"
47 #include "nsContentCID.h"
48 #include "nsIDOMAbstractView.h"
49 #include "nsIDOMCharacterData.h"
50 #include "nsIDOMDocument.h"
51 #include "nsPIDOMWindow.h"
52 #include "nsIDOMDocumentView.h"
53 #include "nsIDOMRange.h"
54 #include "nsIDOMNSRange.h"
55 #include "nsIDOMWindowInternal.h"
56 #include "nsIDOMXULDocument.h"
57 #include "nsIEditingSession.h"
58 #include "nsIEditor.h"
59 #include "nsIFontMetrics.h"
61 #include "nsFrameSelection.h"
62 #include "nsILineIterator.h"
63 #include "nsIInterfaceRequestorUtils.h"
64 #include "nsIPlaintextEditor.h"
65 #include "nsIScrollableFrame.h"
66 #include "nsISelection2.h"
67 #include "nsISelectionPrivate.h"
68 #include "nsIServiceManager.h"
69 #include "nsTextFragment.h"
70 #include "gfxSkipChars.h"
72 static NS_DEFINE_IID(kRangeCID
, NS_RANGE_CID
);
75 // nsHyperTextAccessible
78 NS_IMPL_ADDREF_INHERITED(nsHyperTextAccessible
, nsAccessibleWrap
)
79 NS_IMPL_RELEASE_INHERITED(nsHyperTextAccessible
, nsAccessibleWrap
)
81 nsresult
nsHyperTextAccessible::QueryInterface(REFNSIID aIID
, void** aInstancePtr
)
83 *aInstancePtr
= nsnull
;
85 nsCOMPtr
<nsIDOMXULDocument
> xulDoc(do_QueryInterface(mDOMNode
));
86 if (mDOMNode
&& !xulDoc
) {
87 // We need XUL doc check for now because for now nsDocAccessible must
88 // inherit from nsHyperTextAccessible in order for HTML document accessibles
89 // to get support for these interfaces.
90 // However at some point we may push <body> to implement the interfaces and
91 // return nsDocAccessible to inherit from nsAccessibleWrap.
93 if (aIID
.Equals(NS_GET_IID(nsHyperTextAccessible
))) {
94 *aInstancePtr
= static_cast<nsHyperTextAccessible
*>(this);
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);
115 if (aIID
.Equals(NS_GET_IID(nsIAccessibleHyperText
))) {
116 *aInstancePtr
= static_cast<nsIAccessibleHyperText
*>(this);
121 if (aIID
.Equals(NS_GET_IID(nsIAccessibleEditableText
))) {
122 *aInstancePtr
= static_cast<nsIAccessibleEditableText
*>(this);
128 return nsAccessible::QueryInterface(aIID
, aInstancePtr
);
131 nsHyperTextAccessible::nsHyperTextAccessible(nsIDOMNode
* aNode
, nsIWeakReference
* aShell
):
132 nsAccessibleWrap(aNode
, aShell
)
136 NS_IMETHODIMP
nsHyperTextAccessible::GetRole(PRUint32
*aRole
)
138 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mDOMNode
);
140 return NS_ERROR_FAILURE
;
143 nsIAtom
*tag
= content
->Tag();
145 if (tag
== nsAccessibilityAtoms::form
) {
146 *aRole
= nsIAccessibleRole::ROLE_FORM
;
148 else if (tag
== nsAccessibilityAtoms::div
||
149 tag
== nsAccessibilityAtoms::blockquote
) {
150 *aRole
= nsIAccessibleRole::ROLE_SECTION
;
152 else if (tag
== nsAccessibilityAtoms::h1
||
153 tag
== nsAccessibilityAtoms::h2
||
154 tag
== nsAccessibilityAtoms::h3
||
155 tag
== nsAccessibilityAtoms::h4
||
156 tag
== nsAccessibilityAtoms::h5
||
157 tag
== nsAccessibilityAtoms::h6
) {
158 *aRole
= nsIAccessibleRole::ROLE_HEADING
;
161 nsIFrame
*frame
= GetFrame();
162 if (frame
&& frame
->GetType() == nsAccessibilityAtoms::blockFrame
) {
163 *aRole
= nsIAccessibleRole::ROLE_PARAGRAPH
;
166 *aRole
= nsIAccessibleRole::ROLE_TEXT_CONTAINER
; // In ATK this works
173 nsHyperTextAccessible::GetStateInternal(PRUint32
*aState
, PRUint32
*aExtraState
)
175 nsresult rv
= nsAccessibleWrap::GetStateInternal(aState
, aExtraState
);
176 NS_ENSURE_A11Y_SUCCESS(rv
, rv
);
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 nsPresContext
*context
= shell
->GetPresContext();
277 while (frame
&& startContentOffset
< endContentOffset
) {
278 // Start with this frame's screen rect, which we will
279 // shrink based on the substring we care about within it.
280 // We will then add that frame to the total screenRect we
282 nsIntRect frameScreenRect
= frame
->GetScreenRectExternal();
284 // Get the length of the substring in this frame that we want the bounds for
285 PRInt32 startFrameTextOffset
, endFrameTextOffset
;
286 frame
->GetOffsets(startFrameTextOffset
, endFrameTextOffset
);
287 PRInt32 frameTotalTextLength
= endFrameTextOffset
- startFrameTextOffset
;
288 PRInt32 seekLength
= endContentOffset
- startContentOffset
;
289 PRInt32 frameSubStringLength
= PR_MIN(frameTotalTextLength
- startContentOffsetInFrame
, seekLength
);
291 // Add the point where the string starts to the frameScreenRect
292 nsPoint frameTextStartPoint
;
293 rv
= frame
->GetPointFromOffset(startContentOffset
, &frameTextStartPoint
);
294 NS_ENSURE_SUCCESS(rv
, nsRect());
295 frameScreenRect
.x
+= context
->AppUnitsToDevPixels(frameTextStartPoint
.x
);
297 // Use the point for the end offset to calculate the width
298 nsPoint frameTextEndPoint
;
299 rv
= frame
->GetPointFromOffset(startContentOffset
+ frameSubStringLength
, &frameTextEndPoint
);
300 NS_ENSURE_SUCCESS(rv
, nsRect());
301 frameScreenRect
.width
= context
->AppUnitsToDevPixels(frameTextEndPoint
.x
- frameTextStartPoint
.x
);
303 screenRect
.UnionRect(frameScreenRect
, screenRect
);
305 // Get ready to loop back for next frame continuation
306 startContentOffset
+= frameSubStringLength
;
307 startContentOffsetInFrame
= 0;
308 frame
= frame
->GetNextContinuation();
315 * Gets the specified text.
318 nsHyperTextAccessible::GetPosAndText(PRInt32
& aStartOffset
, PRInt32
& aEndOffset
,
319 nsAString
*aText
, nsIFrame
**aEndFrame
,
320 nsIntRect
*aBoundsRect
,
321 nsIAccessible
**aStartAcc
,
322 nsIAccessible
**aEndAcc
)
324 if (aStartOffset
== nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT
) {
325 GetCharacterCount(&aStartOffset
);
327 if (aStartOffset
== nsIAccessibleText::TEXT_OFFSET_CARET
) {
328 GetCaretOffset(&aStartOffset
);
330 if (aEndOffset
== nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT
) {
331 GetCharacterCount(&aEndOffset
);
333 if (aEndOffset
== nsIAccessibleText::TEXT_OFFSET_CARET
) {
334 GetCaretOffset(&aEndOffset
);
337 PRInt32 startOffset
= aStartOffset
;
338 PRInt32 endOffset
= aEndOffset
;
339 // XXX this prevents text interface usage on <input type="password">
341 (nsAccUtils::Role(this) == nsIAccessibleRole::ROLE_PASSWORD_TEXT
);
343 // Clear out parameters and set up loop
348 const PRInt32 kMaxTextLength
= 32767;
349 endOffset
= kMaxTextLength
; // Max end offset
351 else if (startOffset
> endOffset
) {
355 nsIFrame
*startFrame
= nsnull
;
360 aBoundsRect
->Empty();
368 nsCOMPtr
<nsIAccessible
> accessible
, lastAccessible
;
370 gfxSkipChars skipChars
;
371 gfxSkipCharsIterator iter
;
373 // Loop through children and collect valid offsets, text and bounds
374 // depending on what we need for out parameters
375 while (NextChild(accessible
)) {
376 lastAccessible
= accessible
;
377 nsRefPtr
<nsAccessNode
> accessNode
= nsAccUtils::QueryAccessNode(accessible
);
379 nsIFrame
*frame
= accessNode
->GetFrame();
383 nsIFrame
*primaryFrame
= frame
;
384 if (nsAccUtils::IsText(accessible
)) {
385 // We only need info up to rendered offset -- that is what we're
386 // converting to content offset
387 PRInt32 substringEndOffset
= -1;
388 PRUint32 ourRenderedStart
= 0;
389 PRInt32 ourContentStart
= 0;
390 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
391 nsresult rv
= frame
->GetRenderedText(nsnull
, &skipChars
, &iter
);
392 if (NS_SUCCEEDED(rv
)) {
393 ourRenderedStart
= iter
.GetSkippedOffset();
394 ourContentStart
= iter
.GetOriginalOffset();
396 iter
.ConvertOriginalToSkipped(skipChars
.GetOriginalCharCount() +
397 ourContentStart
) - ourRenderedStart
;
400 if (substringEndOffset
< 0) {
401 // XXX for non-textframe text like list bullets,
402 // should go away after list bullet rewrite
403 substringEndOffset
= nsAccUtils::TextLength(accessible
);
405 if (startOffset
< substringEndOffset
) {
406 // Our start is within this substring
407 if (startOffset
> 0 || endOffset
< substringEndOffset
) {
408 // We don't want the whole string for this accessible
409 // Get out the continuing text frame with this offset
410 PRInt32 outStartLineUnused
;
411 PRInt32 contentOffset
;
412 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
413 contentOffset
= iter
.ConvertSkippedToOriginal(startOffset
) +
414 ourRenderedStart
- ourContentStart
;
417 contentOffset
= startOffset
;
419 frame
->GetChildFrameContainingOffset(contentOffset
, PR_TRUE
,
420 &outStartLineUnused
, &frame
);
422 *aEndFrame
= frame
; // We ended in the current frame
424 NS_ADDREF(*aEndAcc
= accessible
);
426 if (substringEndOffset
> endOffset
) {
427 // Need to stop before the end of the available text
428 substringEndOffset
= endOffset
;
430 aEndOffset
= endOffset
;
434 for (PRInt32 count
= startOffset
; count
< substringEndOffset
; count
++)
435 *aText
+= '*'; // Show *'s only for password text
438 nsCOMPtr
<nsPIAccessible
> pAcc(do_QueryInterface(accessible
));
439 pAcc
->AppendTextTo(*aText
, startOffset
,
440 substringEndOffset
- startOffset
);
443 if (aBoundsRect
) { // Caller wants the bounds of the text
444 aBoundsRect
->UnionRect(*aBoundsRect
,
445 GetBoundsForString(primaryFrame
, startOffset
,
446 substringEndOffset
));
450 aStartOffset
= startOffset
;
452 NS_ADDREF(*aStartAcc
= accessible
);
454 // We already started copying in this accessible's string,
455 // for the next accessible we'll start at offset 0
459 // We have not found the start position yet, get the new startOffset
460 // that is relative to next accessible
461 startOffset
-= substringEndOffset
;
463 // The endOffset needs to be relative to the new startOffset
464 endOffset
-= substringEndOffset
;
467 // Embedded object, append marker
468 // XXX Append \n for <br>'s
469 if (startOffset
>= 1) {
475 if (frame
->GetType() == nsAccessibilityAtoms::brFrame
) {
476 *aText
+= kForcedNewLineChar
;
477 } else if (nsAccUtils::MustPrune(this)) {
478 *aText
+= kImaginaryEmbeddedObjectChar
;
479 // Expose imaginary embedded object character if the accessible
482 *aText
+= kEmbeddedObjectChar
;
486 aBoundsRect
->UnionRect(*aBoundsRect
,
487 frame
->GetScreenRectExternal());
494 NS_ADDREF(*aStartAcc
= accessible
);
499 if (endOffset
<= 0 && startFrame
) {
500 break; // If we don't have startFrame yet, get that in next loop iteration
504 if (aStartAcc
&& !*aStartAcc
) {
505 NS_IF_ADDREF(*aStartAcc
= lastAccessible
);
507 if (aEndFrame
&& !*aEndFrame
) {
508 *aEndFrame
= startFrame
;
509 if (aStartAcc
&& aEndAcc
)
510 NS_IF_ADDREF(*aEndAcc
= *aStartAcc
);
516 NS_IMETHODIMP
nsHyperTextAccessible::GetText(PRInt32 aStartOffset
, PRInt32 aEndOffset
, nsAString
&aText
)
519 return NS_ERROR_FAILURE
;
521 return GetPosAndText(aStartOffset
, aEndOffset
, &aText
) ? NS_OK
: NS_ERROR_FAILURE
;
525 * Gets the character count.
527 NS_IMETHODIMP
nsHyperTextAccessible::GetCharacterCount(PRInt32
*aCharacterCount
)
529 *aCharacterCount
= 0;
531 return NS_ERROR_FAILURE
;
534 nsCOMPtr
<nsIAccessible
> accessible
;
536 while (NextChild(accessible
)) {
537 PRInt32 textLength
= nsAccUtils::TextLength(accessible
);
538 NS_ENSURE_TRUE(textLength
>= 0, nsnull
);
539 *aCharacterCount
+= textLength
;
545 * Gets the specified character.
547 NS_IMETHODIMP
nsHyperTextAccessible::GetCharacterAtOffset(PRInt32 aOffset
, PRUnichar
*aCharacter
)
550 return NS_ERROR_FAILURE
;
553 nsresult rv
= GetText(aOffset
, aOffset
+ 1, text
);
558 if (text
.IsEmpty()) {
559 return NS_ERROR_FAILURE
;
561 *aCharacter
= text
.First();
565 nsresult
nsHyperTextAccessible::DOMPointToHypertextOffset(nsIDOMNode
* aNode
, PRInt32 aNodeOffset
,
566 PRInt32
* aHyperTextOffset
,
567 nsIAccessible
**aFinalAccessible
,
570 // Turn a DOM Node and offset into an offset into this hypertext.
571 // On failure, return null. On success, return the DOM node which contains the offset.
572 NS_ENSURE_ARG_POINTER(aHyperTextOffset
);
573 *aHyperTextOffset
= 0;
576 return NS_ERROR_FAILURE
;
578 if (aFinalAccessible
) {
579 *aFinalAccessible
= nsnull
;
582 PRUint32 addTextOffset
= 0;
583 nsCOMPtr
<nsIDOMNode
> findNode
;
585 unsigned short nodeType
;
586 aNode
->GetNodeType(&nodeType
);
587 if (aNodeOffset
== -1) {
590 else if (nodeType
== nsIDOMNode::TEXT_NODE
) {
591 // For text nodes, aNodeOffset comes in as a character offset
592 // Text offset will be added at the end, if we find the offset in this hypertext
593 // We want the "skipped" offset into the text (rendered text without the extra whitespace)
594 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
595 NS_ASSERTION(content
, "No nsIContent for dom node");
596 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
597 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
598 nsIFrame
*frame
= presShell
->GetPrimaryFrameFor(content
);
599 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
600 nsresult rv
= ContentToRenderedOffset(frame
, aNodeOffset
, &addTextOffset
);
601 NS_ENSURE_SUCCESS(rv
, rv
);
602 // Get the child node and
606 // For non-text nodes, aNodeOffset comes in as a child node index
607 nsCOMPtr
<nsIContent
> parentContent(do_QueryInterface(aNode
));
608 // Should not happen, but better to protect against crash if doc node is somehow passed in
609 NS_ENSURE_TRUE(parentContent
, NS_ERROR_FAILURE
);
610 // findNode could be null if aNodeOffset == # of child nodes, which means one of two things:
611 // 1) we're at the end of the children, keep findNode = null, so that we get the last possible offset
612 // 2) there are no children and the passed-in node is mDOMNode, which means we're an aempty nsIAccessibleText
613 // 3) there are no children, and the passed-in node is not mDOMNode -- use parentContent for the node to find
615 findNode
= do_QueryInterface(parentContent
->GetChildAt(aNodeOffset
));
616 if (!findNode
&& !aNodeOffset
) {
617 if (SameCOMIdentity(parentContent
, mDOMNode
)) {
618 // There are no children, which means this is an empty nsIAccessibleText, in which
619 // case we can only be at hypertext offset 0
620 *aHyperTextOffset
= 0;
623 findNode
= do_QueryInterface(parentContent
); // Case #2: there are no children
627 // Get accessible for this findNode, or if that node isn't accessible, use the
628 // accessible for the next DOM node which has one (based on forward depth first search)
629 nsCOMPtr
<nsIAccessible
> descendantAccessible
;
631 nsCOMPtr
<nsIContent
> findContent
= do_QueryInterface(findNode
);
632 if (findContent
->IsNodeOfType(nsINode::eHTML
) &&
633 findContent
->NodeInfo()->Equals(nsAccessibilityAtoms::br
)) {
634 nsIContent
*parent
= findContent
->GetParent();
636 parent
->IsRootOfNativeAnonymousSubtree() &&
637 parent
->GetChildCount() == 1) {
638 // This <br> is the only node in a text control, therefore it is the hacky
639 // "bogus node" used when there is no text in a control
640 *aHyperTextOffset
= 0;
644 descendantAccessible
= GetFirstAvailableAccessible(findNode
);
646 // From the descendant, go up and get the immediate child of this hypertext
647 nsCOMPtr
<nsIAccessible
> childAccessible
;
648 while (descendantAccessible
) {
649 nsCOMPtr
<nsIAccessible
> parentAccessible
;
650 descendantAccessible
->GetParent(getter_AddRefs(parentAccessible
));
651 if (this == parentAccessible
) {
652 childAccessible
= descendantAccessible
;
655 // This offset no longer applies because the passed-in text object is not a child
656 // of the hypertext. This happens when there are nested hypertexts, e.g.
657 // <div>abc<h1>def</h1>ghi</div>
658 // If the passed-in DOM point was not on a direct child of the hypertext, we will
659 // return the offset for that entire hypertext
661 // Not inclusive, the indicated char comes at index before this offset
662 // If the end offset is after the first character of the passed in object, use 1 for
663 // addTextOffset, to put us after the embedded object char. We'll only treat the offset as
664 // before the embedded object char if we end at the very beginning of the child.
665 addTextOffset
= addTextOffset
> 0;
668 // Start offset, inclusive
669 // Make sure the offset lands on the embedded object character in order to indicate
670 // the true inner offset is inside the subtree for that link
672 (nsAccUtils::TextLength(descendantAccessible
) == static_cast<PRInt32
>(addTextOffset
)) ? 1 : 0;
674 descendantAccessible
= parentAccessible
;
677 // Loop through, adding offsets until we reach childAccessible
678 // If childAccessible is null we will end up adding up the entire length of
679 // the hypertext, which is good -- it just means our offset node
680 // came after the last accessible child's node
681 nsCOMPtr
<nsIAccessible
> accessible
;
682 while (NextChild(accessible
) && accessible
!= childAccessible
) {
683 PRInt32 textLength
= nsAccUtils::TextLength(accessible
);
684 NS_ENSURE_TRUE(textLength
>= 0, nsnull
);
685 *aHyperTextOffset
+= textLength
;
688 *aHyperTextOffset
+= addTextOffset
;
689 NS_ASSERTION(accessible
== childAccessible
, "These should be equal whenever we exit loop and accessible != nsnull");
690 if (aFinalAccessible
&&
691 (NextChild(accessible
) ||
692 static_cast<PRInt32
>(addTextOffset
) < nsAccUtils::TextLength(childAccessible
))) {
693 // If not at end of last text node, we will return the accessible we were in
694 NS_ADDREF(*aFinalAccessible
= childAccessible
);
702 nsHyperTextAccessible::HypertextOffsetToDOMPoint(PRInt32 aHTOffset
,
706 nsCOMPtr
<nsIDOMNode
> endNode
;
709 return HypertextOffsetsToDOMRange(aHTOffset
, aHTOffset
, aNode
, aOffset
,
710 getter_AddRefs(endNode
), &endOffset
);
714 nsHyperTextAccessible::HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset
,
715 PRInt32 aEndHTOffset
,
716 nsIDOMNode
**aStartNode
,
717 PRInt32
*aStartOffset
,
718 nsIDOMNode
**aEndNode
,
721 NS_ENSURE_ARG_POINTER(aStartNode
);
722 *aStartNode
= nsnull
;
724 NS_ENSURE_ARG_POINTER(aStartOffset
);
727 NS_ENSURE_ARG_POINTER(aEndNode
);
730 NS_ENSURE_ARG_POINTER(aEndOffset
);
733 // If the given offsets are 0 and associated editor is empty then return
734 // collapsed range with editor root element as range container.
735 if (aStartHTOffset
== 0 && aEndHTOffset
== 0) {
736 nsCOMPtr
<nsIEditor
> editor
;
737 GetAssociatedEditor(getter_AddRefs(editor
));
739 PRBool isEmpty
= PR_FALSE
;
740 editor
->GetDocumentIsEmpty(&isEmpty
);
742 nsCOMPtr
<nsIDOMElement
> editorRootElm
;
743 editor
->GetRootElement(getter_AddRefs(editorRootElm
));
745 nsCOMPtr
<nsIDOMNode
> editorRoot(do_QueryInterface(editorRootElm
));
747 *aStartOffset
= *aEndOffset
= 0;
748 NS_ADDREF(*aStartNode
= editorRoot
);
749 NS_ADDREF(*aEndNode
= editorRoot
);
757 nsCOMPtr
<nsIAccessible
> startAcc
, endAcc
;
758 PRInt32 startOffset
= aStartHTOffset
, endOffset
= aEndHTOffset
;
759 nsIFrame
*startFrame
= nsnull
, *endFrame
= nsnull
;
761 startFrame
= GetPosAndText(startOffset
, endOffset
, nsnull
, &endFrame
, nsnull
,
762 getter_AddRefs(startAcc
), getter_AddRefs(endAcc
));
763 if (!startAcc
|| !endAcc
)
764 return NS_ERROR_FAILURE
;
766 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
767 nsresult rv
= GetDOMPointByFrameOffset(startFrame
, startOffset
, startAcc
,
768 getter_AddRefs(startNode
),
770 NS_ENSURE_SUCCESS(rv
, rv
);
772 if (aStartHTOffset
!= aEndHTOffset
) {
773 rv
= GetDOMPointByFrameOffset(endFrame
, endOffset
, endAcc
,
774 getter_AddRefs(endNode
), &endOffset
);
775 NS_ENSURE_SUCCESS(rv
, rv
);
778 endOffset
= startOffset
;
781 NS_ADDREF(*aStartNode
= startNode
);
782 *aStartOffset
= startOffset
;
784 NS_ADDREF(*aEndNode
= endNode
);
785 *aEndOffset
= endOffset
;
791 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell
*aPresShell
,
792 nsIFrame
*aFromFrame
,
794 nsIAccessible
*aFromAccessible
,
795 nsSelectionAmount aAmount
,
796 nsDirection aDirection
,
799 const PRBool kIsJumpLinesOk
= PR_TRUE
; // okay to jump lines
800 const PRBool kIsScrollViewAStop
= PR_FALSE
; // do not stop at scroll views
801 const PRBool kIsKeyboardSelect
= PR_TRUE
; // is keyboard selection
802 const PRBool kIsVisualBidi
= PR_FALSE
; // use visual order for bidi text
804 EWordMovementType wordMovementType
= aNeedsStart
? eStartWord
: eEndWord
;
805 if (aAmount
== eSelectLine
) {
806 aAmount
= (aDirection
== eDirNext
) ? eSelectEndLine
: eSelectBeginLine
;
809 // Ask layout for the new node and offset, after moving the appropriate amount
810 nsPeekOffsetStruct pos
;
813 PRInt32 contentOffset
= aFromOffset
;
814 if (nsAccUtils::IsText(aFromAccessible
)) {
815 nsRefPtr
<nsAccessNode
> accessNode
=
816 nsAccUtils::QueryAccessNode(aFromAccessible
);
818 nsIFrame
*frame
= accessNode
->GetFrame();
819 NS_ENSURE_TRUE(frame
, -1);
821 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
822 rv
= RenderedToContentOffset(frame
, aFromOffset
, &contentOffset
);
823 NS_ENSURE_SUCCESS(rv
, -1);
827 pos
.SetData(aAmount
, aDirection
, contentOffset
,
828 0, kIsJumpLinesOk
, kIsScrollViewAStop
, kIsKeyboardSelect
, kIsVisualBidi
,
830 rv
= aFromFrame
->PeekOffset(&pos
);
832 if (aDirection
== eDirPrevious
) {
833 // Use passed-in frame as starting point in failure case for now,
834 // this is a hack to deal with starting on a list bullet frame,
835 // which fails in PeekOffset() because the line iterator doesn't see it.
836 // XXX Need to look at our overall handling of list bullets, which are an odd case
837 pos
.mResultContent
= aFromFrame
->GetContent();
838 PRInt32 endOffsetUnused
;
839 aFromFrame
->GetOffsets(pos
.mContentOffset
, endOffsetUnused
);
846 // Turn the resulting node and offset into a hyperTextOffset
847 PRInt32 hyperTextOffset
;
848 nsCOMPtr
<nsIDOMNode
> resultNode
= do_QueryInterface(pos
.mResultContent
);
849 NS_ENSURE_TRUE(resultNode
, -1);
851 nsCOMPtr
<nsIAccessible
> finalAccessible
;
852 rv
= DOMPointToHypertextOffset(resultNode
, pos
.mContentOffset
, &hyperTextOffset
,
853 getter_AddRefs(finalAccessible
),
854 aDirection
== eDirNext
);
855 // If finalAccessible == nsnull, then DOMPointToHypertextOffset() searched through the hypertext
856 // children without finding the node/offset position
857 NS_ENSURE_SUCCESS(rv
, -1);
859 if (!finalAccessible
&& aDirection
== eDirPrevious
) {
860 // If we reached the end during search, this means we didn't find the DOM point
861 // and we're actually at the start of the paragraph
864 else if (aAmount
== eSelectBeginLine
) {
865 // For line selection with needsStart, set start of line exactly to line break
866 if (pos
.mContentOffset
== 0 && mFirstChild
&&
867 nsAccUtils::Role(mFirstChild
) == nsIAccessibleRole::ROLE_STATICTEXT
&&
868 nsAccUtils::TextLength(mFirstChild
) == hyperTextOffset
) {
869 // XXX Bullet hack -- we should remove this once list bullets use anonymous content
872 if (!aNeedsStart
&& hyperTextOffset
> 0) {
876 else if (aAmount
== eSelectEndLine
&& finalAccessible
) {
877 // If not at very end of hypertext, we may need change the end of line offset by 1,
878 // to make sure we are in the right place relative to the line ending
879 if (nsAccUtils::Role(finalAccessible
) == nsIAccessibleRole::ROLE_WHITESPACE
) { // Landed on <br> hard line break
880 // if aNeedsStart, set end of line exactly 1 character past line break
881 // XXX It would be cleaner if we did not have to have the hard line break check,
882 // and just got the correct results from PeekOffset() for the <br> case -- the returned offset should
883 // come after the new line, as it does in other cases.
884 ++ hyperTextOffset
; // Get past hard line break
886 // We are now 1 character past the line break
892 return hyperTextOffset
;
896 Gets the specified text relative to aBoundaryType, which means:
897 BOUNDARY_CHAR The character before/at/after the offset is returned.
898 BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start.
899 BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end.
900 BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start.
901 BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start.
904 nsresult
nsHyperTextAccessible::GetTextHelper(EGetTextType aType
, nsAccessibleTextBoundary aBoundaryType
,
905 PRInt32 aOffset
, PRInt32
*aStartOffset
, PRInt32
*aEndOffset
,
910 NS_ENSURE_ARG_POINTER(aStartOffset
);
911 NS_ENSURE_ARG_POINTER(aEndOffset
);
912 *aStartOffset
= *aEndOffset
= 0;
914 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
916 return NS_ERROR_FAILURE
;
919 if (aOffset
== nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT
) {
920 GetCharacterCount(&aOffset
);
922 if (aOffset
== nsIAccessibleText::TEXT_OFFSET_CARET
) {
923 GetCaretOffset(&aOffset
);
924 if (aOffset
> 0 && (aBoundaryType
== BOUNDARY_LINE_START
||
925 aBoundaryType
== BOUNDARY_LINE_END
)) {
926 // It is the same character offset when the caret is visually at the very end of a line
927 // or the start of a new line. Getting text at the line should provide the line with the visual caret,
928 // otherwise screen readers will announce the wrong line as the user presses up or down arrow and land
929 // at the end of a line.
930 nsCOMPtr
<nsISelection
> domSel
;
931 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
932 nsnull
, getter_AddRefs(domSel
));
933 NS_ENSURE_SUCCESS(rv
, rv
);
935 nsCOMPtr
<nsISelectionPrivate
> privateSelection(do_QueryInterface(domSel
));
936 nsCOMPtr
<nsFrameSelection
> frameSelection
;
937 rv
= privateSelection
->GetFrameSelection(getter_AddRefs(frameSelection
));
938 NS_ENSURE_SUCCESS(rv
, rv
);
940 if (frameSelection
->GetHint() == nsFrameSelection::HINTLEFT
) {
941 -- aOffset
; // We are at the start of a line
945 else if (aOffset
< 0) {
946 return NS_ERROR_FAILURE
;
949 nsSelectionAmount amount
;
950 PRBool needsStart
= PR_FALSE
;
951 switch (aBoundaryType
) {
953 amount
= eSelectCharacter
;
955 aType
= eGetAfter
; // Avoid returning 2 characters
958 case BOUNDARY_WORD_START
:
959 needsStart
= PR_TRUE
;
960 amount
= eSelectWord
;
963 case BOUNDARY_WORD_END
:
964 amount
= eSelectWord
;
967 case BOUNDARY_LINE_START
:
968 // Newlines are considered at the end of a line. Since getting
969 // the BOUNDARY_LINE_START gets the text from the line-start to the next
970 // line-start, the newline is included at the end of the string.
971 needsStart
= PR_TRUE
;
972 amount
= eSelectLine
;
975 case BOUNDARY_LINE_END
:
976 // Newlines are considered at the end of a line. Since getting
977 // the BOUNDARY_END_START gets the text from the line-end to the next
978 //line-end, the newline is included at the beginning of the string.
979 amount
= eSelectLine
;
982 case BOUNDARY_ATTRIBUTE_RANGE
:
984 nsresult rv
= GetTextAttributes(PR_FALSE
, aOffset
,
985 aStartOffset
, aEndOffset
, nsnull
);
986 NS_ENSURE_SUCCESS(rv
, rv
);
988 return GetText(*aStartOffset
, *aEndOffset
, aText
);
991 default: // Note, sentence support is deprecated and falls through to here
992 return NS_ERROR_INVALID_ARG
;
995 PRInt32 startOffset
= aOffset
+ (aBoundaryType
== BOUNDARY_LINE_END
); // Avoid getting the previous line
996 PRInt32 endOffset
= startOffset
;
998 // Convert offsets to frame-relative
999 nsCOMPtr
<nsIAccessible
> startAcc
;
1000 nsIFrame
*startFrame
= GetPosAndText(startOffset
, endOffset
, nsnull
, nsnull
,
1001 nsnull
, getter_AddRefs(startAcc
));
1005 GetCharacterCount(&textLength
);
1006 if (aBoundaryType
== BOUNDARY_LINE_START
&& aOffset
> 0 && aOffset
== textLength
) {
1007 // Asking for start of line, while on last character
1009 nsRefPtr
<nsAccessNode
> startAccessNode
=
1010 nsAccUtils::QueryAccessNode(startAcc
);
1011 startFrame
= startAccessNode
->GetFrame();
1015 return aOffset
> textLength
? NS_ERROR_FAILURE
: NS_OK
;
1018 // We're on the last continuation since we're on the last character
1019 startFrame
= startFrame
->GetLastContinuation();
1023 PRInt32 finalStartOffset
, finalEndOffset
;
1025 // If aType == eGetAt we'll change both the start and end offset from
1026 // the original offset
1027 if (aType
== eGetAfter
) {
1028 finalStartOffset
= aOffset
;
1031 finalStartOffset
= GetRelativeOffset(presShell
, startFrame
, startOffset
,
1032 startAcc
, amount
, eDirPrevious
,
1034 NS_ENSURE_TRUE(finalStartOffset
>= 0, NS_ERROR_FAILURE
);
1037 if (aType
== eGetBefore
) {
1038 endOffset
= aOffset
;
1041 // Start moving forward from the start so that we don't get
1042 // 2 words/lines if the offset occured on whitespace boundary
1043 // Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed
1044 // For BOUNDARY_LINE_END, make sure we start of this line
1045 startOffset
= endOffset
= finalStartOffset
+ (aBoundaryType
== BOUNDARY_LINE_END
);
1046 nsCOMPtr
<nsIAccessible
> endAcc
;
1047 nsIFrame
*endFrame
= GetPosAndText(startOffset
, endOffset
, nsnull
, nsnull
,
1048 nsnull
, getter_AddRefs(endAcc
));
1049 if (nsAccUtils::Role(endAcc
) == nsIAccessibleRole::ROLE_STATICTEXT
) {
1050 // Static text like list bullets will ruin our forward calculation,
1051 // since the caret cannot be in the static text. Start just after the static text.
1052 startOffset
= endOffset
= finalStartOffset
+
1053 (aBoundaryType
== BOUNDARY_LINE_END
) +
1054 nsAccUtils::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
= nsCoreUtils::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
= nsCoreUtils::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
= nsCoreUtils::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 nsRefPtr
<nsAccessNode
> accessNode
= nsAccUtils::QueryAccessNode(accessible
);
1367 nsIFrame
*primaryFrame
= accessNode
->GetFrame();
1368 NS_ENSURE_TRUE(primaryFrame
, NS_ERROR_FAILURE
);
1370 nsIFrame
*frame
= primaryFrame
;
1372 nsIContent
*content
= frame
->GetContent();
1373 NS_ENSURE_TRUE(content
, NS_ERROR_FAILURE
);
1374 nsPoint pointInFrame
= pointInHyperText
- frame
->GetOffsetToExternal(hyperFrame
);
1375 nsSize frameSize
= frame
->GetSize();
1376 if (pointInFrame
.x
< frameSize
.width
&& pointInFrame
.y
< frameSize
.height
) {
1378 if (frame
->GetType() == nsAccessibilityAtoms::textFrame
) {
1379 nsIFrame::ContentOffsets contentOffsets
= frame
->GetContentOffsetsFromPointExternal(pointInFrame
, PR_TRUE
);
1380 if (contentOffsets
.IsNull() || contentOffsets
.content
!= content
) {
1381 return NS_OK
; // Not found, will return -1
1383 PRUint32 addToOffset
;
1384 nsresult rv
= ContentToRenderedOffset(primaryFrame
,
1385 contentOffsets
.offset
,
1387 NS_ENSURE_SUCCESS(rv
, rv
);
1388 offset
+= addToOffset
;
1393 frame
= frame
->GetNextContinuation();
1395 PRInt32 textLength
= nsAccUtils::TextLength(accessible
);
1396 NS_ENSURE_TRUE(textLength
>= 0, NS_ERROR_FAILURE
);
1397 offset
+= textLength
;
1400 return NS_OK
; // Not found, will return -1
1403 // ------- nsIAccessibleHyperText ---------------
1405 nsHyperTextAccessible::GetLinkCount(PRInt32
*aLinkCount
)
1407 NS_ENSURE_ARG_POINTER(aLinkCount
);
1410 return NS_ERROR_FAILURE
;
1413 nsCOMPtr
<nsIAccessible
> accessible
;
1415 while (NextChild(accessible
)) {
1416 if (nsAccUtils::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 (nsAccUtils::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
= nsAccUtils::Role(accessible
);
1458 if (role
== nsIAccessibleRole::ROLE_TEXT_LEAF
||
1459 role
== nsIAccessibleRole::ROLE_STATICTEXT
) {
1460 PRInt32 textLength
= nsAccUtils::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 nsCoreUtils::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).
1636 nsHyperTextAccessible::GetCaretOffset(PRInt32
*aCaretOffset
)
1640 // No caret if the focused node is not inside this DOM node and this DOM node
1641 // is not inside of focused node.
1642 PRBool isInsideOfFocusedNode
=
1643 nsCoreUtils::IsAncestorOf(gLastFocusedNode
, mDOMNode
);
1645 if (!isInsideOfFocusedNode
&& mDOMNode
!= gLastFocusedNode
&&
1646 !nsCoreUtils::IsAncestorOf(mDOMNode
, gLastFocusedNode
))
1649 // Turn the focus node and offset of the selection into caret hypretext
1651 nsCOMPtr
<nsISelection
> domSel
;
1652 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1653 nsnull
, getter_AddRefs(domSel
));
1654 NS_ENSURE_SUCCESS(rv
, rv
);
1656 nsCOMPtr
<nsIDOMNode
> focusNode
;
1657 rv
= domSel
->GetFocusNode(getter_AddRefs(focusNode
));
1658 NS_ENSURE_SUCCESS(rv
, rv
);
1660 PRInt32 focusOffset
;
1661 rv
= domSel
->GetFocusOffset(&focusOffset
);
1662 NS_ENSURE_SUCCESS(rv
, rv
);
1664 // No caret if this DOM node is inside of focused node but the selection's
1665 // focus point is not inside of this DOM node.
1666 if (isInsideOfFocusedNode
) {
1667 nsCOMPtr
<nsIDOMNode
> resultNode
=
1668 nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode
, focusOffset
);
1669 if (resultNode
!= mDOMNode
&&
1670 !nsCoreUtils::IsAncestorOf(mDOMNode
, resultNode
))
1674 return DOMPointToHypertextOffset(focusNode
, focusOffset
, aCaretOffset
);
1677 PRInt32
nsHyperTextAccessible::GetCaretLineNumber()
1679 // Provide the line number for the caret, relative to the
1680 // currently focused node. Use a 1-based index
1681 nsCOMPtr
<nsISelection
> domSel
;
1682 GetSelections(nsISelectionController::SELECTION_NORMAL
, nsnull
,
1683 getter_AddRefs(domSel
));
1684 nsCOMPtr
<nsISelectionPrivate
> privateSelection(do_QueryInterface(domSel
));
1685 NS_ENSURE_TRUE(privateSelection
, -1);
1686 nsCOMPtr
<nsFrameSelection
> frameSelection
;
1687 privateSelection
->GetFrameSelection(getter_AddRefs(frameSelection
));
1688 NS_ENSURE_TRUE(frameSelection
, -1);
1690 nsCOMPtr
<nsIDOMNode
> caretNode
;
1691 domSel
->GetFocusNode(getter_AddRefs(caretNode
));
1692 nsCOMPtr
<nsIContent
> caretContent
= do_QueryInterface(caretNode
);
1693 if (!caretContent
|| !nsCoreUtils::IsAncestorOf(mDOMNode
, caretNode
)) {
1697 PRInt32 caretOffset
, returnOffsetUnused
;
1698 domSel
->GetFocusOffset(&caretOffset
);
1699 nsFrameSelection::HINT hint
= frameSelection
->GetHint();
1700 nsIFrame
*caretFrame
= frameSelection
->GetFrameForNodeOffset(caretContent
, caretOffset
,
1701 hint
, &returnOffsetUnused
);
1702 NS_ENSURE_TRUE(caretFrame
, -1);
1704 PRInt32 lineNumber
= 1;
1705 nsAutoLineIterator lineIterForCaret
;
1706 nsCOMPtr
<nsIContent
> hyperTextContent
= do_QueryInterface(mDOMNode
);
1707 while (caretFrame
) {
1708 if (hyperTextContent
== caretFrame
->GetContent()) {
1709 return lineNumber
; // Must be in a single line hyper text, there is no line iterator
1711 nsIFrame
*parentFrame
= caretFrame
->GetParent();
1715 // Add lines for the sibling frames before the caret
1716 nsIFrame
*sibling
= parentFrame
->GetFirstChild(nsnull
);
1717 while (sibling
&& sibling
!= caretFrame
) {
1718 nsAutoLineIterator lineIterForSibling
= sibling
->GetLineIterator();
1719 if (lineIterForSibling
) {
1720 // For the frames before that grab all the lines
1721 PRInt32 addLines
= lineIterForSibling
->GetNumLines();
1722 lineNumber
+= addLines
;
1724 sibling
= sibling
->GetNextSibling();
1727 // Get the line number relative to the container with lines
1728 if (!lineIterForCaret
) { // Add the caret line just once
1729 lineIterForCaret
= parentFrame
->GetLineIterator();
1730 if (lineIterForCaret
) {
1731 // Ancestor of caret
1732 PRInt32 addLines
= lineIterForCaret
->FindLineContaining(caretFrame
);
1733 lineNumber
+= addLines
;
1737 caretFrame
= parentFrame
;
1740 NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
1745 nsHyperTextAccessible::GetSelections(PRInt16 aType
,
1746 nsISelectionController
**aSelCon
,
1747 nsISelection
**aDomSel
,
1748 nsCOMArray
<nsIDOMRange
>* aRanges
)
1751 return NS_ERROR_FAILURE
;
1763 nsCOMPtr
<nsISelection
> domSel
;
1764 nsCOMPtr
<nsISelectionController
> selCon
;
1766 nsCOMPtr
<nsIEditor
> editor
;
1767 GetAssociatedEditor(getter_AddRefs(editor
));
1768 nsCOMPtr
<nsIPlaintextEditor
> peditor(do_QueryInterface(editor
));
1770 // Case 1: plain text editor
1771 // This is for form controls which have their own
1772 // selection controller separate from the document, for example
1773 // HTML:input, HTML:textarea, XUL:textbox, etc.
1774 editor
->GetSelectionController(getter_AddRefs(selCon
));
1777 // Case 2: rich content subtree (can be rich editor)
1778 // This uses the selection controller from the entire document
1779 nsIFrame
*frame
= GetFrame();
1780 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
1782 // Get the selection and selection controller
1783 frame
->GetSelectionController(GetPresContext(),
1784 getter_AddRefs(selCon
));
1786 NS_ENSURE_TRUE(selCon
, NS_ERROR_FAILURE
);
1788 selCon
->GetSelection(aType
, getter_AddRefs(domSel
));
1789 NS_ENSURE_TRUE(domSel
, NS_ERROR_FAILURE
);
1792 NS_ADDREF(*aSelCon
= selCon
);
1795 NS_ADDREF(*aDomSel
= domSel
);
1799 nsCOMPtr
<nsISelection2
> selection2(do_QueryInterface(domSel
));
1800 NS_ENSURE_TRUE(selection2
, NS_ERROR_FAILURE
);
1802 nsCOMPtr
<nsIDOMNode
> startNode(mDOMNode
);
1804 nsCOMPtr
<nsIDOMElement
> editorRoot
;
1805 editor
->GetRootElement(getter_AddRefs(editorRoot
));
1806 startNode
= do_QueryInterface(editorRoot
);
1808 NS_ENSURE_STATE(startNode
);
1810 nsCOMPtr
<nsIDOMNodeList
> childNodes
;
1811 nsresult rv
= startNode
->GetChildNodes(getter_AddRefs(childNodes
));
1812 NS_ENSURE_SUCCESS(rv
, rv
);
1813 PRUint32 numChildren
;
1814 rv
= childNodes
->GetLength(&numChildren
);
1815 NS_ENSURE_SUCCESS(rv
, rv
);
1816 rv
= selection2
->GetRangesForIntervalCOMArray(startNode
, 0,
1817 startNode
, numChildren
,
1819 NS_ENSURE_SUCCESS(rv
, rv
);
1820 // Remove collapsed ranges
1821 PRInt32 numRanges
= aRanges
->Count();
1822 for (PRInt32 count
= 0; count
< numRanges
; count
++) {
1824 (*aRanges
)[count
]->GetCollapsed(&isCollapsed
);
1826 aRanges
->RemoveObjectAt(count
);
1837 * Gets the number of selected regions.
1839 NS_IMETHODIMP
nsHyperTextAccessible::GetSelectionCount(PRInt32
*aSelectionCount
)
1841 nsCOMPtr
<nsISelection
> domSel
;
1842 nsCOMArray
<nsIDOMRange
> ranges
;
1843 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1844 nsnull
, nsnull
, &ranges
);
1845 NS_ENSURE_SUCCESS(rv
, rv
);
1847 *aSelectionCount
= ranges
.Count();
1853 * Gets the start and end offset of the specified selection.
1855 NS_IMETHODIMP
nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum
, PRInt32
*aStartOffset
, PRInt32
*aEndOffset
)
1857 *aStartOffset
= *aEndOffset
= 0;
1859 nsCOMPtr
<nsISelection
> domSel
;
1860 nsCOMArray
<nsIDOMRange
> ranges
;
1861 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1862 nsnull
, getter_AddRefs(domSel
), &ranges
);
1863 NS_ENSURE_SUCCESS(rv
, rv
);
1865 PRInt32 rangeCount
= ranges
.Count();
1866 if (aSelectionNum
< 0 || aSelectionNum
>= rangeCount
)
1867 return NS_ERROR_INVALID_ARG
;
1869 nsCOMPtr
<nsIDOMRange
> range
= ranges
[aSelectionNum
];
1872 nsCOMPtr
<nsIDOMNode
> startNode
;
1873 range
->GetStartContainer(getter_AddRefs(startNode
));
1874 PRInt32 startOffset
;
1875 range
->GetStartOffset(&startOffset
);
1878 nsCOMPtr
<nsIDOMNode
> endNode
;
1879 range
->GetEndContainer(getter_AddRefs(endNode
));
1881 range
->GetEndOffset(&endOffset
);
1883 PRInt16 rangeCompareResult
;
1884 rv
= range
->CompareBoundaryPoints(nsIDOMRange::START_TO_END
, range
, &rangeCompareResult
);
1885 NS_ENSURE_SUCCESS(rv
, rv
);
1887 if (rangeCompareResult
< 0) {
1888 // Make sure start is before end, by swapping offsets
1889 // This occurs when the user selects backwards in the text
1890 startNode
.swap(endNode
);
1891 PRInt32 tempOffset
= startOffset
;
1892 startOffset
= endOffset
;
1893 endOffset
= tempOffset
;
1896 nsCOMPtr
<nsIAccessible
> startAccessible
;
1897 rv
= DOMPointToHypertextOffset(startNode
, startOffset
, aStartOffset
, getter_AddRefs(startAccessible
));
1898 NS_ENSURE_SUCCESS(rv
, rv
);
1899 if (!startAccessible
) {
1900 *aStartOffset
= 0; // Could not find start point within this hypertext, so starts before
1903 return DOMPointToHypertextOffset(endNode
, endOffset
, aEndOffset
, nsnull
, PR_TRUE
);
1907 * Changes the start and end offset of the specified selection.
1910 nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum
,
1911 PRInt32 aStartOffset
,
1914 nsCOMPtr
<nsISelection
> domSel
;
1915 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1916 nsnull
, getter_AddRefs(domSel
));
1917 NS_ENSURE_SUCCESS(rv
, rv
);
1919 // Caret is a collapsed selection
1920 PRBool isOnlyCaret
= (aStartOffset
== aEndOffset
);
1923 domSel
->GetRangeCount(&rangeCount
);
1924 nsCOMPtr
<nsIDOMRange
> range
;
1925 if (aSelectionNum
== rangeCount
) { // Add a range
1926 range
= do_CreateInstance(kRangeCID
);
1927 NS_ENSURE_TRUE(range
, NS_ERROR_OUT_OF_MEMORY
);
1929 else if (aSelectionNum
< 0 || aSelectionNum
> rangeCount
) {
1930 return NS_ERROR_INVALID_ARG
;
1933 domSel
->GetRangeAt(aSelectionNum
, getter_AddRefs(range
));
1934 NS_ENSURE_TRUE(range
, NS_ERROR_FAILURE
);
1937 PRInt32 startOffset
, endOffset
;
1938 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
1940 rv
= HypertextOffsetsToDOMRange(aStartOffset
, aEndOffset
,
1941 getter_AddRefs(startNode
), &startOffset
,
1942 getter_AddRefs(endNode
), &endOffset
);
1943 NS_ENSURE_SUCCESS(rv
, rv
);
1945 rv
= range
->SetStart(startNode
, startOffset
);
1946 NS_ENSURE_SUCCESS(rv
, rv
);
1948 rv
= isOnlyCaret
? range
->Collapse(PR_TRUE
) :
1949 range
->SetEnd(endNode
, endOffset
);
1950 NS_ENSURE_SUCCESS(rv
, rv
);
1952 if (aSelectionNum
== rangeCount
) { // Add successfully created new range
1953 return domSel
->AddRange(range
);
1959 * Adds a selection bounded by the specified offsets.
1961 NS_IMETHODIMP
nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset
, PRInt32 aEndOffset
)
1963 nsCOMPtr
<nsISelection
> domSel
;
1964 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1965 nsnull
, getter_AddRefs(domSel
));
1966 NS_ENSURE_SUCCESS(rv
, rv
);
1969 domSel
->GetRangeCount(&rangeCount
);
1971 return SetSelectionBounds(rangeCount
, aStartOffset
, aEndOffset
);
1975 * Removes the specified selection.
1977 NS_IMETHODIMP
nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum
)
1979 nsCOMPtr
<nsISelection
> domSel
;
1980 nsresult rv
= GetSelections(nsISelectionController::SELECTION_NORMAL
,
1981 nsnull
, getter_AddRefs(domSel
));
1982 NS_ENSURE_SUCCESS(rv
, rv
);
1985 domSel
->GetRangeCount(&rangeCount
);
1986 if (aSelectionNum
< 0 || aSelectionNum
>= rangeCount
)
1987 return NS_ERROR_INVALID_ARG
;
1989 nsCOMPtr
<nsIDOMRange
> range
;
1990 domSel
->GetRangeAt(aSelectionNum
, getter_AddRefs(range
));
1991 return domSel
->RemoveRange(range
);
1994 // void nsIAccessibleText::
1995 // scrollSubstringTo(in long startIndex, in long endIndex,
1996 // in unsigned long scrollType);
1998 nsHyperTextAccessible::ScrollSubstringTo(PRInt32 aStartIndex
, PRInt32 aEndIndex
,
1999 PRUint32 aScrollType
)
2001 PRInt32 startOffset
, endOffset
;
2002 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
2004 nsresult rv
= HypertextOffsetsToDOMRange(aStartIndex
, aEndIndex
,
2005 getter_AddRefs(startNode
),
2007 getter_AddRefs(endNode
),
2009 NS_ENSURE_SUCCESS(rv
, rv
);
2011 return nsCoreUtils::ScrollSubstringTo(GetFrame(), startNode
, startOffset
,
2012 endNode
, endOffset
, aScrollType
);
2015 // void nsIAccessibleText::
2016 // scrollSubstringToPoint(in long startIndex, in long endIndex,
2017 // in unsigned long coordinateType,
2018 // in long x, in long y);
2020 nsHyperTextAccessible::ScrollSubstringToPoint(PRInt32 aStartIndex
,
2022 PRUint32 aCoordinateType
,
2023 PRInt32 aX
, PRInt32 aY
)
2025 nsIFrame
*frame
= GetFrame();
2027 return NS_ERROR_FAILURE
;
2030 nsresult rv
= nsAccUtils::ConvertToScreenCoords(aX
, aY
, aCoordinateType
,
2032 NS_ENSURE_SUCCESS(rv
, rv
);
2034 PRInt32 startOffset
, endOffset
;
2035 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
2037 rv
= HypertextOffsetsToDOMRange(aStartIndex
, aEndIndex
,
2038 getter_AddRefs(startNode
), &startOffset
,
2039 getter_AddRefs(endNode
), &endOffset
);
2040 NS_ENSURE_SUCCESS(rv
, rv
);
2042 nsPresContext
*presContext
= frame
->PresContext();
2044 PRBool initialScrolled
= PR_FALSE
;
2045 nsIFrame
*parentFrame
= frame
;
2046 while ((parentFrame
= parentFrame
->GetParent())) {
2047 nsIScrollableFrame
*scrollableFrame
= nsnull
;
2048 CallQueryInterface(parentFrame
, &scrollableFrame
);
2049 if (scrollableFrame
) {
2050 if (!initialScrolled
) {
2051 // Scroll substring to the given point. Turn the point into percents
2052 // relative scrollable area to use nsCoreUtils::ScrollSubstringTo.
2053 nsIntRect frameRect
= parentFrame
->GetScreenRectExternal();
2054 PRInt32 devOffsetX
= coords
.x
- frameRect
.x
;
2055 PRInt32 devOffsetY
= coords
.y
- frameRect
.y
;
2057 nsPoint
offsetPoint(presContext
->DevPixelsToAppUnits(devOffsetX
),
2058 presContext
->DevPixelsToAppUnits(devOffsetY
));
2060 nsSize
size(parentFrame
->GetSize());
2061 PRInt16 hPercent
= offsetPoint
.x
* 100 / size
.width
;
2062 PRInt16 vPercent
= offsetPoint
.y
* 100 / size
.height
;
2064 rv
= nsCoreUtils::ScrollSubstringTo(GetFrame(), startNode
, startOffset
,
2066 vPercent
, hPercent
);
2067 NS_ENSURE_SUCCESS(rv
, rv
);
2069 initialScrolled
= PR_TRUE
;
2071 // Substring was scrolled to the given point already inside its closest
2072 // scrollable area. If there are nested scrollable areas then make
2073 // sure we scroll lower areas to the given point inside currently
2074 // traversed scrollable area.
2075 nsCoreUtils::ScrollFrameToPoint(parentFrame
, frame
, coords
);
2078 frame
= parentFrame
;
2084 nsresult
nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame
*aFrame
, PRInt32 aContentOffset
,
2085 PRUint32
*aRenderedOffset
)
2088 // Current frame not rendered -- this can happen if text is set on
2089 // something with display: none
2090 *aRenderedOffset
= 0;
2093 NS_ASSERTION(aFrame
->GetType() == nsAccessibilityAtoms::textFrame
,
2094 "Need text frame for offset conversion");
2095 NS_ASSERTION(aFrame
->GetPrevContinuation() == nsnull
,
2096 "Call on primary frame only");
2098 gfxSkipChars skipChars
;
2099 gfxSkipCharsIterator iter
;
2100 // Only get info up to original ofset, we know that will be larger than skipped offset
2101 nsresult rv
= aFrame
->GetRenderedText(nsnull
, &skipChars
, &iter
, 0, aContentOffset
);
2102 NS_ENSURE_SUCCESS(rv
, rv
);
2104 PRUint32 ourRenderedStart
= iter
.GetSkippedOffset();
2105 PRInt32 ourContentStart
= iter
.GetOriginalOffset();
2107 *aRenderedOffset
= iter
.ConvertOriginalToSkipped(aContentOffset
+ ourContentStart
) -
2113 nsresult
nsHyperTextAccessible::RenderedToContentOffset(nsIFrame
*aFrame
, PRUint32 aRenderedOffset
,
2114 PRInt32
*aContentOffset
)
2116 *aContentOffset
= 0;
2117 NS_ENSURE_TRUE(aFrame
, NS_ERROR_FAILURE
);
2119 NS_ASSERTION(aFrame
->GetType() == nsAccessibilityAtoms::textFrame
,
2120 "Need text frame for offset conversion");
2121 NS_ASSERTION(aFrame
->GetPrevContinuation() == nsnull
,
2122 "Call on primary frame only");
2124 gfxSkipChars skipChars
;
2125 gfxSkipCharsIterator iter
;
2126 // We only need info up to skipped offset -- that is what we're converting to original offset
2127 nsresult rv
= aFrame
->GetRenderedText(nsnull
, &skipChars
, &iter
, 0, aRenderedOffset
);
2128 NS_ENSURE_SUCCESS(rv
, rv
);
2130 PRUint32 ourRenderedStart
= iter
.GetSkippedOffset();
2131 PRInt32 ourContentStart
= iter
.GetOriginalOffset();
2133 *aContentOffset
= iter
.ConvertSkippedToOriginal(aRenderedOffset
+ ourRenderedStart
) - ourContentStart
;
2139 nsHyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame
*aFrame
,
2141 nsIAccessible
*aAccessible
,
2143 PRInt32
*aNodeOffset
)
2145 NS_ENSURE_ARG(aAccessible
);
2147 nsCOMPtr
<nsIDOMNode
> node
;
2150 // If the given frame is null then set offset after the DOM node of the
2151 // given accessible.
2152 nsCOMPtr
<nsIAccessNode
> accessNode(do_QueryInterface(aAccessible
));
2154 nsCOMPtr
<nsIDOMNode
> DOMNode
;
2155 accessNode
->GetDOMNode(getter_AddRefs(DOMNode
));
2156 nsCOMPtr
<nsIContent
> content(do_QueryInterface(DOMNode
));
2157 NS_ENSURE_STATE(content
);
2159 nsCOMPtr
<nsIContent
> parent(content
->GetParent());
2160 NS_ENSURE_STATE(parent
);
2162 *aNodeOffset
= parent
->IndexOf(content
) + 1;
2163 node
= do_QueryInterface(parent
);
2165 } else if (aFrame
->GetType() == nsAccessibilityAtoms::textFrame
) {
2166 nsCOMPtr
<nsIContent
> content(aFrame
->GetContent());
2167 NS_ENSURE_STATE(content
);
2169 nsCOMPtr
<nsIPresShell
> shell(GetPresShell());
2170 NS_ENSURE_STATE(shell
);
2172 nsIFrame
*primaryFrame
= shell
->GetPrimaryFrameFor(content
);
2173 nsresult rv
= RenderedToContentOffset(primaryFrame
, aOffset
, aNodeOffset
);
2174 NS_ENSURE_SUCCESS(rv
, rv
);
2176 node
= do_QueryInterface(content
);
2179 nsCOMPtr
<nsIContent
> content(aFrame
->GetContent());
2180 NS_ENSURE_STATE(content
);
2182 nsCOMPtr
<nsIContent
> parent(content
->GetParent());
2183 NS_ENSURE_STATE(parent
);
2185 *aNodeOffset
= parent
->IndexOf(content
);
2186 node
= do_QueryInterface(parent
);
2189 NS_IF_ADDREF(*aNode
= node
);
2193 // nsHyperTextAccessible
2195 nsHyperTextAccessible::DOMRangeBoundToHypertextOffset(nsIDOMRange
*aRange
,
2196 PRBool aIsStartBound
,
2197 PRBool aIsStartHTOffset
,
2200 nsCOMPtr
<nsIDOMNode
> node
;
2201 PRInt32 nodeOffset
= 0;
2204 if (aIsStartBound
) {
2205 rv
= aRange
->GetStartContainer(getter_AddRefs(node
));
2206 NS_ENSURE_SUCCESS(rv
, rv
);
2208 rv
= aRange
->GetStartOffset(&nodeOffset
);
2209 NS_ENSURE_SUCCESS(rv
, rv
);
2211 rv
= aRange
->GetEndContainer(getter_AddRefs(node
));
2212 NS_ENSURE_SUCCESS(rv
, rv
);
2214 rv
= aRange
->GetEndOffset(&nodeOffset
);
2215 NS_ENSURE_SUCCESS(rv
, rv
);
2218 nsCOMPtr
<nsIAccessible
> startAcc
;
2219 rv
= DOMPointToHypertextOffset(node
, nodeOffset
, aHTOffset
,
2220 getter_AddRefs(startAcc
));
2221 NS_ENSURE_SUCCESS(rv
, rv
);
2223 if (aIsStartHTOffset
&& !startAcc
)
2229 // nsHyperTextAccessible
2231 nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode
*aNode
,
2232 PRInt32 aNodeOffset
,
2233 PRInt32
*aHTStartOffset
,
2234 PRInt32
*aHTEndOffset
,
2235 nsIPersistentProperties
*aAttributes
)
2237 nsCOMArray
<nsIDOMRange
> ranges
;
2238 nsresult rv
= GetSelections(nsISelectionController::SELECTION_SPELLCHECK
,
2239 nsnull
, nsnull
, &ranges
);
2240 NS_ENSURE_SUCCESS(rv
, rv
);
2242 PRInt32 rangeCount
= ranges
.Count();
2246 for (PRInt32 index
= 0; index
< rangeCount
; index
++) {
2247 nsCOMPtr
<nsIDOMRange
> range
= ranges
[index
];
2248 nsCOMPtr
<nsIDOMNSRange
> nsrange(do_QueryInterface(range
));
2249 NS_ENSURE_STATE(nsrange
);
2252 rv
= nsrange
->ComparePoint(aNode
, aNodeOffset
, &result
);
2253 NS_ENSURE_SUCCESS(rv
, rv
);
2254 // ComparePoint checks boundary points, but we need to check that
2255 // text at aNodeOffset is inside the range.
2256 // See also bug 460690.
2258 nsCOMPtr
<nsIDOMNode
> end
;
2259 rv
= range
->GetEndContainer(getter_AddRefs(end
));
2260 NS_ENSURE_SUCCESS(rv
, rv
);
2262 rv
= range
->GetEndOffset(&endOffset
);
2263 NS_ENSURE_SUCCESS(rv
, rv
);
2264 if (aNode
== end
&& aNodeOffset
== endOffset
) {
2269 if (result
== 1) { // range is before point
2270 PRInt32 startHTOffset
= 0;
2271 rv
= DOMRangeBoundToHypertextOffset(range
, PR_FALSE
, PR_TRUE
,
2273 NS_ENSURE_SUCCESS(rv
, rv
);
2275 if (startHTOffset
> *aHTStartOffset
)
2276 *aHTStartOffset
= startHTOffset
;
2278 } else if (result
== -1) { // range is after point
2279 PRInt32 endHTOffset
= 0;
2280 rv
= DOMRangeBoundToHypertextOffset(range
, PR_TRUE
, PR_FALSE
,
2282 NS_ENSURE_SUCCESS(rv
, rv
);
2284 if (endHTOffset
< *aHTEndOffset
)
2285 *aHTEndOffset
= endHTOffset
;
2287 } else { // point is in range
2288 PRInt32 startHTOffset
= 0;
2289 rv
= DOMRangeBoundToHypertextOffset(range
, PR_TRUE
, PR_TRUE
,
2291 NS_ENSURE_SUCCESS(rv
, rv
);
2293 PRInt32 endHTOffset
= 0;
2294 rv
= DOMRangeBoundToHypertextOffset(range
, PR_FALSE
, PR_FALSE
,
2296 NS_ENSURE_SUCCESS(rv
, rv
);
2298 if (startHTOffset
> *aHTStartOffset
)
2299 *aHTStartOffset
= startHTOffset
;
2300 if (endHTOffset
< *aHTEndOffset
)
2301 *aHTEndOffset
= endHTOffset
;
2304 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::invalid
,
2305 NS_LITERAL_STRING("spelling"));
2315 // nsHyperTextAccessible
2317 nsHyperTextAccessible::GetLangTextAttributes(PRBool aIncludeDefAttrs
,
2318 nsIDOMNode
*aSourceNode
,
2319 PRInt32
*aStartHTOffset
,
2320 PRInt32
*aEndHTOffset
,
2321 nsIPersistentProperties
*aAttributes
)
2323 nsCOMPtr
<nsIDOMElement
> sourceElm(nsCoreUtils::GetDOMElementFor(aSourceNode
));
2325 nsCOMPtr
<nsIContent
> content(do_QueryInterface(sourceElm
));
2326 nsCOMPtr
<nsIContent
> rootContent(do_QueryInterface(mDOMNode
));
2329 nsCoreUtils::GetLanguageFor(content
, rootContent
, lang
);
2331 nsAutoString rootLang
;
2332 nsresult rv
= GetLanguage(rootLang
);
2333 NS_ENSURE_SUCCESS(rv
, rv
);
2336 // Expose 'language' text attribute if the DOM 'lang' attribute is
2337 // presented and it's different from the 'lang' attribute on the root
2338 // element or we should include default values of text attribute.
2339 const nsAString
& resultLang
= lang
.IsEmpty() ? rootLang
: lang
;
2340 if (!resultLang
.IsEmpty() && (aIncludeDefAttrs
|| lang
!= rootLang
))
2341 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::language
,
2345 nsLangTextAttr
textAttr(lang
, rootContent
);
2346 return GetRangeForTextAttr(aSourceNode
, &textAttr
,
2347 aStartHTOffset
, aEndHTOffset
);
2350 // nsHyperTextAccessible
2352 nsHyperTextAccessible::GetCSSTextAttributes(PRBool aIncludeDefAttrs
,
2353 nsIDOMNode
*aSourceNode
,
2354 PRInt32
*aStartHTOffset
,
2355 PRInt32
*aEndHTOffset
,
2356 nsIPersistentProperties
*aAttributes
)
2358 nsCOMPtr
<nsIDOMElement
> sourceElm(nsCoreUtils::GetDOMElementFor(aSourceNode
));
2359 nsCOMPtr
<nsIDOMElement
> rootElm(nsCoreUtils::GetDOMElementFor(mDOMNode
));
2361 nsCSSTextAttr
textAttr(aIncludeDefAttrs
, sourceElm
, rootElm
);
2362 while (textAttr
.Iterate()) {
2364 nsAutoString value
, oldValue
;
2365 if (aAttributes
&& textAttr
.Get(name
, value
))
2366 aAttributes
->SetStringProperty(name
, value
, oldValue
);
2368 nsresult rv
= GetRangeForTextAttr(aSourceNode
, &textAttr
,
2369 aStartHTOffset
, aEndHTOffset
);
2370 NS_ENSURE_SUCCESS(rv
, rv
);
2373 nsIFrame
*sourceFrame
= nsCoreUtils::GetFrameFor(sourceElm
);
2375 nsIFrame
*rootFrame
= nsnull
;
2377 if (!aIncludeDefAttrs
)
2378 rootFrame
= nsCoreUtils::GetFrameFor(rootElm
);
2380 nsBackgroundTextAttr
backgroundTextAttr(sourceFrame
, rootFrame
);
2382 if (backgroundTextAttr
.Get(value
)) {
2383 nsAccUtils::SetAccAttr(aAttributes
,
2384 nsAccessibilityAtoms::backgroundColor
, value
);
2387 nsresult rv
= GetRangeForTextAttr(aSourceNode
, &backgroundTextAttr
,
2388 aStartHTOffset
, aEndHTOffset
);
2395 // nsHyperTextAccessible
2397 nsHyperTextAccessible::GetRangeForTextAttr(nsIDOMNode
*aNode
,
2398 nsTextAttr
*aComparer
,
2399 PRInt32
*aStartHTOffset
,
2400 PRInt32
*aEndHTOffset
)
2402 nsCOMPtr
<nsIDOMElement
> rootElm(nsCoreUtils::GetDOMElementFor(mDOMNode
));
2403 NS_ENSURE_STATE(rootElm
);
2405 nsCOMPtr
<nsIDOMNode
> tmpNode(aNode
);
2406 nsCOMPtr
<nsIDOMNode
> currNode(aNode
);
2408 // Navigate backwards and forwards from current node to the root node to
2409 // calculate range bounds for the text attribute. Navigation sequence is the
2411 // 1. Navigate through the siblings.
2412 // 2. If the traversed sibling has children then navigate from its leaf child
2413 // to it through whole tree of the traversed sibling.
2414 // 3. Get the parent and cycle algorithm until the root node.
2416 // Navigate backwards (find the start offset).
2417 while (currNode
&& currNode
!= rootElm
) {
2418 nsCOMPtr
<nsIDOMElement
> currElm(nsCoreUtils::GetDOMElementFor(currNode
));
2419 NS_ENSURE_STATE(currElm
);
2421 if (currNode
!= aNode
&& !aComparer
->Equal(currElm
)) {
2422 PRInt32 startHTOffset
= 0;
2423 nsCOMPtr
<nsIAccessible
> startAcc
;
2424 nsresult rv
= DOMPointToHypertextOffset(tmpNode
, -1, &startHTOffset
,
2425 getter_AddRefs(startAcc
));
2426 NS_ENSURE_SUCCESS(rv
, rv
);
2431 if (startHTOffset
> *aStartHTOffset
)
2432 *aStartHTOffset
= startHTOffset
;
2437 currNode
->GetPreviousSibling(getter_AddRefs(tmpNode
));
2439 // Navigate through the subtree of traversed children to calculate
2440 // left bound of the range.
2441 FindStartOffsetInSubtree(tmpNode
, currNode
, aComparer
, aStartHTOffset
);
2444 currNode
->GetParentNode(getter_AddRefs(tmpNode
));
2445 currNode
.swap(tmpNode
);
2448 // Navigate forwards (find the end offset).
2449 PRBool moveIntoSubtree
= PR_TRUE
;
2451 while (currNode
&& currNode
!= rootElm
) {
2452 nsCOMPtr
<nsIDOMElement
> currElm(nsCoreUtils::GetDOMElementFor(currNode
));
2453 NS_ENSURE_STATE(currElm
);
2455 // Stop new end offset searching if the given text attribute changes its
2457 if (!aComparer
->Equal(currElm
)) {
2458 PRInt32 endHTOffset
= 0;
2459 nsresult rv
= DOMPointToHypertextOffset(currNode
, -1, &endHTOffset
);
2460 NS_ENSURE_SUCCESS(rv
, rv
);
2462 if (endHTOffset
< *aEndHTOffset
)
2463 *aEndHTOffset
= endHTOffset
;
2468 if (moveIntoSubtree
) {
2469 // Navigate through subtree of traversed node. We use 'moveIntoSubtree'
2470 // flag to avoid traversing the same subtree twice.
2471 currNode
->GetFirstChild(getter_AddRefs(tmpNode
));
2473 FindEndOffsetInSubtree(tmpNode
, aComparer
, aEndHTOffset
);
2476 currNode
->GetNextSibling(getter_AddRefs(tmpNode
));
2477 moveIntoSubtree
= PR_TRUE
;
2479 currNode
->GetParentNode(getter_AddRefs(tmpNode
));
2480 moveIntoSubtree
= PR_FALSE
;
2483 currNode
.swap(tmpNode
);
2491 nsHyperTextAccessible::FindEndOffsetInSubtree(nsIDOMNode
*aCurrNode
,
2492 nsTextAttr
*aComparer
,
2498 nsCOMPtr
<nsIDOMElement
> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode
));
2499 NS_ENSURE_STATE(currElm
);
2501 // If the given text attribute (pointed by nsTextAttr object) changes its
2502 // value on the traversed element then fit the end of range.
2503 if (!aComparer
->Equal(currElm
)) {
2504 PRInt32 endHTOffset
= 0;
2505 nsresult rv
= DOMPointToHypertextOffset(aCurrNode
, -1, &endHTOffset
);
2506 NS_ENSURE_SUCCESS(rv
, rv
);
2508 if (endHTOffset
< *aHTOffset
)
2509 *aHTOffset
= endHTOffset
;
2514 // Deeply traverse into the tree to fit the end of range.
2515 nsCOMPtr
<nsIDOMNode
> nextNode
;
2516 aCurrNode
->GetFirstChild(getter_AddRefs(nextNode
));
2518 PRBool res
= FindEndOffsetInSubtree(nextNode
, aComparer
, aHTOffset
);
2523 aCurrNode
->GetNextSibling(getter_AddRefs(nextNode
));
2525 if (FindEndOffsetInSubtree(nextNode
, aComparer
, aHTOffset
))
2533 nsHyperTextAccessible::FindStartOffsetInSubtree(nsIDOMNode
*aCurrNode
,
2534 nsIDOMNode
*aPrevNode
,
2535 nsTextAttr
*aComparer
,
2541 // Find the closest element back to the traversed element.
2542 nsCOMPtr
<nsIDOMNode
> nextNode
;
2543 aCurrNode
->GetLastChild(getter_AddRefs(nextNode
));
2545 if (FindStartOffsetInSubtree(nextNode
, aPrevNode
, aComparer
, aHTOffset
))
2549 nsCOMPtr
<nsIDOMElement
> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode
));
2550 NS_ENSURE_STATE(currElm
);
2552 // If the given text attribute (pointed by nsTextAttr object) changes its
2553 // value on the traversed element then fit the start of range.
2554 if (!aComparer
->Equal(currElm
)) {
2555 PRInt32 startHTOffset
= 0;
2556 nsCOMPtr
<nsIAccessible
> startAcc
;
2557 nsresult rv
= DOMPointToHypertextOffset(aPrevNode
, -1, &startHTOffset
,
2558 getter_AddRefs(startAcc
));
2559 NS_ENSURE_SUCCESS(rv
, rv
);
2564 if (startHTOffset
> *aHTOffset
)
2565 *aHTOffset
= startHTOffset
;
2570 // Moving backwards to find the start of range.
2571 aCurrNode
->GetPreviousSibling(getter_AddRefs(nextNode
));
2573 if (FindStartOffsetInSubtree(nextNode
, aCurrNode
, aComparer
, aHTOffset
))