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
4 : *
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/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Ginn Chen (ginn.chen@sun.com)
24 : * Aaron Leventhal (aleventh@us.ibm.com)
25 : *
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.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsHyperTextAccessible.h"
41 :
42 : #include "nsAccessibilityService.h"
43 : #include "nsAccUtils.h"
44 : #include "nsDocAccessible.h"
45 : #include "Role.h"
46 : #include "States.h"
47 : #include "TextAttrs.h"
48 :
49 : #include "nsIClipboard.h"
50 : #include "nsContentUtils.h"
51 : #include "nsFocusManager.h"
52 : #include "nsIDOMCharacterData.h"
53 : #include "nsIDOMDocument.h"
54 : #include "nsIDOMRange.h"
55 : #include "nsIDOMXULDocument.h"
56 : #include "nsIEditingSession.h"
57 : #include "nsIEditor.h"
58 : #include "nsIFrame.h"
59 : #include "nsFrameSelection.h"
60 : #include "nsILineIterator.h"
61 : #include "nsIInterfaceRequestorUtils.h"
62 : #include "nsIPlaintextEditor.h"
63 : #include "nsIScrollableFrame.h"
64 : #include "nsISelectionPrivate.h"
65 : #include "nsIServiceManager.h"
66 : #include "nsTextFragment.h"
67 : #include "gfxSkipChars.h"
68 :
69 : using namespace mozilla::a11y;
70 :
71 : ////////////////////////////////////////////////////////////////////////////////
72 : // nsHyperTextAccessible
73 : ////////////////////////////////////////////////////////////////////////////////
74 :
75 0 : nsHyperTextAccessible::
76 : nsHyperTextAccessible(nsIContent* aNode, nsDocAccessible* aDoc) :
77 0 : nsAccessibleWrap(aNode, aDoc)
78 : {
79 0 : mFlags |= eHyperTextAccessible;
80 0 : }
81 :
82 0 : NS_IMPL_ADDREF_INHERITED(nsHyperTextAccessible, nsAccessibleWrap)
83 0 : NS_IMPL_RELEASE_INHERITED(nsHyperTextAccessible, nsAccessibleWrap)
84 :
85 0 : nsresult nsHyperTextAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
86 : {
87 0 : *aInstancePtr = nsnull;
88 :
89 0 : if (aIID.Equals(NS_GET_IID(nsHyperTextAccessible))) {
90 0 : *aInstancePtr = static_cast<nsHyperTextAccessible*>(this);
91 0 : NS_ADDREF_THIS();
92 0 : return NS_OK;
93 : }
94 :
95 0 : if (mRoleMapEntry &&
96 : (mRoleMapEntry->role == roles::GRAPHIC ||
97 : mRoleMapEntry->role == roles::IMAGE_MAP ||
98 : mRoleMapEntry->role == roles::SLIDER ||
99 : mRoleMapEntry->role == roles::PROGRESSBAR ||
100 : mRoleMapEntry->role == roles::SEPARATOR)) {
101 : // ARIA roles that these interfaces are not appropriate for
102 0 : return nsAccessible::QueryInterface(aIID, aInstancePtr);
103 : }
104 :
105 0 : if (aIID.Equals(NS_GET_IID(nsIAccessibleText))) {
106 0 : *aInstancePtr = static_cast<nsIAccessibleText*>(this);
107 0 : NS_ADDREF_THIS();
108 0 : return NS_OK;
109 : }
110 :
111 0 : if (aIID.Equals(NS_GET_IID(nsIAccessibleHyperText))) {
112 0 : *aInstancePtr = static_cast<nsIAccessibleHyperText*>(this);
113 0 : NS_ADDREF_THIS();
114 0 : return NS_OK;
115 : }
116 :
117 0 : if (aIID.Equals(NS_GET_IID(nsIAccessibleEditableText))) {
118 0 : *aInstancePtr = static_cast<nsIAccessibleEditableText*>(this);
119 0 : NS_ADDREF_THIS();
120 0 : return NS_OK;
121 : }
122 :
123 0 : return nsAccessible::QueryInterface(aIID, aInstancePtr);
124 : }
125 :
126 : role
127 0 : nsHyperTextAccessible::NativeRole()
128 : {
129 0 : nsIAtom *tag = mContent->Tag();
130 :
131 0 : if (tag == nsGkAtoms::form)
132 0 : return roles::FORM;
133 :
134 0 : if (tag == nsGkAtoms::blockquote || tag == nsGkAtoms::div ||
135 : tag == nsGkAtoms::nav)
136 0 : return roles::SECTION;
137 :
138 0 : if (tag == nsGkAtoms::h1 || tag == nsGkAtoms::h2 ||
139 : tag == nsGkAtoms::h3 || tag == nsGkAtoms::h4 ||
140 : tag == nsGkAtoms::h5 || tag == nsGkAtoms::h6)
141 0 : return roles::HEADING;
142 :
143 0 : if (tag == nsGkAtoms::article)
144 0 : return roles::DOCUMENT;
145 :
146 : // Deal with html landmark elements
147 0 : if (tag == nsGkAtoms::header)
148 0 : return roles::HEADER;
149 :
150 0 : if (tag == nsGkAtoms::footer)
151 0 : return roles::FOOTER;
152 :
153 0 : if (tag == nsGkAtoms::aside)
154 0 : return roles::NOTE;
155 :
156 : // Treat block frames as paragraphs
157 0 : nsIFrame *frame = GetFrame();
158 0 : if (frame && frame->GetType() == nsGkAtoms::blockFrame)
159 0 : return roles::PARAGRAPH;
160 :
161 0 : return roles::TEXT_CONTAINER; // In ATK this works
162 : }
163 :
164 : PRUint64
165 0 : nsHyperTextAccessible::NativeState()
166 : {
167 0 : PRUint64 states = nsAccessibleWrap::NativeState();
168 :
169 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
170 0 : if (editor) {
171 : PRUint32 flags;
172 0 : editor->GetFlags(&flags);
173 0 : if (0 == (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
174 0 : states |= states::EDITABLE;
175 : }
176 0 : } else if (mContent->Tag() == nsGkAtoms::article) {
177 : // We want <article> to behave like a document in terms of readonly state.
178 0 : states |= states::READONLY;
179 : }
180 :
181 0 : if (GetChildCount() > 0)
182 0 : states |= states::SELECTABLE_TEXT;
183 :
184 0 : return states;
185 : }
186 :
187 : // Substring must be entirely within the same text node
188 0 : nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRUint32 aStartRenderedOffset,
189 : PRUint32 aEndRenderedOffset)
190 : {
191 0 : nsIntRect screenRect;
192 0 : NS_ENSURE_TRUE(aFrame, screenRect);
193 0 : if (aFrame->GetType() != nsGkAtoms::textFrame) {
194 : // XXX fallback for non-text frames, happens for bullets right now
195 : // but in the future bullets will have proper text frames
196 0 : return aFrame->GetScreenRectExternal();
197 : }
198 :
199 : PRInt32 startContentOffset, endContentOffset;
200 0 : nsresult rv = RenderedToContentOffset(aFrame, aStartRenderedOffset, &startContentOffset);
201 0 : NS_ENSURE_SUCCESS(rv, screenRect);
202 0 : rv = RenderedToContentOffset(aFrame, aEndRenderedOffset, &endContentOffset);
203 0 : NS_ENSURE_SUCCESS(rv, screenRect);
204 :
205 : nsIFrame *frame;
206 : PRInt32 startContentOffsetInFrame;
207 : // Get the right frame continuation -- not really a child, but a sibling of
208 : // the primary frame passed in
209 : rv = aFrame->GetChildFrameContainingOffset(startContentOffset, false,
210 0 : &startContentOffsetInFrame, &frame);
211 0 : NS_ENSURE_SUCCESS(rv, screenRect);
212 :
213 0 : NS_ENSURE_TRUE(mDoc, screenRect);
214 0 : nsIPresShell* shell = mDoc->PresShell();
215 0 : NS_ENSURE_TRUE(shell, screenRect);
216 :
217 0 : nsPresContext *context = shell->GetPresContext();
218 :
219 0 : while (frame && startContentOffset < endContentOffset) {
220 : // Start with this frame's screen rect, which we will
221 : // shrink based on the substring we care about within it.
222 : // We will then add that frame to the total screenRect we
223 : // are returning.
224 0 : nsIntRect frameScreenRect = frame->GetScreenRectExternal();
225 :
226 : // Get the length of the substring in this frame that we want the bounds for
227 : PRInt32 startFrameTextOffset, endFrameTextOffset;
228 0 : frame->GetOffsets(startFrameTextOffset, endFrameTextOffset);
229 0 : PRInt32 frameTotalTextLength = endFrameTextOffset - startFrameTextOffset;
230 0 : PRInt32 seekLength = endContentOffset - startContentOffset;
231 0 : PRInt32 frameSubStringLength = NS_MIN(frameTotalTextLength - startContentOffsetInFrame, seekLength);
232 :
233 : // Add the point where the string starts to the frameScreenRect
234 0 : nsPoint frameTextStartPoint;
235 0 : rv = frame->GetPointFromOffset(startContentOffset, &frameTextStartPoint);
236 0 : NS_ENSURE_SUCCESS(rv, nsIntRect());
237 0 : frameScreenRect.x += context->AppUnitsToDevPixels(frameTextStartPoint.x);
238 :
239 : // Use the point for the end offset to calculate the width
240 0 : nsPoint frameTextEndPoint;
241 0 : rv = frame->GetPointFromOffset(startContentOffset + frameSubStringLength, &frameTextEndPoint);
242 0 : NS_ENSURE_SUCCESS(rv, nsIntRect());
243 0 : frameScreenRect.width = context->AppUnitsToDevPixels(frameTextEndPoint.x - frameTextStartPoint.x);
244 :
245 0 : screenRect.UnionRect(frameScreenRect, screenRect);
246 :
247 : // Get ready to loop back for next frame continuation
248 0 : startContentOffset += frameSubStringLength;
249 0 : startContentOffsetInFrame = 0;
250 0 : frame = frame->GetNextContinuation();
251 : }
252 :
253 0 : return screenRect;
254 : }
255 :
256 : /*
257 : * Gets the specified text.
258 : */
259 : nsIFrame*
260 0 : nsHyperTextAccessible::GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset,
261 : nsAString *aText, nsIFrame **aEndFrame,
262 : nsIntRect *aBoundsRect,
263 : nsAccessible **aStartAcc,
264 : nsAccessible **aEndAcc)
265 : {
266 0 : if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
267 0 : aStartOffset = CharacterCount();
268 : }
269 0 : if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
270 0 : GetCaretOffset(&aStartOffset);
271 : }
272 0 : if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
273 0 : aEndOffset = CharacterCount();
274 : }
275 0 : if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
276 0 : GetCaretOffset(&aEndOffset);
277 : }
278 :
279 0 : PRInt32 startOffset = aStartOffset;
280 0 : PRInt32 endOffset = aEndOffset;
281 : // XXX this prevents text interface usage on <input type="password">
282 0 : bool isPassword = (Role() == roles::PASSWORD_TEXT);
283 :
284 : // Clear out parameters and set up loop
285 0 : if (aText) {
286 0 : aText->Truncate();
287 : }
288 0 : if (endOffset < 0) {
289 0 : const PRInt32 kMaxTextLength = 32767;
290 0 : endOffset = kMaxTextLength; // Max end offset
291 : }
292 0 : else if (startOffset > endOffset) {
293 0 : return nsnull;
294 : }
295 :
296 0 : nsIFrame *startFrame = nsnull;
297 0 : if (aEndFrame) {
298 0 : *aEndFrame = nsnull;
299 : }
300 0 : if (aBoundsRect) {
301 0 : aBoundsRect->SetEmpty();
302 : }
303 0 : if (aStartAcc)
304 0 : *aStartAcc = nsnull;
305 0 : if (aEndAcc)
306 0 : *aEndAcc = nsnull;
307 :
308 0 : nsIntRect unionRect;
309 0 : nsAccessible *lastAccessible = nsnull;
310 :
311 0 : gfxSkipChars skipChars;
312 0 : gfxSkipCharsIterator iter;
313 :
314 : // Loop through children and collect valid offsets, text and bounds
315 : // depending on what we need for out parameters.
316 0 : PRInt32 childCount = GetChildCount();
317 0 : for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
318 0 : nsAccessible *childAcc = mChildren[childIdx];
319 0 : lastAccessible = childAcc;
320 :
321 0 : nsIFrame *frame = childAcc->GetFrame();
322 0 : if (!frame) {
323 0 : continue;
324 : }
325 0 : nsIFrame *primaryFrame = frame;
326 0 : if (nsAccUtils::IsText(childAcc)) {
327 : // We only need info up to rendered offset -- that is what we're
328 : // converting to content offset
329 0 : PRInt32 substringEndOffset = -1;
330 0 : PRUint32 ourRenderedStart = 0;
331 0 : PRInt32 ourContentStart = 0;
332 0 : if (frame->GetType() == nsGkAtoms::textFrame) {
333 0 : nsresult rv = frame->GetRenderedText(nsnull, &skipChars, &iter);
334 0 : if (NS_SUCCEEDED(rv)) {
335 0 : ourRenderedStart = iter.GetSkippedOffset();
336 0 : ourContentStart = iter.GetOriginalOffset();
337 : substringEndOffset =
338 0 : iter.ConvertOriginalToSkipped(skipChars.GetOriginalCharCount() +
339 0 : ourContentStart) - ourRenderedStart;
340 : }
341 : }
342 0 : if (substringEndOffset < 0) {
343 : // XXX for non-textframe text like list bullets,
344 : // should go away after list bullet rewrite
345 0 : substringEndOffset = nsAccUtils::TextLength(childAcc);
346 : }
347 0 : if (startOffset < substringEndOffset) {
348 : // Our start is within this substring
349 0 : if (startOffset > 0 || endOffset < substringEndOffset) {
350 : // We don't want the whole string for this accessible
351 : // Get out the continuing text frame with this offset
352 : PRInt32 outStartLineUnused;
353 : PRInt32 contentOffset;
354 0 : if (frame->GetType() == nsGkAtoms::textFrame) {
355 0 : contentOffset = iter.ConvertSkippedToOriginal(startOffset) +
356 0 : ourRenderedStart - ourContentStart;
357 : }
358 : else {
359 0 : contentOffset = startOffset;
360 : }
361 : frame->GetChildFrameContainingOffset(contentOffset, true,
362 0 : &outStartLineUnused, &frame);
363 0 : if (aEndFrame) {
364 0 : *aEndFrame = frame; // We ended in the current frame
365 0 : if (aEndAcc)
366 0 : NS_ADDREF(*aEndAcc = childAcc);
367 : }
368 0 : if (substringEndOffset > endOffset) {
369 : // Need to stop before the end of the available text
370 0 : substringEndOffset = endOffset;
371 : }
372 0 : aEndOffset = endOffset;
373 : }
374 0 : if (aText) {
375 0 : if (isPassword) {
376 0 : for (PRInt32 count = startOffset; count < substringEndOffset; count ++)
377 0 : *aText += '*'; // Show *'s only for password text
378 : }
379 : else {
380 : childAcc->AppendTextTo(*aText, startOffset,
381 0 : substringEndOffset - startOffset);
382 : }
383 : }
384 0 : if (aBoundsRect) { // Caller wants the bounds of the text
385 : aBoundsRect->UnionRect(*aBoundsRect,
386 : GetBoundsForString(primaryFrame, startOffset,
387 0 : substringEndOffset));
388 : }
389 0 : if (!startFrame) {
390 0 : startFrame = frame;
391 0 : aStartOffset = startOffset;
392 0 : if (aStartAcc)
393 0 : NS_ADDREF(*aStartAcc = childAcc);
394 : }
395 : // We already started copying in this accessible's string,
396 : // for the next accessible we'll start at offset 0
397 0 : startOffset = 0;
398 : }
399 : else {
400 : // We have not found the start position yet, get the new startOffset
401 : // that is relative to next accessible
402 0 : startOffset -= substringEndOffset;
403 : }
404 : // The endOffset needs to be relative to the new startOffset
405 0 : endOffset -= substringEndOffset;
406 : }
407 : else {
408 : // Embedded object, append marker
409 : // XXX Append \n for <br>'s
410 0 : if (startOffset >= 1) {
411 0 : -- startOffset;
412 : }
413 : else {
414 0 : if (endOffset > 0) {
415 0 : if (aText) {
416 : // XXX: should use nsIAccessible::AppendTextTo.
417 0 : if (frame->GetType() == nsGkAtoms::brFrame) {
418 0 : *aText += kForcedNewLineChar;
419 0 : } else if (nsAccUtils::MustPrune(this)) {
420 0 : *aText += kImaginaryEmbeddedObjectChar;
421 : // Expose imaginary embedded object character if the accessible
422 : // hans't children.
423 : } else {
424 0 : *aText += kEmbeddedObjectChar;
425 : }
426 : }
427 0 : if (aBoundsRect) {
428 : aBoundsRect->UnionRect(*aBoundsRect,
429 0 : frame->GetScreenRectExternal());
430 : }
431 : }
432 0 : if (!startFrame) {
433 0 : startFrame = frame;
434 0 : aStartOffset = 0;
435 0 : if (aStartAcc)
436 0 : NS_ADDREF(*aStartAcc = childAcc);
437 : }
438 : }
439 0 : -- endOffset;
440 : }
441 0 : if (endOffset <= 0 && startFrame) {
442 0 : break; // If we don't have startFrame yet, get that in next loop iteration
443 : }
444 : }
445 :
446 0 : if (aStartAcc && !*aStartAcc) {
447 0 : NS_IF_ADDREF(*aStartAcc = lastAccessible);
448 : }
449 0 : if (aEndFrame && !*aEndFrame) {
450 0 : *aEndFrame = startFrame;
451 0 : if (aStartAcc && aEndAcc)
452 0 : NS_IF_ADDREF(*aEndAcc = *aStartAcc);
453 : }
454 :
455 0 : return startFrame;
456 : }
457 :
458 : NS_IMETHODIMP
459 0 : nsHyperTextAccessible::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset,
460 : nsAString &aText)
461 : {
462 0 : aText.Truncate();
463 :
464 0 : if (IsDefunct())
465 0 : return NS_ERROR_FAILURE;
466 :
467 0 : PRInt32 startOffset = ConvertMagicOffset(aStartOffset);
468 0 : PRInt32 startChildIdx = GetChildIndexAtOffset(startOffset);
469 0 : if (startChildIdx == -1)
470 0 : return NS_ERROR_INVALID_ARG;
471 :
472 0 : PRInt32 endOffset = ConvertMagicOffset(aEndOffset);
473 0 : PRInt32 endChildIdx = GetChildIndexAtOffset(endOffset);
474 0 : if (endChildIdx == -1)
475 0 : return NS_ERROR_INVALID_ARG;
476 :
477 0 : if (startChildIdx == endChildIdx) {
478 0 : PRInt32 childOffset = GetChildOffset(startChildIdx);
479 0 : NS_ENSURE_STATE(childOffset != -1);
480 :
481 0 : nsAccessible* child = GetChildAt(startChildIdx);
482 : child->AppendTextTo(aText, startOffset - childOffset,
483 0 : endOffset - startOffset);
484 :
485 0 : return NS_OK;
486 : }
487 :
488 0 : PRInt32 startChildOffset = GetChildOffset(startChildIdx);
489 0 : NS_ENSURE_STATE(startChildOffset != -1);
490 :
491 0 : nsAccessible* startChild = GetChildAt(startChildIdx);
492 0 : startChild->AppendTextTo(aText, startOffset - startChildOffset);
493 :
494 0 : for (PRInt32 childIdx = startChildIdx + 1; childIdx < endChildIdx; childIdx++) {
495 0 : nsAccessible* child = GetChildAt(childIdx);
496 0 : child->AppendTextTo(aText);
497 : }
498 :
499 0 : PRInt32 endChildOffset = GetChildOffset(endChildIdx);
500 0 : NS_ENSURE_STATE(endChildOffset != -1);
501 :
502 0 : nsAccessible* endChild = GetChildAt(endChildIdx);
503 0 : endChild->AppendTextTo(aText, 0, endOffset - endChildOffset);
504 :
505 0 : return NS_OK;
506 : }
507 :
508 : /*
509 : * Gets the character count.
510 : */
511 0 : NS_IMETHODIMP nsHyperTextAccessible::GetCharacterCount(PRInt32 *aCharacterCount)
512 : {
513 0 : NS_ENSURE_ARG_POINTER(aCharacterCount);
514 0 : *aCharacterCount = 0;
515 :
516 0 : if (IsDefunct())
517 0 : return NS_ERROR_FAILURE;
518 :
519 0 : *aCharacterCount = CharacterCount();
520 0 : return NS_OK;
521 : }
522 :
523 : /*
524 : * Gets the specified character.
525 : */
526 0 : NS_IMETHODIMP nsHyperTextAccessible::GetCharacterAtOffset(PRInt32 aOffset, PRUnichar *aCharacter)
527 : {
528 0 : NS_ENSURE_ARG_POINTER(aCharacter);
529 0 : *aCharacter = nsnull;
530 :
531 0 : if (IsDefunct())
532 0 : return NS_ERROR_FAILURE;
533 :
534 0 : nsAutoString character;
535 0 : if (GetCharAt(aOffset, eGetAt, character)) {
536 0 : *aCharacter = character.First();
537 0 : return NS_OK;
538 : }
539 :
540 0 : return NS_ERROR_INVALID_ARG;
541 : }
542 :
543 : nsAccessible*
544 0 : nsHyperTextAccessible::DOMPointToHypertextOffset(nsINode *aNode,
545 : PRInt32 aNodeOffset,
546 : PRInt32 *aHyperTextOffset,
547 : bool aIsEndOffset)
548 : {
549 0 : if (!aHyperTextOffset)
550 0 : return nsnull;
551 0 : *aHyperTextOffset = 0;
552 :
553 0 : if (!aNode)
554 0 : return nsnull;
555 :
556 0 : PRUint32 addTextOffset = 0;
557 0 : nsINode* findNode = nsnull;
558 :
559 0 : if (aNodeOffset == -1) {
560 0 : findNode = aNode;
561 :
562 0 : } else if (aNode->IsNodeOfType(nsINode::eTEXT)) {
563 : // For text nodes, aNodeOffset comes in as a character offset
564 : // Text offset will be added at the end, if we find the offset in this hypertext
565 : // We want the "skipped" offset into the text (rendered text without the extra whitespace)
566 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
567 0 : NS_ASSERTION(content, "No nsIContent for dom node");
568 0 : nsIFrame *frame = content->GetPrimaryFrame();
569 0 : NS_ENSURE_TRUE(frame, nsnull);
570 0 : nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &addTextOffset);
571 0 : NS_ENSURE_SUCCESS(rv, nsnull);
572 : // Get the child node and
573 0 : findNode = aNode;
574 :
575 : } else {
576 : // findNode could be null if aNodeOffset == # of child nodes, which means
577 : // one of two things:
578 : // 1) we're at the end of the children, keep findNode = null, so that we get
579 : // the last possible offset
580 : // 2) there are no children and the passed-in node is mContent, which means
581 : // we're an aempty nsIAccessibleText
582 : // 3) there are no children, and the passed-in node is not mContent -- use
583 : // parentContent for the node to find
584 :
585 0 : findNode = aNode->GetChildAt(aNodeOffset);
586 0 : if (!findNode && !aNodeOffset) {
587 0 : if (aNode == GetNode()) {
588 : // There are no children, which means this is an empty nsIAccessibleText, in which
589 : // case we can only be at hypertext offset 0
590 0 : *aHyperTextOffset = 0;
591 0 : return nsnull;
592 : }
593 0 : findNode = aNode; // Case #2: there are no children
594 : }
595 : }
596 :
597 : // Get accessible for this findNode, or if that node isn't accessible, use the
598 : // accessible for the next DOM node which has one (based on forward depth first search)
599 0 : nsAccessible *descendantAcc = nsnull;
600 0 : if (findNode) {
601 0 : nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
602 0 : if (findContent && findContent->IsHTML() &&
603 0 : findContent->NodeInfo()->Equals(nsGkAtoms::br) &&
604 0 : findContent->AttrValueIs(kNameSpaceID_None,
605 : nsGkAtoms::mozeditorbogusnode,
606 : nsGkAtoms::_true,
607 0 : eIgnoreCase)) {
608 : // This <br> is the hacky "bogus node" used when there is no text in a control
609 0 : *aHyperTextOffset = 0;
610 0 : return nsnull;
611 : }
612 0 : descendantAcc = GetFirstAvailableAccessible(findNode);
613 : }
614 :
615 : // From the descendant, go up and get the immediate child of this hypertext
616 0 : nsAccessible* childAccAtOffset = nsnull;
617 0 : while (descendantAcc) {
618 0 : nsAccessible* parentAcc = descendantAcc->Parent();
619 0 : if (parentAcc == this) {
620 0 : childAccAtOffset = descendantAcc;
621 0 : break;
622 : }
623 :
624 : // This offset no longer applies because the passed-in text object is not a child
625 : // of the hypertext. This happens when there are nested hypertexts, e.g.
626 : // <div>abc<h1>def</h1>ghi</div>
627 : // If the passed-in DOM point was not on a direct child of the hypertext, we will
628 : // return the offset for that entire hypertext
629 0 : if (aIsEndOffset) {
630 : // Not inclusive, the indicated char comes at index before this offset
631 : // If the end offset is after the first character of the passed in object, use 1 for
632 : // addTextOffset, to put us after the embedded object char. We'll only treat the offset as
633 : // before the embedded object char if we end at the very beginning of the child.
634 0 : addTextOffset = addTextOffset > 0;
635 : }
636 : else {
637 : // Start offset, inclusive
638 : // Make sure the offset lands on the embedded object character in order to indicate
639 : // the true inner offset is inside the subtree for that link
640 : addTextOffset =
641 0 : (nsAccUtils::TextLength(descendantAcc) == addTextOffset) ? 1 : 0;
642 : }
643 :
644 0 : descendantAcc = parentAcc;
645 : }
646 :
647 : // Loop through, adding offsets until we reach childAccessible
648 : // If childAccessible is null we will end up adding up the entire length of
649 : // the hypertext, which is good -- it just means our offset node
650 : // came after the last accessible child's node
651 0 : PRInt32 childCount = GetChildCount();
652 :
653 0 : PRInt32 childIdx = 0;
654 0 : nsAccessible *childAcc = nsnull;
655 0 : for (; childIdx < childCount; childIdx++) {
656 0 : childAcc = mChildren[childIdx];
657 0 : if (childAcc == childAccAtOffset)
658 0 : break;
659 :
660 0 : *aHyperTextOffset += nsAccUtils::TextLength(childAcc);
661 : }
662 :
663 0 : if (childIdx < childCount) {
664 0 : *aHyperTextOffset += addTextOffset;
665 0 : NS_ASSERTION(childAcc == childAccAtOffset,
666 : "These should be equal whenever we exit loop and childAcc != nsnull");
667 :
668 0 : if (childIdx < childCount - 1 ||
669 0 : addTextOffset < nsAccUtils::TextLength(childAccAtOffset)) {
670 : // If not at end of last text node, we will return the accessible we were in
671 0 : return childAccAtOffset;
672 : }
673 : }
674 :
675 0 : return nsnull;
676 : }
677 :
678 : nsresult
679 0 : nsHyperTextAccessible::HypertextOffsetToDOMPoint(PRInt32 aHTOffset,
680 : nsIDOMNode **aNode,
681 : PRInt32 *aOffset)
682 : {
683 0 : nsCOMPtr<nsIDOMNode> endNode;
684 : PRInt32 endOffset;
685 :
686 : return HypertextOffsetsToDOMRange(aHTOffset, aHTOffset, aNode, aOffset,
687 0 : getter_AddRefs(endNode), &endOffset);
688 : }
689 :
690 : nsresult
691 0 : nsHyperTextAccessible::HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset,
692 : PRInt32 aEndHTOffset,
693 : nsIDOMNode **aStartNode,
694 : PRInt32 *aStartOffset,
695 : nsIDOMNode **aEndNode,
696 : PRInt32 *aEndOffset)
697 : {
698 0 : NS_ENSURE_ARG_POINTER(aStartNode);
699 0 : *aStartNode = nsnull;
700 :
701 0 : NS_ENSURE_ARG_POINTER(aStartOffset);
702 0 : *aStartOffset = -1;
703 :
704 0 : NS_ENSURE_ARG_POINTER(aEndNode);
705 0 : *aEndNode = nsnull;
706 :
707 0 : NS_ENSURE_ARG_POINTER(aEndOffset);
708 0 : *aEndOffset = -1;
709 :
710 : // If the given offsets are 0 and associated editor is empty then return
711 : // collapsed range with editor root element as range container.
712 0 : if (aStartHTOffset == 0 && aEndHTOffset == 0) {
713 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
714 0 : if (editor) {
715 0 : bool isEmpty = false;
716 0 : editor->GetDocumentIsEmpty(&isEmpty);
717 0 : if (isEmpty) {
718 0 : nsCOMPtr<nsIDOMElement> editorRootElm;
719 0 : editor->GetRootElement(getter_AddRefs(editorRootElm));
720 :
721 0 : nsCOMPtr<nsIDOMNode> editorRoot(do_QueryInterface(editorRootElm));
722 0 : if (editorRoot) {
723 0 : *aStartOffset = *aEndOffset = 0;
724 0 : NS_ADDREF(*aStartNode = editorRoot);
725 0 : NS_ADDREF(*aEndNode = editorRoot);
726 :
727 0 : return NS_OK;
728 : }
729 : }
730 : }
731 : }
732 :
733 0 : nsRefPtr<nsAccessible> startAcc, endAcc;
734 0 : PRInt32 startOffset = aStartHTOffset, endOffset = aEndHTOffset;
735 0 : nsIFrame *startFrame = nsnull, *endFrame = nsnull;
736 :
737 : startFrame = GetPosAndText(startOffset, endOffset, nsnull, &endFrame, nsnull,
738 0 : getter_AddRefs(startAcc), getter_AddRefs(endAcc));
739 0 : if (!startAcc || !endAcc)
740 0 : return NS_ERROR_FAILURE;
741 :
742 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
743 : nsresult rv = GetDOMPointByFrameOffset(startFrame, startOffset, startAcc,
744 0 : getter_AddRefs(startNode),
745 0 : &startOffset);
746 0 : NS_ENSURE_SUCCESS(rv, rv);
747 :
748 0 : if (aStartHTOffset != aEndHTOffset) {
749 : rv = GetDOMPointByFrameOffset(endFrame, endOffset, endAcc,
750 0 : getter_AddRefs(endNode), &endOffset);
751 0 : NS_ENSURE_SUCCESS(rv, rv);
752 : } else {
753 0 : endNode = startNode;
754 0 : endOffset = startOffset;
755 : }
756 :
757 0 : NS_ADDREF(*aStartNode = startNode);
758 0 : *aStartOffset = startOffset;
759 :
760 0 : NS_ADDREF(*aEndNode = endNode);
761 0 : *aEndOffset = endOffset;
762 :
763 0 : return NS_OK;
764 : }
765 :
766 : PRInt32
767 0 : nsHyperTextAccessible::GetRelativeOffset(nsIPresShell *aPresShell,
768 : nsIFrame *aFromFrame,
769 : PRInt32 aFromOffset,
770 : nsAccessible *aFromAccessible,
771 : nsSelectionAmount aAmount,
772 : nsDirection aDirection,
773 : bool aNeedsStart)
774 : {
775 0 : const bool kIsJumpLinesOk = true; // okay to jump lines
776 0 : const bool kIsScrollViewAStop = false; // do not stop at scroll views
777 0 : const bool kIsKeyboardSelect = true; // is keyboard selection
778 0 : const bool kIsVisualBidi = false; // use visual order for bidi text
779 :
780 0 : EWordMovementType wordMovementType = aNeedsStart ? eStartWord : eEndWord;
781 0 : if (aAmount == eSelectLine) {
782 0 : aAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
783 : }
784 :
785 : // Ask layout for the new node and offset, after moving the appropriate amount
786 0 : nsPeekOffsetStruct pos;
787 :
788 : nsresult rv;
789 0 : PRInt32 contentOffset = aFromOffset;
790 0 : if (nsAccUtils::IsText(aFromAccessible)) {
791 0 : nsIFrame *frame = aFromAccessible->GetFrame();
792 0 : NS_ENSURE_TRUE(frame, -1);
793 :
794 0 : if (frame->GetType() == nsGkAtoms::textFrame) {
795 0 : rv = RenderedToContentOffset(frame, aFromOffset, &contentOffset);
796 0 : NS_ENSURE_SUCCESS(rv, -1);
797 : }
798 : }
799 :
800 : pos.SetData(aAmount, aDirection, contentOffset,
801 : 0, kIsJumpLinesOk, kIsScrollViewAStop, kIsKeyboardSelect, kIsVisualBidi,
802 0 : wordMovementType);
803 0 : rv = aFromFrame->PeekOffset(&pos);
804 0 : if (NS_FAILED(rv)) {
805 0 : if (aDirection == eDirPrevious) {
806 : // Use passed-in frame as starting point in failure case for now,
807 : // this is a hack to deal with starting on a list bullet frame,
808 : // which fails in PeekOffset() because the line iterator doesn't see it.
809 : // XXX Need to look at our overall handling of list bullets, which are an odd case
810 0 : pos.mResultContent = aFromFrame->GetContent();
811 : PRInt32 endOffsetUnused;
812 0 : aFromFrame->GetOffsets(pos.mContentOffset, endOffsetUnused);
813 : }
814 : else {
815 0 : return -1;
816 : }
817 : }
818 :
819 : // Turn the resulting node and offset into a hyperTextOffset
820 : PRInt32 hyperTextOffset;
821 0 : if (!pos.mResultContent)
822 0 : return -1;
823 :
824 : // If finalAccessible is nsnull, then DOMPointToHypertextOffset() searched
825 : // through the hypertext children without finding the node/offset position.
826 : nsAccessible *finalAccessible =
827 : DOMPointToHypertextOffset(pos.mResultContent, pos.mContentOffset,
828 0 : &hyperTextOffset, aDirection == eDirNext);
829 :
830 0 : if (!finalAccessible && aDirection == eDirPrevious) {
831 : // If we reached the end during search, this means we didn't find the DOM point
832 : // and we're actually at the start of the paragraph
833 0 : hyperTextOffset = 0;
834 : }
835 0 : else if (aAmount == eSelectBeginLine) {
836 0 : nsAccessible *firstChild = mChildren.SafeElementAt(0, nsnull);
837 : // For line selection with needsStart, set start of line exactly to line break
838 0 : if (pos.mContentOffset == 0 && firstChild &&
839 0 : firstChild->Role() == roles::STATICTEXT &&
840 0 : static_cast<PRInt32>(nsAccUtils::TextLength(firstChild)) == hyperTextOffset) {
841 : // XXX Bullet hack -- we should remove this once list bullets use anonymous content
842 0 : hyperTextOffset = 0;
843 : }
844 0 : if (!aNeedsStart && hyperTextOffset > 0) {
845 0 : -- hyperTextOffset;
846 : }
847 : }
848 0 : else if (aAmount == eSelectEndLine && finalAccessible) {
849 : // If not at very end of hypertext, we may need change the end of line offset by 1,
850 : // to make sure we are in the right place relative to the line ending
851 0 : if (finalAccessible->Role() == roles::WHITESPACE) { // Landed on <br> hard line break
852 : // if aNeedsStart, set end of line exactly 1 character past line break
853 : // XXX It would be cleaner if we did not have to have the hard line break check,
854 : // and just got the correct results from PeekOffset() for the <br> case -- the returned offset should
855 : // come after the new line, as it does in other cases.
856 0 : ++ hyperTextOffset; // Get past hard line break
857 : }
858 : // We are now 1 character past the line break
859 0 : if (!aNeedsStart) {
860 0 : -- hyperTextOffset;
861 : }
862 : }
863 :
864 0 : return hyperTextOffset;
865 : }
866 :
867 : /*
868 : Gets the specified text relative to aBoundaryType, which means:
869 : BOUNDARY_CHAR The character before/at/after the offset is returned.
870 : BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start.
871 : BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end.
872 : BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start.
873 : BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start.
874 : */
875 :
876 0 : nsresult nsHyperTextAccessible::GetTextHelper(EGetTextType aType, nsAccessibleTextBoundary aBoundaryType,
877 : PRInt32 aOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset,
878 : nsAString &aText)
879 : {
880 0 : aText.Truncate();
881 :
882 0 : NS_ENSURE_ARG_POINTER(aStartOffset);
883 0 : NS_ENSURE_ARG_POINTER(aEndOffset);
884 0 : *aStartOffset = *aEndOffset = 0;
885 :
886 0 : if (!mDoc)
887 0 : return NS_ERROR_FAILURE;
888 :
889 0 : nsIPresShell* presShell = mDoc->PresShell();
890 0 : if (!presShell) {
891 0 : return NS_ERROR_FAILURE;
892 : }
893 :
894 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
895 0 : aOffset = CharacterCount();
896 : }
897 0 : if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
898 0 : GetCaretOffset(&aOffset);
899 0 : if (aOffset > 0 && (aBoundaryType == BOUNDARY_LINE_START ||
900 : aBoundaryType == BOUNDARY_LINE_END)) {
901 : // It is the same character offset when the caret is visually at the very end of a line
902 : // or the start of a new line. Getting text at the line should provide the line with the visual caret,
903 : // otherwise screen readers will announce the wrong line as the user presses up or down arrow and land
904 : // at the end of a line.
905 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
906 0 : if (frameSelection &&
907 0 : frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
908 0 : -- aOffset; // We are at the start of a line
909 : }
910 : }
911 : }
912 0 : else if (aOffset < 0) {
913 0 : return NS_ERROR_FAILURE;
914 : }
915 :
916 : nsSelectionAmount amount;
917 0 : bool needsStart = false;
918 0 : switch (aBoundaryType) {
919 : case BOUNDARY_CHAR:
920 0 : amount = eSelectCluster;
921 0 : if (aType == eGetAt)
922 0 : aType = eGetAfter; // Avoid returning 2 characters
923 0 : break;
924 :
925 : case BOUNDARY_WORD_START:
926 0 : needsStart = true;
927 0 : amount = eSelectWord;
928 0 : break;
929 :
930 : case BOUNDARY_WORD_END:
931 0 : amount = eSelectWord;
932 0 : break;
933 :
934 : case BOUNDARY_LINE_START:
935 : // Newlines are considered at the end of a line. Since getting
936 : // the BOUNDARY_LINE_START gets the text from the line-start to the next
937 : // line-start, the newline is included at the end of the string.
938 0 : needsStart = true;
939 0 : amount = eSelectLine;
940 0 : break;
941 :
942 : case BOUNDARY_LINE_END:
943 : // Newlines are considered at the end of a line. Since getting
944 : // the BOUNDARY_END_START gets the text from the line-end to the next
945 : //line-end, the newline is included at the beginning of the string.
946 0 : amount = eSelectLine;
947 0 : break;
948 :
949 : case BOUNDARY_ATTRIBUTE_RANGE:
950 : {
951 : nsresult rv = GetTextAttributes(false, aOffset,
952 0 : aStartOffset, aEndOffset, nsnull);
953 0 : NS_ENSURE_SUCCESS(rv, rv);
954 :
955 0 : return GetText(*aStartOffset, *aEndOffset, aText);
956 : }
957 :
958 : default: // Note, sentence support is deprecated and falls through to here
959 0 : return NS_ERROR_INVALID_ARG;
960 : }
961 :
962 0 : PRInt32 startOffset = aOffset + (aBoundaryType == BOUNDARY_LINE_END); // Avoid getting the previous line
963 0 : PRInt32 endOffset = startOffset;
964 :
965 : // Convert offsets to frame-relative
966 0 : nsRefPtr<nsAccessible> startAcc;
967 : nsIFrame *startFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
968 0 : nsnull, getter_AddRefs(startAcc));
969 :
970 0 : if (!startFrame) {
971 0 : PRInt32 textLength = CharacterCount();
972 0 : if (aBoundaryType == BOUNDARY_LINE_START && aOffset > 0 && aOffset == textLength) {
973 : // Asking for start of line, while on last character
974 0 : if (startAcc)
975 0 : startFrame = startAcc->GetFrame();
976 : }
977 0 : if (!startFrame) {
978 0 : return aOffset > textLength ? NS_ERROR_FAILURE : NS_OK;
979 : }
980 : else {
981 : // We're on the last continuation since we're on the last character
982 0 : startFrame = startFrame->GetLastContinuation();
983 : }
984 : }
985 :
986 : PRInt32 finalStartOffset, finalEndOffset;
987 :
988 : // If aType == eGetAt we'll change both the start and end offset from
989 : // the original offset
990 0 : if (aType == eGetAfter) {
991 0 : finalStartOffset = aOffset;
992 : }
993 : else {
994 : finalStartOffset = GetRelativeOffset(presShell, startFrame, startOffset,
995 : startAcc, amount, eDirPrevious,
996 0 : needsStart);
997 0 : NS_ENSURE_TRUE(finalStartOffset >= 0, NS_ERROR_FAILURE);
998 : }
999 :
1000 0 : if (aType == eGetBefore) {
1001 0 : finalEndOffset = aOffset;
1002 : }
1003 : else {
1004 : // Start moving forward from the start so that we don't get
1005 : // 2 words/lines if the offset occurred on whitespace boundary
1006 : // Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed
1007 : // For BOUNDARY_LINE_END, make sure we start of this line
1008 0 : startOffset = endOffset = finalStartOffset + (aBoundaryType == BOUNDARY_LINE_END);
1009 0 : nsRefPtr<nsAccessible> endAcc;
1010 : nsIFrame *endFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
1011 0 : nsnull, getter_AddRefs(endAcc));
1012 0 : if (endAcc && endAcc->Role() == roles::STATICTEXT) {
1013 : // Static text like list bullets will ruin our forward calculation,
1014 : // since the caret cannot be in the static text. Start just after the static text.
1015 : startOffset = endOffset = finalStartOffset +
1016 : (aBoundaryType == BOUNDARY_LINE_END) +
1017 0 : nsAccUtils::TextLength(endAcc);
1018 :
1019 : endFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
1020 0 : nsnull, getter_AddRefs(endAcc));
1021 : }
1022 0 : if (!endFrame) {
1023 0 : return NS_ERROR_FAILURE;
1024 : }
1025 : finalEndOffset = GetRelativeOffset(presShell, endFrame, endOffset, endAcc,
1026 0 : amount, eDirNext, needsStart);
1027 0 : NS_ENSURE_TRUE(endOffset >= 0, NS_ERROR_FAILURE);
1028 0 : if (finalEndOffset == aOffset) {
1029 0 : if (aType == eGetAt && amount == eSelectWord) {
1030 : // Fix word error for the first character in word: PeekOffset() will return the previous word when
1031 : // aOffset points to the first character of the word, but accessibility APIs want the current word
1032 : // that the first character is in
1033 0 : return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1034 : }
1035 0 : PRInt32 textLength = CharacterCount();
1036 0 : if (finalEndOffset < textLength) {
1037 : // This happens sometimes when current character at finalStartOffset
1038 : // is an embedded object character representing another hypertext, that
1039 : // the AT really needs to dig into separately
1040 0 : ++ finalEndOffset;
1041 : }
1042 : }
1043 : }
1044 :
1045 0 : *aStartOffset = finalStartOffset;
1046 0 : *aEndOffset = finalEndOffset;
1047 :
1048 0 : NS_ASSERTION((finalStartOffset < aOffset && finalEndOffset >= aOffset) || aType != eGetBefore, "Incorrect results for GetTextHelper");
1049 0 : NS_ASSERTION((finalStartOffset <= aOffset && finalEndOffset > aOffset) || aType == eGetBefore, "Incorrect results for GetTextHelper");
1050 :
1051 0 : GetPosAndText(finalStartOffset, finalEndOffset, &aText);
1052 0 : return NS_OK;
1053 : }
1054 :
1055 : /**
1056 : * nsIAccessibleText impl.
1057 : */
1058 0 : NS_IMETHODIMP nsHyperTextAccessible::GetTextBeforeOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
1059 : PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
1060 : {
1061 0 : if (aBoundaryType == BOUNDARY_CHAR) {
1062 0 : GetCharAt(aOffset, eGetBefore, aText, aStartOffset, aEndOffset);
1063 0 : return NS_OK;
1064 : }
1065 :
1066 0 : return GetTextHelper(eGetBefore, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1067 : }
1068 :
1069 0 : NS_IMETHODIMP nsHyperTextAccessible::GetTextAtOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
1070 : PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
1071 : {
1072 0 : if (aBoundaryType == BOUNDARY_CHAR) {
1073 0 : GetCharAt(aOffset, eGetAt, aText, aStartOffset, aEndOffset);
1074 0 : return NS_OK;
1075 : }
1076 :
1077 0 : return GetTextHelper(eGetAt, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1078 : }
1079 :
1080 0 : NS_IMETHODIMP nsHyperTextAccessible::GetTextAfterOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
1081 : PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
1082 : {
1083 0 : if (aBoundaryType == BOUNDARY_CHAR) {
1084 0 : GetCharAt(aOffset, eGetAfter, aText, aStartOffset, aEndOffset);
1085 0 : return NS_OK;
1086 : }
1087 :
1088 0 : return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
1089 : }
1090 :
1091 : // nsIPersistentProperties
1092 : // nsIAccessibleText::getTextAttributes(in boolean includeDefAttrs,
1093 : // in long offset,
1094 : // out long rangeStartOffset,
1095 : // out long rangeEndOffset);
1096 : NS_IMETHODIMP
1097 0 : nsHyperTextAccessible::GetTextAttributes(bool aIncludeDefAttrs,
1098 : PRInt32 aOffset,
1099 : PRInt32 *aStartOffset,
1100 : PRInt32 *aEndOffset,
1101 : nsIPersistentProperties **aAttributes)
1102 : {
1103 : // 1. Get each attribute and its ranges one after another.
1104 : // 2. As we get each new attribute, we pass the current start and end offsets
1105 : // as in/out parameters. In other words, as attributes are collected,
1106 : // the attribute range itself can only stay the same or get smaller.
1107 :
1108 0 : NS_ENSURE_ARG_POINTER(aStartOffset);
1109 0 : *aStartOffset = 0;
1110 :
1111 0 : NS_ENSURE_ARG_POINTER(aEndOffset);
1112 0 : *aEndOffset = 0;
1113 :
1114 0 : if (IsDefunct())
1115 0 : return NS_ERROR_FAILURE;
1116 :
1117 0 : if (aAttributes) {
1118 0 : *aAttributes = nsnull;
1119 :
1120 : nsCOMPtr<nsIPersistentProperties> attributes =
1121 0 : do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
1122 0 : NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
1123 :
1124 0 : NS_ADDREF(*aAttributes = attributes);
1125 : }
1126 :
1127 0 : nsAccessible* accAtOffset = GetChildAtOffset(aOffset);
1128 0 : if (!accAtOffset) {
1129 : // Offset 0 is correct offset when accessible has empty text. Include
1130 : // default attributes if they were requested, otherwise return empty set.
1131 0 : if (aOffset == 0) {
1132 0 : if (aIncludeDefAttrs) {
1133 0 : TextAttrsMgr textAttrsMgr(this);
1134 0 : textAttrsMgr.GetAttributes(*aAttributes);
1135 : }
1136 0 : return NS_OK;
1137 : }
1138 0 : return NS_ERROR_INVALID_ARG;
1139 : }
1140 :
1141 0 : PRInt32 accAtOffsetIdx = accAtOffset->IndexInParent();
1142 0 : PRInt32 startOffset = GetChildOffset(accAtOffsetIdx);
1143 0 : PRInt32 endOffset = GetChildOffset(accAtOffsetIdx + 1);
1144 0 : PRInt32 offsetInAcc = aOffset - startOffset;
1145 :
1146 : TextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, accAtOffset,
1147 0 : accAtOffsetIdx);
1148 0 : textAttrsMgr.GetAttributes(*aAttributes, &startOffset, &endOffset);
1149 :
1150 : // Compute spelling attributes on text accessible only.
1151 0 : nsIFrame *offsetFrame = accAtOffset->GetFrame();
1152 0 : if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) {
1153 0 : PRInt32 nodeOffset = 0;
1154 : nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc,
1155 0 : &nodeOffset);
1156 0 : NS_ENSURE_SUCCESS(rv, rv);
1157 :
1158 : // Set 'misspelled' text attribute.
1159 0 : rv = GetSpellTextAttribute(accAtOffset->GetNode(), nodeOffset,
1160 : &startOffset, &endOffset,
1161 0 : aAttributes ? *aAttributes : nsnull);
1162 0 : NS_ENSURE_SUCCESS(rv, rv);
1163 : }
1164 :
1165 0 : *aStartOffset = startOffset;
1166 0 : *aEndOffset = endOffset;
1167 0 : return NS_OK;
1168 : }
1169 :
1170 : // nsIPersistentProperties
1171 : // nsIAccessibleText::defaultTextAttributes
1172 : NS_IMETHODIMP
1173 0 : nsHyperTextAccessible::GetDefaultTextAttributes(nsIPersistentProperties **aAttributes)
1174 : {
1175 0 : NS_ENSURE_ARG_POINTER(aAttributes);
1176 0 : *aAttributes = nsnull;
1177 :
1178 0 : if (IsDefunct())
1179 0 : return NS_ERROR_FAILURE;
1180 :
1181 : nsCOMPtr<nsIPersistentProperties> attributes =
1182 0 : do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
1183 0 : NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
1184 :
1185 0 : NS_ADDREF(*aAttributes = attributes);
1186 :
1187 0 : TextAttrsMgr textAttrsMgr(this);
1188 0 : textAttrsMgr.GetAttributes(*aAttributes);
1189 0 : return NS_OK;
1190 : }
1191 :
1192 : PRInt32
1193 0 : nsHyperTextAccessible::GetLevelInternal()
1194 : {
1195 0 : nsIAtom *tag = mContent->Tag();
1196 0 : if (tag == nsGkAtoms::h1)
1197 0 : return 1;
1198 0 : if (tag == nsGkAtoms::h2)
1199 0 : return 2;
1200 0 : if (tag == nsGkAtoms::h3)
1201 0 : return 3;
1202 0 : if (tag == nsGkAtoms::h4)
1203 0 : return 4;
1204 0 : if (tag == nsGkAtoms::h5)
1205 0 : return 5;
1206 0 : if (tag == nsGkAtoms::h6)
1207 0 : return 6;
1208 :
1209 0 : return nsAccessibleWrap::GetLevelInternal();
1210 : }
1211 :
1212 : nsresult
1213 0 : nsHyperTextAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
1214 : {
1215 0 : nsresult rv = nsAccessibleWrap::GetAttributesInternal(aAttributes);
1216 0 : NS_ENSURE_SUCCESS(rv, rv);
1217 :
1218 : // Indicate when the current object uses block-level formatting
1219 : // via formatting: block
1220 : // XXX: 'formatting' attribute is deprecated and will be removed in Mozilla2,
1221 : // use 'display' attribute instead.
1222 0 : nsIFrame *frame = GetFrame();
1223 0 : if (frame && frame->GetType() == nsGkAtoms::blockFrame) {
1224 0 : nsAutoString oldValueUnused;
1225 0 : aAttributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"), NS_LITERAL_STRING("block"),
1226 0 : oldValueUnused);
1227 : }
1228 :
1229 0 : if (FocusMgr()->IsFocused(this)) {
1230 0 : PRInt32 lineNumber = GetCaretLineNumber();
1231 0 : if (lineNumber >= 1) {
1232 0 : nsAutoString strLineNumber;
1233 0 : strLineNumber.AppendInt(lineNumber);
1234 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::lineNumber,
1235 0 : strLineNumber);
1236 : }
1237 : }
1238 :
1239 : // For the html landmark elements we expose them like we do aria landmarks to
1240 : // make AT navigation schemes "just work". Note html:header is redundant as
1241 : // a landmark since it usually contains headings. We're not yet sure how the
1242 : // web will use html:footer but our best bet right now is as contentinfo.
1243 0 : if (mContent->Tag() == nsGkAtoms::nav)
1244 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1245 0 : NS_LITERAL_STRING("navigation"));
1246 0 : else if (mContent->Tag() == nsGkAtoms::footer)
1247 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1248 0 : NS_LITERAL_STRING("contentinfo"));
1249 0 : else if (mContent->Tag() == nsGkAtoms::aside)
1250 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
1251 0 : NS_LITERAL_STRING("complementary"));
1252 :
1253 0 : return NS_OK;
1254 : }
1255 :
1256 : /*
1257 : * Given an offset, the x, y, width, and height values are filled appropriately.
1258 : */
1259 0 : NS_IMETHODIMP nsHyperTextAccessible::GetCharacterExtents(PRInt32 aOffset, PRInt32 *aX, PRInt32 *aY,
1260 : PRInt32 *aWidth, PRInt32 *aHeight,
1261 : PRUint32 aCoordType)
1262 : {
1263 0 : return GetRangeExtents(aOffset, aOffset + 1, aX, aY, aWidth, aHeight, aCoordType);
1264 : }
1265 :
1266 : /*
1267 : * Given a start & end offset, the x, y, width, and height values are filled appropriately.
1268 : */
1269 0 : NS_IMETHODIMP nsHyperTextAccessible::GetRangeExtents(PRInt32 aStartOffset, PRInt32 aEndOffset,
1270 : PRInt32 *aX, PRInt32 *aY,
1271 : PRInt32 *aWidth, PRInt32 *aHeight,
1272 : PRUint32 aCoordType)
1273 : {
1274 0 : nsIntRect boundsRect;
1275 : nsIFrame *endFrameUnused;
1276 0 : if (!GetPosAndText(aStartOffset, aEndOffset, nsnull, &endFrameUnused, &boundsRect) ||
1277 0 : boundsRect.IsEmpty()) {
1278 0 : return NS_ERROR_FAILURE;
1279 : }
1280 :
1281 0 : *aX = boundsRect.x;
1282 0 : *aY = boundsRect.y;
1283 0 : *aWidth = boundsRect.width;
1284 0 : *aHeight = boundsRect.height;
1285 :
1286 0 : return nsAccUtils::ConvertScreenCoordsTo(aX, aY, aCoordType, this);
1287 : }
1288 :
1289 : /*
1290 : * Gets the offset of the character located at coordinates x and y. x and y are interpreted as being relative to
1291 : * the screen or this widget's window depending on coords.
1292 : */
1293 : NS_IMETHODIMP
1294 0 : nsHyperTextAccessible::GetOffsetAtPoint(PRInt32 aX, PRInt32 aY,
1295 : PRUint32 aCoordType, PRInt32 *aOffset)
1296 : {
1297 0 : *aOffset = -1;
1298 0 : if (!mDoc)
1299 0 : return NS_ERROR_FAILURE;
1300 :
1301 0 : nsIPresShell* shell = mDoc->PresShell();
1302 0 : if (!shell) {
1303 0 : return NS_ERROR_FAILURE;
1304 : }
1305 0 : nsIFrame *hyperFrame = GetFrame();
1306 0 : if (!hyperFrame) {
1307 0 : return NS_ERROR_FAILURE;
1308 : }
1309 0 : nsIntRect frameScreenRect = hyperFrame->GetScreenRectExternal();
1310 :
1311 0 : nsIntPoint coords;
1312 : nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType,
1313 0 : this, &coords);
1314 0 : NS_ENSURE_SUCCESS(rv, rv);
1315 :
1316 : // coords are currently screen coordinates, and we need to turn them into
1317 : // frame coordinates relative to the current accessible
1318 0 : if (!frameScreenRect.Contains(coords.x, coords.y)) {
1319 0 : return NS_OK; // Not found, will return -1
1320 : }
1321 : nsIntPoint pxInHyperText(coords.x - frameScreenRect.x,
1322 0 : coords.y - frameScreenRect.y);
1323 0 : nsPresContext *context = GetPresContext();
1324 0 : NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1325 : nsPoint pointInHyperText(context->DevPixelsToAppUnits(pxInHyperText.x),
1326 0 : context->DevPixelsToAppUnits(pxInHyperText.y));
1327 :
1328 : // Go through the frames to check if each one has the point.
1329 : // When one does, add up the character offsets until we have a match
1330 :
1331 : // We have an point in an accessible child of this, now we need to add up the
1332 : // offsets before it to what we already have
1333 0 : PRInt32 offset = 0;
1334 0 : PRInt32 childCount = GetChildCount();
1335 0 : for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
1336 0 : nsAccessible *childAcc = mChildren[childIdx];
1337 :
1338 0 : nsIFrame *primaryFrame = childAcc->GetFrame();
1339 0 : NS_ENSURE_TRUE(primaryFrame, NS_ERROR_FAILURE);
1340 :
1341 0 : nsIFrame *frame = primaryFrame;
1342 0 : while (frame) {
1343 0 : nsIContent *content = frame->GetContent();
1344 0 : NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
1345 0 : nsPoint pointInFrame = pointInHyperText - frame->GetOffsetToExternal(hyperFrame);
1346 0 : nsSize frameSize = frame->GetSize();
1347 0 : if (pointInFrame.x < frameSize.width && pointInFrame.y < frameSize.height) {
1348 : // Finished
1349 0 : if (frame->GetType() == nsGkAtoms::textFrame) {
1350 0 : nsIFrame::ContentOffsets contentOffsets = frame->GetContentOffsetsFromPointExternal(pointInFrame, true);
1351 0 : if (contentOffsets.IsNull() || contentOffsets.content != content) {
1352 0 : return NS_OK; // Not found, will return -1
1353 : }
1354 : PRUint32 addToOffset;
1355 : nsresult rv = ContentToRenderedOffset(primaryFrame,
1356 : contentOffsets.offset,
1357 0 : &addToOffset);
1358 0 : NS_ENSURE_SUCCESS(rv, rv);
1359 0 : offset += addToOffset;
1360 : }
1361 0 : *aOffset = offset;
1362 0 : return NS_OK;
1363 : }
1364 0 : frame = frame->GetNextContinuation();
1365 : }
1366 :
1367 0 : offset += nsAccUtils::TextLength(childAcc);
1368 : }
1369 :
1370 0 : return NS_OK; // Not found, will return -1
1371 : }
1372 :
1373 :
1374 : ////////////////////////////////////////////////////////////////////////////////
1375 : // nsIAccessibleHyperText
1376 :
1377 : NS_IMETHODIMP
1378 0 : nsHyperTextAccessible::GetLinkCount(PRInt32 *aLinkCount)
1379 : {
1380 0 : NS_ENSURE_ARG_POINTER(aLinkCount);
1381 0 : *aLinkCount = 0;
1382 :
1383 0 : if (IsDefunct())
1384 0 : return NS_ERROR_FAILURE;
1385 :
1386 0 : *aLinkCount = GetLinkCount();
1387 0 : return NS_OK;
1388 : }
1389 :
1390 : NS_IMETHODIMP
1391 0 : nsHyperTextAccessible::GetLinkAt(PRInt32 aIndex, nsIAccessibleHyperLink** aLink)
1392 : {
1393 0 : NS_ENSURE_ARG_POINTER(aLink);
1394 0 : *aLink = nsnull;
1395 :
1396 0 : if (IsDefunct())
1397 0 : return NS_ERROR_FAILURE;
1398 :
1399 0 : nsAccessible* link = GetLinkAt(aIndex);
1400 0 : if (link)
1401 0 : CallQueryInterface(link, aLink);
1402 :
1403 0 : return NS_OK;
1404 : }
1405 :
1406 : NS_IMETHODIMP
1407 0 : nsHyperTextAccessible::GetLinkIndex(nsIAccessibleHyperLink* aLink,
1408 : PRInt32* aIndex)
1409 : {
1410 0 : NS_ENSURE_ARG_POINTER(aLink);
1411 :
1412 0 : if (IsDefunct())
1413 0 : return NS_ERROR_FAILURE;
1414 :
1415 0 : nsRefPtr<nsAccessible> link(do_QueryObject(aLink));
1416 0 : *aIndex = GetLinkIndex(link);
1417 0 : return NS_OK;
1418 : }
1419 :
1420 : NS_IMETHODIMP
1421 0 : nsHyperTextAccessible::GetLinkIndexAtOffset(PRInt32 aOffset,
1422 : PRInt32* aLinkIndex)
1423 : {
1424 0 : NS_ENSURE_ARG_POINTER(aLinkIndex);
1425 0 : *aLinkIndex = -1; // API says this magic value means 'not found'
1426 :
1427 0 : if (IsDefunct())
1428 0 : return NS_ERROR_FAILURE;
1429 :
1430 0 : *aLinkIndex = GetLinkIndexAtOffset(aOffset);
1431 0 : return NS_OK;
1432 : }
1433 :
1434 : /**
1435 : * nsIAccessibleEditableText impl.
1436 : */
1437 0 : NS_IMETHODIMP nsHyperTextAccessible::SetAttributes(PRInt32 aStartPos, PRInt32 aEndPos,
1438 : nsISupports *aAttributes)
1439 : {
1440 0 : return NS_ERROR_NOT_IMPLEMENTED;
1441 : }
1442 :
1443 0 : NS_IMETHODIMP nsHyperTextAccessible::SetTextContents(const nsAString &aText)
1444 : {
1445 0 : PRInt32 numChars = CharacterCount();
1446 0 : if (numChars == 0 || NS_SUCCEEDED(DeleteText(0, numChars))) {
1447 0 : return InsertText(aText, 0);
1448 : }
1449 0 : return NS_ERROR_FAILURE;
1450 : }
1451 :
1452 : NS_IMETHODIMP
1453 0 : nsHyperTextAccessible::InsertText(const nsAString &aText, PRInt32 aPosition)
1454 : {
1455 0 : if (IsDefunct())
1456 0 : return NS_ERROR_FAILURE;
1457 :
1458 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1459 :
1460 0 : nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(editor));
1461 0 : NS_ENSURE_STATE(peditor);
1462 :
1463 0 : nsresult rv = SetSelectionRange(aPosition, aPosition);
1464 0 : NS_ENSURE_SUCCESS(rv, rv);
1465 :
1466 0 : return peditor->InsertText(aText);
1467 : }
1468 :
1469 : NS_IMETHODIMP
1470 0 : nsHyperTextAccessible::CopyText(PRInt32 aStartPos, PRInt32 aEndPos)
1471 : {
1472 0 : if (IsDefunct())
1473 0 : return NS_ERROR_FAILURE;
1474 :
1475 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1476 0 : NS_ENSURE_STATE(editor);
1477 :
1478 0 : nsresult rv = SetSelectionRange(aStartPos, aEndPos);
1479 0 : NS_ENSURE_SUCCESS(rv, rv);
1480 :
1481 0 : return editor->Copy();
1482 : }
1483 :
1484 : NS_IMETHODIMP
1485 0 : nsHyperTextAccessible::CutText(PRInt32 aStartPos, PRInt32 aEndPos)
1486 : {
1487 0 : if (IsDefunct())
1488 0 : return NS_ERROR_FAILURE;
1489 :
1490 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1491 0 : NS_ENSURE_STATE(editor);
1492 :
1493 0 : nsresult rv = SetSelectionRange(aStartPos, aEndPos);
1494 0 : NS_ENSURE_SUCCESS(rv, rv);
1495 :
1496 0 : return editor->Cut();
1497 : }
1498 :
1499 : NS_IMETHODIMP
1500 0 : nsHyperTextAccessible::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos)
1501 : {
1502 0 : if (IsDefunct())
1503 0 : return NS_ERROR_FAILURE;
1504 :
1505 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1506 0 : NS_ENSURE_STATE(editor);
1507 :
1508 0 : nsresult rv = SetSelectionRange(aStartPos, aEndPos);
1509 0 : NS_ENSURE_SUCCESS(rv, rv);
1510 :
1511 0 : return editor->DeleteSelection(nsIEditor::eNone);
1512 : }
1513 :
1514 : NS_IMETHODIMP
1515 0 : nsHyperTextAccessible::PasteText(PRInt32 aPosition)
1516 : {
1517 0 : if (IsDefunct())
1518 0 : return NS_ERROR_FAILURE;
1519 :
1520 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1521 0 : NS_ENSURE_STATE(editor);
1522 :
1523 0 : nsresult rv = SetSelectionRange(aPosition, aPosition);
1524 0 : NS_ENSURE_SUCCESS(rv, rv);
1525 :
1526 0 : return editor->Paste(nsIClipboard::kGlobalClipboard);
1527 : }
1528 :
1529 : already_AddRefed<nsIEditor>
1530 0 : nsHyperTextAccessible::GetEditor() const
1531 : {
1532 0 : if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
1533 : // If we're inside an editable container, then return that container's editor
1534 0 : nsAccessible* ancestor = Parent();
1535 0 : while (ancestor) {
1536 0 : nsHyperTextAccessible* hyperText = ancestor->AsHyperText();
1537 0 : if (hyperText) {
1538 : // Recursion will stop at container doc because it has its own impl
1539 : // of GetEditor()
1540 0 : return hyperText->GetEditor();
1541 : }
1542 :
1543 0 : ancestor = ancestor->Parent();
1544 : }
1545 :
1546 0 : return nsnull;
1547 : }
1548 :
1549 : nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
1550 0 : nsCoreUtils::GetDocShellTreeItemFor(mContent);
1551 0 : nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(docShellTreeItem));
1552 0 : if (!editingSession)
1553 0 : return nsnull; // No editing session interface
1554 :
1555 0 : nsCOMPtr<nsIEditor> editor;
1556 0 : nsIDocument* docNode = mDoc->GetDocumentNode();
1557 0 : editingSession->GetEditorForWindow(docNode->GetWindow(),
1558 0 : getter_AddRefs(editor));
1559 0 : return editor.forget();
1560 : }
1561 :
1562 : /**
1563 : * =================== Caret & Selection ======================
1564 : */
1565 :
1566 : nsresult
1567 0 : nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
1568 : {
1569 0 : bool isFocusable = State() & states::FOCUSABLE;
1570 :
1571 : // If accessible is focusable then focus it before setting the selection to
1572 : // neglect control's selection changes on focus if any (for example, inputs
1573 : // that do select all on focus).
1574 : // some input controls
1575 0 : if (isFocusable)
1576 0 : TakeFocus();
1577 :
1578 : // Set the selection
1579 0 : SetSelectionBounds(0, aStartPos, aEndPos);
1580 :
1581 : // If range 0 was successfully set, clear any additional selection
1582 : // ranges remaining from previous selection
1583 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
1584 0 : NS_ENSURE_STATE(frameSelection);
1585 :
1586 : nsCOMPtr<nsISelection> domSel =
1587 0 : frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
1588 0 : NS_ENSURE_STATE(domSel);
1589 :
1590 0 : PRInt32 numRanges = 0;
1591 0 : domSel->GetRangeCount(&numRanges);
1592 :
1593 0 : for (PRInt32 count = 0; count < numRanges - 1; count ++) {
1594 0 : nsCOMPtr<nsIDOMRange> range;
1595 0 : domSel->GetRangeAt(1, getter_AddRefs(range));
1596 0 : domSel->RemoveRange(range);
1597 : }
1598 :
1599 : // When selection is done, move the focus to the selection if accessible is
1600 : // not focusable. That happens when selection is set within hypertext
1601 : // accessible.
1602 0 : if (isFocusable)
1603 0 : return NS_OK;
1604 :
1605 0 : nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
1606 0 : if (DOMFocusManager) {
1607 0 : NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE);
1608 0 : nsIDocument* docNode = mDoc->GetDocumentNode();
1609 0 : NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE);
1610 0 : nsCOMPtr<nsPIDOMWindow> window = docNode->GetWindow();
1611 0 : nsCOMPtr<nsIDOMElement> result;
1612 : DOMFocusManager->MoveFocus(window, nsnull, nsIFocusManager::MOVEFOCUS_CARET,
1613 0 : nsIFocusManager::FLAG_BYMOVEFOCUS, getter_AddRefs(result));
1614 : }
1615 :
1616 0 : return NS_OK;
1617 : }
1618 :
1619 : NS_IMETHODIMP
1620 0 : nsHyperTextAccessible::SetCaretOffset(PRInt32 aCaretOffset)
1621 : {
1622 0 : return SetSelectionRange(aCaretOffset, aCaretOffset);
1623 : }
1624 :
1625 : /*
1626 : * Gets the offset position of the caret (cursor).
1627 : */
1628 : NS_IMETHODIMP
1629 0 : nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
1630 : {
1631 0 : NS_ENSURE_ARG_POINTER(aCaretOffset);
1632 0 : *aCaretOffset = -1;
1633 :
1634 0 : if (IsDefunct())
1635 0 : return NS_ERROR_FAILURE;
1636 :
1637 : // Not focused focusable accessible except document accessible doesn't have
1638 : // a caret.
1639 0 : if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
1640 0 : (State() & states::FOCUSABLE)) {
1641 0 : return NS_OK;
1642 : }
1643 :
1644 : // No caret if the focused node is not inside this DOM node and this DOM node
1645 : // is not inside of focused node.
1646 : FocusManager::FocusDisposition focusDisp =
1647 0 : FocusMgr()->IsInOrContainsFocus(this);
1648 0 : if (focusDisp == FocusManager::eNone)
1649 0 : return NS_OK;
1650 :
1651 : // Turn the focus node and offset of the selection into caret hypretext
1652 : // offset.
1653 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
1654 0 : NS_ENSURE_STATE(frameSelection);
1655 :
1656 : nsISelection* domSel =
1657 0 : frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
1658 0 : NS_ENSURE_STATE(domSel);
1659 :
1660 0 : nsCOMPtr<nsIDOMNode> focusDOMNode;
1661 0 : nsresult rv = domSel->GetFocusNode(getter_AddRefs(focusDOMNode));
1662 0 : NS_ENSURE_SUCCESS(rv, rv);
1663 :
1664 : PRInt32 focusOffset;
1665 0 : rv = domSel->GetFocusOffset(&focusOffset);
1666 0 : NS_ENSURE_SUCCESS(rv, rv);
1667 :
1668 : // No caret if this DOM node is inside of focused node but the selection's
1669 : // focus point is not inside of this DOM node.
1670 0 : nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
1671 0 : if (focusDisp == FocusManager::eContainedByFocus) {
1672 : nsINode *resultNode =
1673 0 : nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
1674 :
1675 0 : nsINode* thisNode = GetNode();
1676 0 : if (resultNode != thisNode &&
1677 0 : !nsCoreUtils::IsAncestorOf(thisNode, resultNode))
1678 0 : return NS_OK;
1679 : }
1680 :
1681 0 : DOMPointToHypertextOffset(focusNode, focusOffset, aCaretOffset);
1682 0 : return NS_OK;
1683 : }
1684 :
1685 : PRInt32
1686 0 : nsHyperTextAccessible::GetCaretLineNumber()
1687 : {
1688 : // Provide the line number for the caret, relative to the
1689 : // currently focused node. Use a 1-based index
1690 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
1691 0 : if (!frameSelection)
1692 0 : return -1;
1693 :
1694 : nsISelection* domSel =
1695 0 : frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
1696 0 : if (!domSel)
1697 0 : return - 1;
1698 :
1699 0 : nsCOMPtr<nsIDOMNode> caretNode;
1700 0 : domSel->GetFocusNode(getter_AddRefs(caretNode));
1701 0 : nsCOMPtr<nsIContent> caretContent = do_QueryInterface(caretNode);
1702 0 : if (!caretContent || !nsCoreUtils::IsAncestorOf(GetNode(), caretContent))
1703 0 : return -1;
1704 :
1705 : PRInt32 caretOffset, returnOffsetUnused;
1706 0 : domSel->GetFocusOffset(&caretOffset);
1707 0 : nsFrameSelection::HINT hint = frameSelection->GetHint();
1708 0 : nsIFrame *caretFrame = frameSelection->GetFrameForNodeOffset(caretContent, caretOffset,
1709 0 : hint, &returnOffsetUnused);
1710 0 : NS_ENSURE_TRUE(caretFrame, -1);
1711 :
1712 0 : PRInt32 lineNumber = 1;
1713 0 : nsAutoLineIterator lineIterForCaret;
1714 0 : nsIContent *hyperTextContent = IsContent() ? mContent.get() : nsnull;
1715 0 : while (caretFrame) {
1716 0 : if (hyperTextContent == caretFrame->GetContent()) {
1717 0 : return lineNumber; // Must be in a single line hyper text, there is no line iterator
1718 : }
1719 0 : nsIFrame *parentFrame = caretFrame->GetParent();
1720 0 : if (!parentFrame)
1721 0 : break;
1722 :
1723 : // Add lines for the sibling frames before the caret
1724 0 : nsIFrame *sibling = parentFrame->GetFirstPrincipalChild();
1725 0 : while (sibling && sibling != caretFrame) {
1726 0 : nsAutoLineIterator lineIterForSibling = sibling->GetLineIterator();
1727 0 : if (lineIterForSibling) {
1728 : // For the frames before that grab all the lines
1729 0 : PRInt32 addLines = lineIterForSibling->GetNumLines();
1730 0 : lineNumber += addLines;
1731 : }
1732 0 : sibling = sibling->GetNextSibling();
1733 : }
1734 :
1735 : // Get the line number relative to the container with lines
1736 0 : if (!lineIterForCaret) { // Add the caret line just once
1737 0 : lineIterForCaret = parentFrame->GetLineIterator();
1738 0 : if (lineIterForCaret) {
1739 : // Ancestor of caret
1740 0 : PRInt32 addLines = lineIterForCaret->FindLineContaining(caretFrame);
1741 0 : lineNumber += addLines;
1742 : }
1743 : }
1744 :
1745 0 : caretFrame = parentFrame;
1746 : }
1747 :
1748 0 : NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
1749 0 : return lineNumber;
1750 : }
1751 :
1752 : already_AddRefed<nsFrameSelection>
1753 0 : nsHyperTextAccessible::FrameSelection()
1754 : {
1755 0 : nsIFrame* frame = GetFrame();
1756 0 : return frame ? frame->GetFrameSelection() : nsnull;
1757 : }
1758 :
1759 : void
1760 0 : nsHyperTextAccessible::GetSelectionDOMRanges(PRInt16 aType,
1761 : nsTArray<nsRange*>* aRanges)
1762 : {
1763 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
1764 0 : if (!frameSelection)
1765 : return;
1766 :
1767 0 : nsISelection* domSel = frameSelection->GetSelection(aType);
1768 0 : if (!domSel)
1769 : return;
1770 :
1771 0 : nsCOMPtr<nsINode> startNode = GetNode();
1772 :
1773 0 : nsCOMPtr<nsIEditor> editor = GetEditor();
1774 0 : if (editor) {
1775 0 : nsCOMPtr<nsIDOMElement> editorRoot;
1776 0 : editor->GetRootElement(getter_AddRefs(editorRoot));
1777 0 : startNode = do_QueryInterface(editorRoot);
1778 : }
1779 :
1780 0 : if (!startNode)
1781 : return;
1782 :
1783 0 : PRUint32 childCount = startNode->GetChildCount();
1784 0 : nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(domSel));
1785 0 : nsresult rv = privSel->
1786 0 : GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
1787 0 : NS_ENSURE_SUCCESS(rv,);
1788 :
1789 : // Remove collapsed ranges
1790 0 : PRUint32 numRanges = aRanges->Length();
1791 0 : for (PRUint32 idx = 0; idx < numRanges; idx ++) {
1792 0 : if ((*aRanges)[idx]->Collapsed()) {
1793 0 : aRanges->RemoveElementAt(idx);
1794 0 : --numRanges;
1795 0 : --idx;
1796 : }
1797 : }
1798 : }
1799 :
1800 : /*
1801 : * Gets the number of selected regions.
1802 : */
1803 : NS_IMETHODIMP
1804 0 : nsHyperTextAccessible::GetSelectionCount(PRInt32* aSelectionCount)
1805 : {
1806 0 : NS_ENSURE_ARG_POINTER(aSelectionCount);
1807 0 : *aSelectionCount = 0;
1808 :
1809 0 : nsTArray<nsRange*> ranges;
1810 0 : GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
1811 0 : *aSelectionCount = PRInt32(ranges.Length());
1812 :
1813 0 : return NS_OK;
1814 : }
1815 :
1816 : /*
1817 : * Gets the start and end offset of the specified selection.
1818 : */
1819 : NS_IMETHODIMP
1820 0 : nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum,
1821 : PRInt32* aStartOffset,
1822 : PRInt32* aEndOffset)
1823 : {
1824 0 : NS_ENSURE_ARG_POINTER(aStartOffset);
1825 0 : NS_ENSURE_ARG_POINTER(aEndOffset);
1826 0 : *aStartOffset = *aEndOffset = 0;
1827 :
1828 0 : nsTArray<nsRange*> ranges;
1829 0 : GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
1830 :
1831 0 : PRUint32 rangeCount = ranges.Length();
1832 0 : if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
1833 0 : return NS_ERROR_INVALID_ARG;
1834 :
1835 0 : nsRange* range = ranges[aSelectionNum];
1836 :
1837 : // Get start and end points.
1838 0 : nsINode* startNode = range->GetStartParent();
1839 0 : nsINode* endNode = range->GetEndParent();
1840 0 : PRInt32 startOffset = range->StartOffset(), endOffset = range->EndOffset();
1841 :
1842 : // Make sure start is before end, by swapping DOM points. This occurs when
1843 : // the user selects backwards in the text.
1844 : PRInt32 rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
1845 0 : startNode, startOffset);
1846 0 : if (rangeCompare < 0) {
1847 0 : nsINode* tempNode = startNode;
1848 0 : startNode = endNode;
1849 0 : endNode = tempNode;
1850 0 : PRInt32 tempOffset = startOffset;
1851 0 : startOffset = endOffset;
1852 0 : endOffset = tempOffset;
1853 : }
1854 :
1855 : nsAccessible *startAccessible =
1856 0 : DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
1857 0 : if (!startAccessible) {
1858 0 : *aStartOffset = 0; // Could not find start point within this hypertext, so starts before
1859 : }
1860 :
1861 0 : DOMPointToHypertextOffset(endNode, endOffset, aEndOffset, true);
1862 0 : return NS_OK;
1863 : }
1864 :
1865 : /*
1866 : * Changes the start and end offset of the specified selection.
1867 : */
1868 : NS_IMETHODIMP
1869 0 : nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
1870 : PRInt32 aStartOffset,
1871 : PRInt32 aEndOffset)
1872 : {
1873 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
1874 0 : NS_ENSURE_STATE(frameSelection);
1875 :
1876 : nsCOMPtr<nsISelection> domSel =
1877 0 : frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
1878 0 : NS_ENSURE_STATE(domSel);
1879 :
1880 : // Caret is a collapsed selection
1881 0 : bool isOnlyCaret = (aStartOffset == aEndOffset);
1882 :
1883 0 : PRInt32 rangeCount = 0;
1884 0 : domSel->GetRangeCount(&rangeCount);
1885 0 : nsCOMPtr<nsIDOMRange> range;
1886 0 : if (aSelectionNum == rangeCount) { // Add a range
1887 0 : range = new nsRange();
1888 : }
1889 0 : else if (aSelectionNum < 0 || aSelectionNum > rangeCount) {
1890 0 : return NS_ERROR_INVALID_ARG;
1891 : }
1892 : else {
1893 0 : domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
1894 0 : NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
1895 : }
1896 :
1897 0 : PRInt32 startOffset = 0, endOffset = 0;
1898 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
1899 :
1900 : nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset,
1901 0 : getter_AddRefs(startNode), &startOffset,
1902 0 : getter_AddRefs(endNode), &endOffset);
1903 0 : NS_ENSURE_SUCCESS(rv, rv);
1904 :
1905 0 : rv = range->SetStart(startNode, startOffset);
1906 0 : NS_ENSURE_SUCCESS(rv, rv);
1907 :
1908 0 : rv = isOnlyCaret ? range->Collapse(true) :
1909 0 : range->SetEnd(endNode, endOffset);
1910 0 : NS_ENSURE_SUCCESS(rv, rv);
1911 :
1912 : // If new range was created then add it, otherwise notify selection listeners
1913 : // that existing selection range was changed.
1914 0 : if (aSelectionNum == rangeCount)
1915 0 : return domSel->AddRange(range);
1916 :
1917 0 : domSel->RemoveRange(range);
1918 0 : domSel->AddRange(range);
1919 0 : return NS_OK;
1920 : }
1921 :
1922 : /*
1923 : * Adds a selection bounded by the specified offsets.
1924 : */
1925 : NS_IMETHODIMP
1926 0 : nsHyperTextAccessible::AddSelection(PRInt32 aStartOffset, PRInt32 aEndOffset)
1927 : {
1928 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
1929 0 : NS_ENSURE_STATE(frameSelection);
1930 :
1931 : nsCOMPtr<nsISelection> domSel =
1932 0 : frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
1933 0 : NS_ENSURE_STATE(domSel);
1934 :
1935 0 : PRInt32 rangeCount = 0;
1936 0 : domSel->GetRangeCount(&rangeCount);
1937 :
1938 0 : return SetSelectionBounds(rangeCount, aStartOffset, aEndOffset);
1939 : }
1940 :
1941 : /*
1942 : * Removes the specified selection.
1943 : */
1944 : NS_IMETHODIMP
1945 0 : nsHyperTextAccessible::RemoveSelection(PRInt32 aSelectionNum)
1946 : {
1947 0 : nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
1948 0 : NS_ENSURE_STATE(frameSelection);
1949 :
1950 : nsCOMPtr<nsISelection> domSel =
1951 0 : frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
1952 0 : NS_ENSURE_STATE(domSel);
1953 :
1954 : PRInt32 rangeCount;
1955 0 : domSel->GetRangeCount(&rangeCount);
1956 0 : if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
1957 0 : return NS_ERROR_INVALID_ARG;
1958 :
1959 0 : nsCOMPtr<nsIDOMRange> range;
1960 0 : domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
1961 0 : return domSel->RemoveRange(range);
1962 : }
1963 :
1964 : // void nsIAccessibleText::
1965 : // scrollSubstringTo(in long startIndex, in long endIndex,
1966 : // in unsigned long scrollType);
1967 : NS_IMETHODIMP
1968 0 : nsHyperTextAccessible::ScrollSubstringTo(PRInt32 aStartIndex, PRInt32 aEndIndex,
1969 : PRUint32 aScrollType)
1970 : {
1971 : PRInt32 startOffset, endOffset;
1972 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
1973 :
1974 : nsresult rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex,
1975 0 : getter_AddRefs(startNode),
1976 : &startOffset,
1977 0 : getter_AddRefs(endNode),
1978 0 : &endOffset);
1979 0 : NS_ENSURE_SUCCESS(rv, rv);
1980 :
1981 0 : return nsCoreUtils::ScrollSubstringTo(GetFrame(), startNode, startOffset,
1982 0 : endNode, endOffset, aScrollType);
1983 : }
1984 :
1985 : // void nsIAccessibleText::
1986 : // scrollSubstringToPoint(in long startIndex, in long endIndex,
1987 : // in unsigned long coordinateType,
1988 : // in long x, in long y);
1989 : NS_IMETHODIMP
1990 0 : nsHyperTextAccessible::ScrollSubstringToPoint(PRInt32 aStartIndex,
1991 : PRInt32 aEndIndex,
1992 : PRUint32 aCoordinateType,
1993 : PRInt32 aX, PRInt32 aY)
1994 : {
1995 0 : nsIFrame *frame = GetFrame();
1996 0 : if (!frame)
1997 0 : return NS_ERROR_FAILURE;
1998 :
1999 0 : nsIntPoint coords;
2000 : nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
2001 0 : this, &coords);
2002 0 : NS_ENSURE_SUCCESS(rv, rv);
2003 :
2004 : PRInt32 startOffset, endOffset;
2005 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
2006 :
2007 : rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex,
2008 0 : getter_AddRefs(startNode), &startOffset,
2009 0 : getter_AddRefs(endNode), &endOffset);
2010 0 : NS_ENSURE_SUCCESS(rv, rv);
2011 :
2012 0 : nsPresContext *presContext = frame->PresContext();
2013 :
2014 0 : bool initialScrolled = false;
2015 0 : nsIFrame *parentFrame = frame;
2016 0 : while ((parentFrame = parentFrame->GetParent())) {
2017 0 : nsIScrollableFrame *scrollableFrame = do_QueryFrame(parentFrame);
2018 0 : if (scrollableFrame) {
2019 0 : if (!initialScrolled) {
2020 : // Scroll substring to the given point. Turn the point into percents
2021 : // relative scrollable area to use nsCoreUtils::ScrollSubstringTo.
2022 0 : nsIntRect frameRect = parentFrame->GetScreenRectExternal();
2023 0 : PRInt32 devOffsetX = coords.x - frameRect.x;
2024 0 : PRInt32 devOffsetY = coords.y - frameRect.y;
2025 :
2026 : nsPoint offsetPoint(presContext->DevPixelsToAppUnits(devOffsetX),
2027 0 : presContext->DevPixelsToAppUnits(devOffsetY));
2028 :
2029 0 : nsSize size(parentFrame->GetSize());
2030 :
2031 : // avoid divide by zero
2032 0 : size.width = size.width ? size.width : 1;
2033 0 : size.height = size.height ? size.height : 1;
2034 :
2035 0 : PRInt16 hPercent = offsetPoint.x * 100 / size.width;
2036 0 : PRInt16 vPercent = offsetPoint.y * 100 / size.height;
2037 :
2038 0 : rv = nsCoreUtils::ScrollSubstringTo(GetFrame(), startNode, startOffset,
2039 : endNode, endOffset,
2040 0 : vPercent, hPercent);
2041 0 : NS_ENSURE_SUCCESS(rv, rv);
2042 :
2043 0 : initialScrolled = true;
2044 : } else {
2045 : // Substring was scrolled to the given point already inside its closest
2046 : // scrollable area. If there are nested scrollable areas then make
2047 : // sure we scroll lower areas to the given point inside currently
2048 : // traversed scrollable area.
2049 0 : nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
2050 : }
2051 : }
2052 0 : frame = parentFrame;
2053 : }
2054 :
2055 0 : return NS_OK;
2056 : }
2057 :
2058 : ////////////////////////////////////////////////////////////////////////////////
2059 : // nsAccessible public
2060 :
2061 : nsresult
2062 0 : nsHyperTextAccessible::GetNameInternal(nsAString& aName)
2063 : {
2064 0 : nsresult rv = nsAccessibleWrap::GetNameInternal(aName);
2065 0 : NS_ENSURE_SUCCESS(rv, rv);
2066 :
2067 : // Get name from title attribute for HTML abbr and acronym elements making it
2068 : // a valid name from markup. Otherwise their name isn't picked up by recursive
2069 : // name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
2070 0 : if (aName.IsEmpty() && IsAbbreviation()) {
2071 0 : nsAutoString name;
2072 0 : if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, name)) {
2073 0 : name.CompressWhitespace();
2074 0 : aName = name;
2075 : }
2076 : }
2077 0 : return NS_OK;
2078 : }
2079 :
2080 : void
2081 0 : nsHyperTextAccessible::InvalidateChildren()
2082 : {
2083 0 : mOffsets.Clear();
2084 :
2085 0 : nsAccessibleWrap::InvalidateChildren();
2086 0 : }
2087 :
2088 : bool
2089 0 : nsHyperTextAccessible::RemoveChild(nsAccessible* aAccessible)
2090 : {
2091 0 : PRInt32 childIndex = aAccessible->IndexInParent();
2092 0 : PRInt32 count = mOffsets.Length() - childIndex;
2093 0 : if (count > 0)
2094 0 : mOffsets.RemoveElementsAt(childIndex, count);
2095 :
2096 0 : return nsAccessible::RemoveChild(aAccessible);
2097 : }
2098 :
2099 : ////////////////////////////////////////////////////////////////////////////////
2100 : // nsHyperTextAccessible public static
2101 :
2102 0 : nsresult nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
2103 : PRUint32 *aRenderedOffset)
2104 : {
2105 0 : if (!aFrame) {
2106 : // Current frame not rendered -- this can happen if text is set on
2107 : // something with display: none
2108 0 : *aRenderedOffset = 0;
2109 0 : return NS_OK;
2110 : }
2111 0 : NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
2112 : "Need text frame for offset conversion");
2113 0 : NS_ASSERTION(aFrame->GetPrevContinuation() == nsnull,
2114 : "Call on primary frame only");
2115 :
2116 0 : gfxSkipChars skipChars;
2117 0 : gfxSkipCharsIterator iter;
2118 : // Only get info up to original offset, we know that will be larger than skipped offset
2119 0 : nsresult rv = aFrame->GetRenderedText(nsnull, &skipChars, &iter, 0, aContentOffset);
2120 0 : NS_ENSURE_SUCCESS(rv, rv);
2121 :
2122 0 : PRUint32 ourRenderedStart = iter.GetSkippedOffset();
2123 0 : PRInt32 ourContentStart = iter.GetOriginalOffset();
2124 :
2125 0 : *aRenderedOffset = iter.ConvertOriginalToSkipped(aContentOffset + ourContentStart) -
2126 0 : ourRenderedStart;
2127 :
2128 0 : return NS_OK;
2129 : }
2130 :
2131 0 : nsresult nsHyperTextAccessible::RenderedToContentOffset(nsIFrame *aFrame, PRUint32 aRenderedOffset,
2132 : PRInt32 *aContentOffset)
2133 : {
2134 0 : *aContentOffset = 0;
2135 0 : NS_ENSURE_TRUE(aFrame, NS_ERROR_FAILURE);
2136 :
2137 0 : NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
2138 : "Need text frame for offset conversion");
2139 0 : NS_ASSERTION(aFrame->GetPrevContinuation() == nsnull,
2140 : "Call on primary frame only");
2141 :
2142 0 : gfxSkipChars skipChars;
2143 0 : gfxSkipCharsIterator iter;
2144 : // We only need info up to skipped offset -- that is what we're converting to original offset
2145 0 : nsresult rv = aFrame->GetRenderedText(nsnull, &skipChars, &iter, 0, aRenderedOffset);
2146 0 : NS_ENSURE_SUCCESS(rv, rv);
2147 :
2148 0 : PRUint32 ourRenderedStart = iter.GetSkippedOffset();
2149 0 : PRInt32 ourContentStart = iter.GetOriginalOffset();
2150 :
2151 0 : *aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart;
2152 :
2153 0 : return NS_OK;
2154 : }
2155 :
2156 : ////////////////////////////////////////////////////////////////////////////////
2157 : // nsHyperTextAccessible public
2158 :
2159 : bool
2160 0 : nsHyperTextAccessible::GetCharAt(PRInt32 aOffset, EGetTextType aShift,
2161 : nsAString& aChar, PRInt32* aStartOffset,
2162 : PRInt32* aEndOffset)
2163 : {
2164 0 : aChar.Truncate();
2165 :
2166 0 : PRInt32 offset = ConvertMagicOffset(aOffset) + static_cast<PRInt32>(aShift);
2167 0 : PRInt32 childIdx = GetChildIndexAtOffset(offset);
2168 0 : if (childIdx == -1)
2169 0 : return false;
2170 :
2171 0 : nsAccessible* child = GetChildAt(childIdx);
2172 0 : child->AppendTextTo(aChar, offset - GetChildOffset(childIdx), 1);
2173 :
2174 0 : if (aStartOffset)
2175 0 : *aStartOffset = offset;
2176 0 : if (aEndOffset)
2177 0 : *aEndOffset = aChar.IsEmpty() ? offset : offset + 1;
2178 :
2179 0 : return true;
2180 : }
2181 :
2182 : PRInt32
2183 0 : nsHyperTextAccessible::GetChildOffset(PRUint32 aChildIndex,
2184 : bool aInvalidateAfter)
2185 : {
2186 0 : if (aChildIndex == 0) {
2187 0 : if (aInvalidateAfter)
2188 0 : mOffsets.Clear();
2189 :
2190 0 : return aChildIndex;
2191 : }
2192 :
2193 0 : PRInt32 count = mOffsets.Length() - aChildIndex;
2194 0 : if (count > 0) {
2195 0 : if (aInvalidateAfter)
2196 0 : mOffsets.RemoveElementsAt(aChildIndex, count);
2197 :
2198 0 : return mOffsets[aChildIndex - 1];
2199 : }
2200 :
2201 0 : PRUint32 lastOffset = mOffsets.IsEmpty() ?
2202 0 : 0 : mOffsets[mOffsets.Length() - 1];
2203 :
2204 0 : while (mOffsets.Length() < aChildIndex) {
2205 0 : nsAccessible* child = mChildren[mOffsets.Length()];
2206 0 : lastOffset += nsAccUtils::TextLength(child);
2207 0 : mOffsets.AppendElement(lastOffset);
2208 : }
2209 :
2210 0 : return mOffsets[aChildIndex - 1];
2211 : }
2212 :
2213 : PRInt32
2214 0 : nsHyperTextAccessible::GetChildIndexAtOffset(PRUint32 aOffset)
2215 : {
2216 0 : PRUint32 lastOffset = 0;
2217 0 : PRUint32 offsetCount = mOffsets.Length();
2218 0 : if (offsetCount > 0) {
2219 0 : lastOffset = mOffsets[offsetCount - 1];
2220 0 : if (aOffset < lastOffset) {
2221 0 : PRUint32 low = 0, high = offsetCount;
2222 0 : while (high > low) {
2223 0 : PRUint32 mid = (high + low) >> 1;
2224 0 : if (mOffsets[mid] == aOffset)
2225 0 : return mid < offsetCount - 1 ? mid + 1 : mid;
2226 :
2227 0 : if (mOffsets[mid] < aOffset)
2228 0 : low = mid + 1;
2229 : else
2230 0 : high = mid;
2231 : }
2232 0 : if (high == offsetCount)
2233 0 : return -1;
2234 :
2235 0 : return low;
2236 : }
2237 : }
2238 :
2239 0 : PRUint32 childCount = GetChildCount();
2240 0 : while (mOffsets.Length() < childCount) {
2241 0 : nsAccessible* child = GetChildAt(mOffsets.Length());
2242 0 : lastOffset += nsAccUtils::TextLength(child);
2243 0 : mOffsets.AppendElement(lastOffset);
2244 0 : if (aOffset < lastOffset)
2245 0 : return mOffsets.Length() - 1;
2246 : }
2247 :
2248 0 : if (aOffset == lastOffset)
2249 0 : return mOffsets.Length() - 1;
2250 :
2251 0 : return -1;
2252 : }
2253 :
2254 : ////////////////////////////////////////////////////////////////////////////////
2255 : // nsHyperTextAccessible protected
2256 :
2257 : nsresult
2258 0 : nsHyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame *aFrame,
2259 : PRInt32 aOffset,
2260 : nsIAccessible *aAccessible,
2261 : nsIDOMNode **aNode,
2262 : PRInt32 *aNodeOffset)
2263 : {
2264 0 : NS_ENSURE_ARG(aAccessible);
2265 :
2266 0 : nsCOMPtr<nsIDOMNode> node;
2267 :
2268 0 : if (!aFrame) {
2269 : // If the given frame is null then set offset after the DOM node of the
2270 : // given accessible.
2271 0 : nsCOMPtr<nsIDOMNode> DOMNode;
2272 0 : aAccessible->GetDOMNode(getter_AddRefs(DOMNode));
2273 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(DOMNode));
2274 0 : NS_ENSURE_STATE(content);
2275 :
2276 0 : nsCOMPtr<nsIContent> parent(content->GetParent());
2277 0 : NS_ENSURE_STATE(parent);
2278 :
2279 0 : *aNodeOffset = parent->IndexOf(content) + 1;
2280 0 : node = do_QueryInterface(parent);
2281 :
2282 0 : } else if (aFrame->GetType() == nsGkAtoms::textFrame) {
2283 0 : nsCOMPtr<nsIContent> content(aFrame->GetContent());
2284 0 : NS_ENSURE_STATE(content);
2285 :
2286 0 : nsIFrame *primaryFrame = content->GetPrimaryFrame();
2287 0 : nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, aNodeOffset);
2288 0 : NS_ENSURE_SUCCESS(rv, rv);
2289 :
2290 0 : node = do_QueryInterface(content);
2291 :
2292 : } else {
2293 0 : nsCOMPtr<nsIContent> content(aFrame->GetContent());
2294 0 : NS_ENSURE_STATE(content);
2295 :
2296 0 : nsCOMPtr<nsIContent> parent(content->GetParent());
2297 0 : NS_ENSURE_STATE(parent);
2298 :
2299 0 : *aNodeOffset = parent->IndexOf(content);
2300 0 : node = do_QueryInterface(parent);
2301 : }
2302 :
2303 0 : NS_IF_ADDREF(*aNode = node);
2304 0 : return NS_OK;
2305 : }
2306 :
2307 : // nsHyperTextAccessible
2308 : nsresult
2309 0 : nsHyperTextAccessible::RangeBoundToHypertextOffset(nsRange *aRange,
2310 : bool aIsStartBound,
2311 : bool aIsStartHTOffset,
2312 : PRInt32 *aHTOffset)
2313 : {
2314 0 : nsINode* node = nsnull;
2315 0 : PRInt32 nodeOffset = 0;
2316 :
2317 0 : if (aIsStartBound) {
2318 0 : node = aRange->GetStartParent();
2319 0 : nodeOffset = aRange->StartOffset();
2320 : } else {
2321 0 : node = aRange->GetEndParent();
2322 0 : nodeOffset = aRange->EndOffset();
2323 : }
2324 :
2325 : nsAccessible *startAcc =
2326 0 : DOMPointToHypertextOffset(node, nodeOffset, aHTOffset);
2327 :
2328 0 : if (aIsStartHTOffset && !startAcc)
2329 0 : *aHTOffset = 0;
2330 :
2331 0 : return NS_OK;
2332 : }
2333 :
2334 : // nsHyperTextAccessible
2335 : nsresult
2336 0 : nsHyperTextAccessible::GetSpellTextAttribute(nsINode* aNode,
2337 : PRInt32 aNodeOffset,
2338 : PRInt32 *aHTStartOffset,
2339 : PRInt32 *aHTEndOffset,
2340 : nsIPersistentProperties *aAttributes)
2341 : {
2342 0 : nsTArray<nsRange*> ranges;
2343 0 : GetSelectionDOMRanges(nsISelectionController::SELECTION_SPELLCHECK, &ranges);
2344 :
2345 0 : PRUint32 rangeCount = ranges.Length();
2346 0 : if (!rangeCount)
2347 0 : return NS_OK;
2348 :
2349 0 : nsCOMPtr<nsIDOMNode> DOMNode = do_QueryInterface(aNode);
2350 0 : for (PRUint32 index = 0; index < rangeCount; index++) {
2351 0 : nsRange* range = ranges[index];
2352 :
2353 : PRInt16 result;
2354 0 : nsresult rv = range->ComparePoint(DOMNode, aNodeOffset, &result);
2355 0 : NS_ENSURE_SUCCESS(rv, rv);
2356 : // ComparePoint checks boundary points, but we need to check that
2357 : // text at aNodeOffset is inside the range.
2358 : // See also bug 460690.
2359 0 : if (result == 0) {
2360 0 : if (aNode == range->GetEndParent() && aNodeOffset == range->EndOffset())
2361 0 : result = 1;
2362 : }
2363 :
2364 0 : if (result == 1) { // range is before point
2365 0 : PRInt32 startHTOffset = 0;
2366 : nsresult rv = RangeBoundToHypertextOffset(range, false, true,
2367 0 : &startHTOffset);
2368 0 : NS_ENSURE_SUCCESS(rv, rv);
2369 :
2370 0 : if (startHTOffset > *aHTStartOffset)
2371 0 : *aHTStartOffset = startHTOffset;
2372 :
2373 0 : } else if (result == -1) { // range is after point
2374 0 : PRInt32 endHTOffset = 0;
2375 : nsresult rv = RangeBoundToHypertextOffset(range, true, false,
2376 0 : &endHTOffset);
2377 0 : NS_ENSURE_SUCCESS(rv, rv);
2378 :
2379 0 : if (endHTOffset < *aHTEndOffset)
2380 0 : *aHTEndOffset = endHTOffset;
2381 :
2382 : } else { // point is in range
2383 0 : PRInt32 startHTOffset = 0;
2384 : nsresult rv = RangeBoundToHypertextOffset(range, true, true,
2385 0 : &startHTOffset);
2386 0 : NS_ENSURE_SUCCESS(rv, rv);
2387 :
2388 0 : PRInt32 endHTOffset = 0;
2389 : rv = RangeBoundToHypertextOffset(range, false, false,
2390 0 : &endHTOffset);
2391 0 : NS_ENSURE_SUCCESS(rv, rv);
2392 :
2393 0 : if (startHTOffset > *aHTStartOffset)
2394 0 : *aHTStartOffset = startHTOffset;
2395 0 : if (endHTOffset < *aHTEndOffset)
2396 0 : *aHTEndOffset = endHTOffset;
2397 :
2398 0 : if (aAttributes) {
2399 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
2400 0 : NS_LITERAL_STRING("spelling"));
2401 : }
2402 :
2403 0 : return NS_OK;
2404 : }
2405 : }
2406 :
2407 0 : return NS_OK;
2408 : }
|